From 2046e733e125fa58ed997f3d26d43543faf82c95 Mon Sep 17 00:00:00 2001 From: Guo Zhengkui Date: Mon, 28 Feb 2022 22:23:50 +0800 Subject: drm/nouveau/instmem: fix uninitialized_var.cocci warning Fix following coccicheck warning: drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c:316:11-12: WARNING this kind of initialization is deprecated. `void *map = map` has the same form of uninitialized_var() macro. I remove the redundant assignement. It has been tested with gcc (Debian 8.3.0-6) 8.3.0. The patch which removed uninitialized_var() is: https://lore.kernel.org/all/20121028102007.GA7547@gmail.com/ And there is very few "/* GCC */" comments in the Linux kernel code now. Signed-off-by: Guo Zhengkui Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20220228142352.18006-1-guozhengkui@vivo.com --- drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c index 96aca0edfa3c..c51bac76174c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c @@ -313,7 +313,7 @@ nv50_instobj_dtor(struct nvkm_memory *memory) struct nv50_instobj *iobj = nv50_instobj(memory); struct nvkm_instmem *imem = &iobj->imem->base; struct nvkm_vma *bar; - void *map = map; + void *map; mutex_lock(&imem->mutex); if (likely(iobj->lru.next)) -- cgit From fc3785fb56a27304c769af730d079f4337d4dc76 Mon Sep 17 00:00:00 2001 From: Arunpravin Date: Fri, 4 Mar 2022 01:46:02 +0530 Subject: drm/selftests: fix a shift-out-of-bounds bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pass the correct size value computed using the max_order. [ 68.124177][ T1] UBSAN: shift-out-of-bounds in include/linux/log2.h:67:13 [ 68.125333][ T1] shift exponent 4294967295 is too large for 32-bit type 'long unsigned int' [ 68.126563][ T1] CPU: 0 PID: 1 Comm: swapper Not tainted 5.17.0-rc2-00311-g39ec47bbfd5d #2 [ 68.127758][ T1] Call Trace: [ 68.128187][ T1] dump_stack_lvl (lib/dump_stack.c:108) [ 68.128793][ T1] dump_stack (lib/dump_stack.c:114) [ 68.129331][ T1] ubsan_epilogue (lib/ubsan.c:152) [ 68.129958][ T1] __ubsan_handle_shift_out_of_bounds.cold (arch/x86/include/asm/smap.h:85) [ 68.130791][ T1] ? drm_block_alloc+0x28/0x80 [ 68.131582][ T1] ? rcu_read_lock_sched_held (kernel/rcu/update.c:125) [ 68.132215][ T1] ? kmem_cache_alloc (include/trace/events/kmem.h:54 mm/slab.c:3501) [ 68.132878][ T1] ? mark_free+0x2e/0x80 [ 68.133524][ T1] drm_buddy_init.cold (include/linux/log2.h:67 drivers/gpu/drm/drm_buddy.c:131) [ 68.134145][ T1] ? test_drm_cmdline_init (drivers/gpu/drm/selftests/test-drm_buddy.c:87) [ 68.134770][ T1] igt_buddy_alloc_limit (drivers/gpu/drm/selftests/test-drm_buddy.c:30) [ 68.135472][ T1] ? vprintk_default (kernel/printk/printk.c:2257) [ 68.136057][ T1] ? test_drm_cmdline_init (drivers/gpu/drm/selftests/test-drm_buddy.c:87) [ 68.136812][ T1] test_drm_buddy_init (drivers/gpu/drm/selftests/drm_selftest.c:77 drivers/gpu/drm/selftests/test-drm_buddy.c:95) [ 68.137475][ T1] do_one_initcall (init/main.c:1300) [ 68.138111][ T1] ? parse_args (kernel/params.c:609 kernel/params.c:146 kernel/params.c:188) [ 68.138717][ T1] do_basic_setup (init/main.c:1372 init/main.c:1389 init/main.c:1408) [ 68.139366][ T1] kernel_init_freeable (init/main.c:1617) [ 68.140040][ T1] ? rest_init (init/main.c:1494) [ 68.140634][ T1] kernel_init (init/main.c:1504) [ 68.141155][ T1] ret_from_fork (arch/x86/entry/entry_32.S:772) [ 68.141607][ T1] ================================================================================ [ 68.146730][ T1] ------------[ cut here ]------------ [ 68.147460][ T1] kernel BUG at drivers/gpu/drm/drm_buddy.c:140! [ 68.148280][ T1] invalid opcode: 0000 [#1] [ 68.148895][ T1] CPU: 0 PID: 1 Comm: swapper Not tainted 5.17.0-rc2-00311-g39ec47bbfd5d #2 [ 68.149896][ T1] EIP: drm_buddy_init (drivers/gpu/drm/drm_buddy.c:140 (discriminator 1)) For more details: https://lists.01.org/hyperkitty/list/lkp@lists.01.org/thread/FDIF3HCILZNN5UQAZMOR7E3MQSMHHKWU/ Signed-off-by: Arunpravin Reported-by: kernel test robot Acked-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220303201602.2365-1-Arunpravin.PaneerSelvam@amd.com Signed-off-by: Christian König --- drivers/gpu/drm/selftests/test-drm_buddy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/selftests/test-drm_buddy.c b/drivers/gpu/drm/selftests/test-drm_buddy.c index fa997f89522b..913cbd7eae04 100644 --- a/drivers/gpu/drm/selftests/test-drm_buddy.c +++ b/drivers/gpu/drm/selftests/test-drm_buddy.c @@ -902,14 +902,13 @@ err_fini: static int igt_buddy_alloc_limit(void *arg) { - u64 end, size = U64_MAX, start = 0; + u64 size = U64_MAX, start = 0; struct drm_buddy_block *block; unsigned long flags = 0; LIST_HEAD(allocated); struct drm_buddy mm; int err; - size = end = round_down(size, 4096); err = drm_buddy_init(&mm, size, PAGE_SIZE); if (err) return err; @@ -921,7 +920,8 @@ static int igt_buddy_alloc_limit(void *arg) goto out_fini; } - err = drm_buddy_alloc_blocks(&mm, start, end, size, + size = mm.chunk_size << mm.max_order; + err = drm_buddy_alloc_blocks(&mm, start, size, size, PAGE_SIZE, &allocated, flags); if (unlikely(err)) -- cgit From 09077bc3116581f4d1cb961ec359ad56586e370b Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Sat, 25 Dec 2021 09:31:51 +0300 Subject: drm/bridge_connector: enable HPD by default if supported Hotplug events reported by bridge drivers over drm_bridge_hpd_notify() get ignored unless somebody calls drm_bridge_hpd_enable(). When the connector for the bridge is bridge_connector, such a call is done from drm_bridge_connector_enable_hpd(). However drm_bridge_connector_enable_hpd() is never called on init paths, documentation suggests that it is intended for suspend/resume paths. In result, once encoders are switched to bridge_connector, bridge-detected HPD stops working. This patch adds a call to that API on init path. This fixes HDMI HPD with rcar-du + adv7513 case when adv7513 reports HPD events via interrupts. Fixes: c24110a8fd09 ("drm: rcar-du: Use drm_bridge_connector_init() helper") Signed-off-by: Nikita Yushchenko Signed-off-by: Paul Cercueil Tested-by: Kieran Bingham Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Link: https://patchwork.freedesktop.org/patch/msgid/20211225063151.2110878-1-nikita.yoush@cogentembedded.com --- drivers/gpu/drm/drm_bridge_connector.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index 60923cdfe8e1..6b3dad03d77d 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -384,8 +384,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, connector_type, ddc); drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); - if (bridge_connector->bridge_hpd) + if (bridge_connector->bridge_hpd) { connector->polled = DRM_CONNECTOR_POLL_HPD; + drm_bridge_connector_enable_hpd(connector); + } else if (bridge_connector->bridge_detect) connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; -- cgit From a4a072d98d8c2370476de3b5f0c94f2cf2fd35bf Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Fri, 25 Feb 2022 21:51:33 +0100 Subject: sysfb: Make config option dependencies explicit efifb and vesafb requires sysfb implicitly but this is not stated in Kconfig. Add the dependency. With that all drivers that require sysfb depend on it so it can default to disabled. Signed-off-by: Michal Suchanek Reviewed-by: Javier Martinez Canillas Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/a0fa41e9186653e4c41ad0a28259e5cdc71b1f66.1645822213.git.msuchanek@suse.de --- drivers/firmware/Kconfig | 5 ++--- drivers/video/fbdev/Kconfig | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 75cb91055c17..5265eca74ba1 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -218,12 +218,11 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT config SYSFB bool - default y - depends on X86 || EFI config SYSFB_SIMPLEFB bool "Mark VGA/VBE/EFI FB as generic system framebuffer" - depends on SYSFB + depends on X86 || EFI + select SYSFB help Firmwares often provide initial graphics framebuffers so the BIOS, bootloader or kernel can show basic video-output during boot for diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 6ed5e608dd04..5851ee6d5502 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -628,6 +628,7 @@ config FB_VESA select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_BOOT_VESA_SUPPORT + select SYSFB help This is the frame buffer device driver for generic VESA 2.0 compliant graphic cards. The older VESA 1.2 cards are not supported. @@ -641,6 +642,7 @@ config FB_EFI select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select SYSFB help This is the EFI frame buffer device driver. If the firmware on your platform is EFI 1.10 or UEFI 2.0, select Y to add support for -- cgit From 8b766b0f8eece55155146f7628610ce54a065e0f Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Fri, 25 Feb 2022 21:51:34 +0100 Subject: sysfb: Enable boot time VESA graphic mode selection Since switch to simplefb/simpledrm VESA graphic mode selection with vga= kernel parameter is no longer available with legacy BIOS. The x86 realmode boot code enables the VESA graphic modes when option FB_BOOT_VESA_SUPPORT is enabled. This option is selected by vesafb but not simplefb/simpledrm. To enable use of VESA modes with simplefb in legacy BIOS boot mode drop dependency of BOOT_VESA_SUPPORT on FB, also drop the FB_ prefix. Select the option from sysfb rather than the drivers that depend on it. The BOOT_VESA_SUPPORT is not specific to framebuffer but rather to x86 platform, move it from fbdev to x86 Kconfig. Fixes: e3263ab389a7 ("x86: provide platform-devices for boot-framebuffers") Signed-off-by: Michal Suchanek Reviewed-by: Javier Martinez Canillas Acked-by: Borislav Petkov Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/948c39940a4e99f5b43bdbcbe537faae71a43e1d.1645822213.git.msuchanek@suse.de --- arch/x86/Kconfig | 6 ++++++ arch/x86/boot/video-vesa.c | 4 ++-- drivers/firmware/Kconfig | 1 + drivers/video/fbdev/Kconfig | 12 ++---------- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 9f5bd41bf660..cceb1aab0486 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -942,6 +942,12 @@ config GART_IOMMU If unsure, say Y. +config BOOT_VESA_SUPPORT + bool + help + If true, at least one selected framebuffer driver can take advantage + of VESA video modes set at an early boot stage via the vga= parameter. + config MAXSMP bool "Enable Maximum number of SMP Processors and NUMA Nodes" depends on X86_64 && SMP && DEBUG_KERNEL diff --git a/arch/x86/boot/video-vesa.c b/arch/x86/boot/video-vesa.c index 7e185977a984..c2c6d35e3a43 100644 --- a/arch/x86/boot/video-vesa.c +++ b/arch/x86/boot/video-vesa.c @@ -83,7 +83,7 @@ static int vesa_probe(void) (vminfo.memory_layout == 4 || vminfo.memory_layout == 6) && vminfo.memory_planes == 1) { -#ifdef CONFIG_FB_BOOT_VESA_SUPPORT +#ifdef CONFIG_BOOT_VESA_SUPPORT /* Graphics mode, color, linear frame buffer supported. Only register the mode if if framebuffer is configured, however, @@ -121,7 +121,7 @@ static int vesa_set_mode(struct mode_info *mode) if ((vminfo.mode_attr & 0x15) == 0x05) { /* It's a supported text mode */ is_graphic = 0; -#ifdef CONFIG_FB_BOOT_VESA_SUPPORT +#ifdef CONFIG_BOOT_VESA_SUPPORT } else if ((vminfo.mode_attr & 0x99) == 0x99) { /* It's a graphics mode with linear frame buffer */ is_graphic = 1; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 5265eca74ba1..be42ae6f079e 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -218,6 +218,7 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT config SYSFB bool + select BOOT_VESA_SUPPORT config SYSFB_SIMPLEFB bool "Mark VGA/VBE/EFI FB as generic system framebuffer" diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5851ee6d5502..01cf070f24f3 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -66,13 +66,6 @@ config FB_DDC select I2C_ALGOBIT select I2C -config FB_BOOT_VESA_SUPPORT - bool - depends on FB - help - If true, at least one selected framebuffer driver can take advantage - of VESA video modes set at an early boot stage via the vga= parameter. - config FB_CFB_FILLRECT tristate depends on FB @@ -627,7 +620,6 @@ config FB_VESA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select FB_BOOT_VESA_SUPPORT select SYSFB help This is the frame buffer device driver for generic VESA 2.0 @@ -1053,7 +1045,7 @@ config FB_INTEL select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select FB_BOOT_VESA_SUPPORT if FB_INTEL = y + select BOOT_VESA_SUPPORT if FB_INTEL = y depends on !DRM_I915 help This driver supports the on-board graphics built in to the Intel @@ -1380,7 +1372,7 @@ config FB_SIS select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select FB_BOOT_VESA_SUPPORT if FB_SIS = y + select BOOT_VESA_SUPPORT if FB_SIS = y select FB_SIS_300 if !FB_SIS_315 help This is the frame buffer device driver for the SiS 300, 315, 330 -- cgit From fd8be27e50e04f6e80af0f3e327cced525558256 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Fri, 25 Feb 2022 21:51:35 +0100 Subject: efifb: Remove redundant efifb_setup_from_dmi stub efifb is the only user of efifb_setup_from_dmi which is provided by sysfb which is selected by efifb. That makes the stub redundant. Signed-off-by: Michal Suchanek Reviewed-by: Javier Martinez Canillas Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/7416c439d68e9e96068ea5c77e05c99c7df41750.1645822213.git.msuchanek@suse.de --- include/linux/efi.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/efi.h b/include/linux/efi.h index ccd4d3f91c98..0cbbc4103632 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1329,10 +1329,6 @@ static inline struct efi_mokvar_table_entry *efi_mokvar_entry_find( } #endif -#ifdef CONFIG_SYSFB extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt); -#else -static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt) { } -#endif #endif /* _LINUX_EFI_H */ -- cgit From c7fa848ff01dad9ed3146a6b1a7d3622131bcedd Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 3 Mar 2022 15:33:10 +1000 Subject: KVM: PPC: Book3S HV P9: Fix "lost kick" race When new work is created that requires attention from the hypervisor (e.g., to inject an interrupt into the guest), fast_vcpu_kick is used to pull the target vcpu out of the guest if it may have been running. Therefore the work creation side looks like this: vcpu->arch.doorbell_request = 1; kvmppc_fast_vcpu_kick_hv(vcpu) { smp_mb(); cpu = vcpu->cpu; if (cpu != -1) send_ipi(cpu); } And the guest entry side *should* look like this: vcpu->cpu = smp_processor_id(); smp_mb(); if (vcpu->arch.doorbell_request) { // do something (abort entry or inject doorbell etc) } But currently the store and load are flipped, so it is possible for the entry to see no doorbell pending, and the doorbell creation misses the store to set cpu, resulting lost work (or at least delayed until the next guest exit). Fix this by reordering the entry operations and adding a smp_mb between them. The P8 path appears to have a similar race which is commented but not addressed yet. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20220303053315.1056880-2-npiggin@gmail.com --- arch/powerpc/kvm/book3s_hv.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index c886557638a1..6fa518f6501d 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -225,6 +225,13 @@ static void kvmppc_fast_vcpu_kick_hv(struct kvm_vcpu *vcpu) int cpu; struct rcuwait *waitp; + /* + * rcuwait_wake_up contains smp_mb() which orders prior stores that + * create pending work vs below loads of cpu fields. The other side + * is the barrier in vcpu run that orders setting the cpu fields vs + * testing for pending work. + */ + waitp = kvm_arch_vcpu_get_wait(vcpu); if (rcuwait_wake_up(waitp)) ++vcpu->stat.generic.halt_wakeup; @@ -1089,7 +1096,7 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) break; } tvcpu->arch.prodded = 1; - smp_mb(); + smp_mb(); /* This orders prodded store vs ceded load */ if (tvcpu->arch.ceded) kvmppc_fast_vcpu_kick_hv(tvcpu); break; @@ -3766,6 +3773,14 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc) pvc = core_info.vc[sub]; pvc->pcpu = pcpu + thr; for_each_runnable_thread(i, vcpu, pvc) { + /* + * XXX: is kvmppc_start_thread called too late here? + * It updates vcpu->cpu and vcpu->arch.thread_cpu + * which are used by kvmppc_fast_vcpu_kick_hv(), but + * kick is called after new exceptions become available + * and exceptions are checked earlier than here, by + * kvmppc_core_prepare_to_enter. + */ kvmppc_start_thread(vcpu, pvc); kvmppc_create_dtl_entry(vcpu, pvc); trace_kvm_guest_enter(vcpu); @@ -4487,6 +4502,21 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit, if (need_resched() || !kvm->arch.mmu_ready) goto out; + vcpu->cpu = pcpu; + vcpu->arch.thread_cpu = pcpu; + vc->pcpu = pcpu; + local_paca->kvm_hstate.kvm_vcpu = vcpu; + local_paca->kvm_hstate.ptid = 0; + local_paca->kvm_hstate.fake_suspend = 0; + + /* + * Orders set cpu/thread_cpu vs testing for pending interrupts and + * doorbells below. The other side is when these fields are set vs + * kvmppc_fast_vcpu_kick_hv reading the cpu/thread_cpu fields to + * kick a vCPU to notice the pending interrupt. + */ + smp_mb(); + if (!nested) { kvmppc_core_prepare_to_enter(vcpu); if (test_bit(BOOK3S_IRQPRIO_EXTERNAL, @@ -4506,13 +4536,6 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit, tb = mftb(); - vcpu->cpu = pcpu; - vcpu->arch.thread_cpu = pcpu; - vc->pcpu = pcpu; - local_paca->kvm_hstate.kvm_vcpu = vcpu; - local_paca->kvm_hstate.ptid = 0; - local_paca->kvm_hstate.fake_suspend = 0; - __kvmppc_create_dtl_entry(vcpu, pcpu, tb + vc->tb_offset, 0); trace_kvm_guest_enter(vcpu); @@ -4614,6 +4637,8 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit, run->exit_reason = KVM_EXIT_INTR; vcpu->arch.ret = -EINTR; out: + vcpu->cpu = -1; + vcpu->arch.thread_cpu = -1; powerpc_local_irq_pmu_restore(flags); preempt_enable(); goto done; -- cgit From f6e68388443ff50088e224b3a75090bdc0403be6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 3 Mar 2022 13:44:56 +0100 Subject: drm: Drop commas after SoC match table sentinels It does not make sense to have a comma after a sentinel, as any new elements must be added before the sentinel. Signed-off-by: Geert Uytterhoeven Reviewed-by: Neil Armstrong Acked-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/298f0644275d7d0b4aa3ee7143756a2431a4900c.1646311443.git.geert+renesas@glider.be --- drivers/gpu/drm/bridge/nwl-dsi.c | 2 +- drivers/gpu/drm/meson/meson_drv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index 71b24e2d64f5..14c763e1fa6f 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -1155,7 +1155,7 @@ MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids); static const struct soc_device_attribute nwl_dsi_quirks_match[] = { { .soc_id = "i.MX8MQ", .revision = "2.0", .data = (void *)E11418_HS_MODE_QUIRK }, - { /* sentinel. */ }, + { /* sentinel. */ } }; static int nwl_dsi_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 93a7a033a3e8..6c2b0c90b69e 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -168,7 +168,7 @@ static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = { }, .attrs = (const struct soc_device_attribute []) { { .soc_id = "GXL (S805*)", }, - { /* sentinel */ }, + { /* sentinel */ } } }, }; -- cgit From 016d1ca3f6ad05676fd9e418715ddce1f4ab5a73 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 7 Mar 2022 15:54:58 +0300 Subject: drm/selftests: missing error code in igt_buddy_alloc_smoke() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the error code to -ENOMEM if drm_random_order() fails. Fixes: e6ff5ef81170 ("drm/selftests: add drm buddy smoke testcase") Signed-off-by: Dan Carpenter Reviewed-by: Arunpravin Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220307125458.GA16710@kili --- drivers/gpu/drm/selftests/test-drm_buddy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/selftests/test-drm_buddy.c b/drivers/gpu/drm/selftests/test-drm_buddy.c index 913cbd7eae04..aca0c491040f 100644 --- a/drivers/gpu/drm/selftests/test-drm_buddy.c +++ b/drivers/gpu/drm/selftests/test-drm_buddy.c @@ -488,8 +488,10 @@ static int igt_buddy_alloc_smoke(void *arg) } order = drm_random_order(mm.max_order + 1, &prng); - if (!order) + if (!order) { + err = -ENOMEM; goto out_fini; + } for (i = 0; i <= mm.max_order; ++i) { struct drm_buddy_block *block; -- cgit From b5149e229218118c9cd44a4d256f970ddcbf745b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 22 Feb 2022 16:47:25 +1000 Subject: KVM: PPC: Book3S PR: Disable SCV when AIL could be disabled PR KVM does not support running with AIL enabled, and SCV does is not supported with AIL disabled. Fix this by ensuring the SCV facility is disabled with FSCR while a CPU could be running with AIL=0. The PowerNV host supports disabling AIL on a per-CPU basis, so SCV just needs to be disabled when a vCPU is being run. The pSeries machine can only switch AIL on a system-wide basis, so it must disable SCV support at boot if the configuration can potentially run a PR KVM guest. Also ensure a the FSCR[SCV] bit can not be enabled when emulating mtFSCR for the guest. SCV is not emulated for the PR guest at the moment, this just fixes the host crashes. Alternatives considered and rejected: - SCV support can not be disabled by PR KVM after boot, because it is advertised to userspace with HWCAP. - AIL can not be disabled on a per-CPU basis. At least when running on pseries it is a per-LPAR setting. - Support for real-mode SCV vectors will not be added because they are at 0x17000 so making such a large fixed head space causes immediate value limits to be exceeded, requiring a lot rework and more code. - Disabling SCV for any PR KVM possible kernel will cause a slowdown when not using PR KVM. - A boot time option to disable SCV to use PR KVM is user-hostile. - System call instruction emulation for SCV facility unavailable instructions is too complex and old emulation code was subtly broken and removed. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20220222064727.2314380-2-npiggin@gmail.com --- arch/powerpc/kernel/exceptions-64s.S | 4 ++++ arch/powerpc/kernel/setup_64.c | 28 ++++++++++++++++++++++++++++ arch/powerpc/kvm/Kconfig | 9 +++++++++ arch/powerpc/kvm/book3s_pr.c | 26 +++++++++++++++++--------- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 55caeee37c08..b66dd6f775a4 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -809,6 +809,10 @@ __start_interrupts: * - MSR_EE|MSR_RI is clear (no reentrant exceptions) * - Standard kernel environment is set up (stack, paca, etc) * + * KVM: + * These interrupts do not elevate HV 0->1, so HV is not involved. PR KVM + * ensures that FSCR[SCV] is disabled whenever it has to force AIL off. + * * Call convention: * * syscall register convention is in Documentation/powerpc/syscall64-abi.rst diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index be8577ac9397..d973ae7558e3 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -197,6 +197,34 @@ static void __init configure_exceptions(void) /* Under a PAPR hypervisor, we need hypercalls */ if (firmware_has_feature(FW_FEATURE_SET_MODE)) { + /* + * - PR KVM does not support AIL mode interrupts in the host + * while a PR guest is running. + * + * - SCV system call interrupt vectors are only implemented for + * AIL mode interrupts. + * + * - On pseries, AIL mode can only be enabled and disabled + * system-wide so when a PR VM is created on a pseries host, + * all CPUs of the host are set to AIL=0 mode. + * + * - Therefore host CPUs must not execute scv while a PR VM + * exists. + * + * - SCV support can not be disabled dynamically because the + * feature is advertised to host userspace. Disabling the + * facility and emulating it would be possible but is not + * implemented. + * + * - So SCV support is blanket disabled if PR KVM could possibly + * run. That is, PR support compiled in, booting on pseries + * with hash MMU. + */ + if (IS_ENABLED(CONFIG_KVM_BOOK3S_PR_POSSIBLE) && !radix_enabled()) { + init_task.thread.fscr &= ~FSCR_SCV; + cur_cpu_spec->cpu_user_features2 &= ~PPC_FEATURE2_SCV; + } + /* Enable AIL if possible */ if (!pseries_enable_reloc_on_exc()) { init_task.thread.fscr &= ~FSCR_SCV; diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 18e58085447c..ddd88179110a 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -112,12 +112,21 @@ config KVM_BOOK3S_64_PR guest in user mode (problem state) and emulating all privileged instructions and registers. + This is only available for hash MMU mode and only supports + guests that use hash MMU mode. + This is not as fast as using hypervisor mode, but works on machines where hypervisor mode is not available or not usable, and can emulate processors that are different from the host processor, including emulating 32-bit processors on a 64-bit host. + Selecting this option will cause the SCV facility to be + disabled when the kernel is booted on the pseries platform in + hash MMU mode (regardless of PR VMs running). When any PR VMs + are running, "AIL" mode is disabled which may slow interrupts + and system calls on the host. + config KVM_BOOK3S_HV_EXIT_TIMING bool "Detailed timing for hypervisor real-mode code" depends on KVM_BOOK3S_HV_POSSIBLE && DEBUG_FS diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 34a801c3604a..7bf9e6ca5c2d 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -137,12 +137,15 @@ static void kvmppc_core_vcpu_load_pr(struct kvm_vcpu *vcpu, int cpu) svcpu->slb_max = to_book3s(vcpu)->slb_shadow_max; svcpu->in_use = 0; svcpu_put(svcpu); -#endif /* Disable AIL if supported */ - if (cpu_has_feature(CPU_FTR_HVMODE) && - cpu_has_feature(CPU_FTR_ARCH_207S)) - mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~LPCR_AIL); + if (cpu_has_feature(CPU_FTR_HVMODE)) { + if (cpu_has_feature(CPU_FTR_ARCH_207S)) + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~LPCR_AIL); + if (cpu_has_feature(CPU_FTR_ARCH_300) && (current->thread.fscr & FSCR_SCV)) + mtspr(SPRN_FSCR, mfspr(SPRN_FSCR) & ~FSCR_SCV); + } +#endif vcpu->cpu = smp_processor_id(); #ifdef CONFIG_PPC_BOOK3S_32 @@ -165,6 +168,14 @@ static void kvmppc_core_vcpu_put_pr(struct kvm_vcpu *vcpu) memcpy(to_book3s(vcpu)->slb_shadow, svcpu->slb, sizeof(svcpu->slb)); to_book3s(vcpu)->slb_shadow_max = svcpu->slb_max; svcpu_put(svcpu); + + /* Enable AIL if supported */ + if (cpu_has_feature(CPU_FTR_HVMODE)) { + if (cpu_has_feature(CPU_FTR_ARCH_207S)) + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_AIL_3); + if (cpu_has_feature(CPU_FTR_ARCH_300) && (current->thread.fscr & FSCR_SCV)) + mtspr(SPRN_FSCR, mfspr(SPRN_FSCR) | FSCR_SCV); + } #endif if (kvmppc_is_split_real(vcpu)) @@ -174,11 +185,6 @@ static void kvmppc_core_vcpu_put_pr(struct kvm_vcpu *vcpu) kvmppc_giveup_fac(vcpu, FSCR_TAR_LG); kvmppc_save_tm_pr(vcpu); - /* Enable AIL if supported */ - if (cpu_has_feature(CPU_FTR_HVMODE) && - cpu_has_feature(CPU_FTR_ARCH_207S)) - mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_AIL_3); - vcpu->cpu = -1; } @@ -1037,6 +1043,8 @@ static int kvmppc_handle_fac(struct kvm_vcpu *vcpu, ulong fac) void kvmppc_set_fscr(struct kvm_vcpu *vcpu, u64 fscr) { + if (fscr & FSCR_SCV) + fscr &= ~FSCR_SCV; /* SCV must not be enabled */ if ((vcpu->arch.fscr & FSCR_TAR) && !(fscr & FSCR_TAR)) { /* TAR got dropped, drop it in shadow too */ kvmppc_giveup_fac(vcpu, FSCR_TAR_LG); -- cgit From 839d893b4067da14b9c46fda2dfd88b80aeed551 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 22 Feb 2022 16:47:26 +1000 Subject: KVM: PPC: Book3S PR: Disallow AIL != 0 KVM PR does not implement address translation modes on interrupt, so it must not allow H_SET_MODE to succeed. The behaviour change caused by this mode is architected and not advisory (interrupts *must* behave differently). QEMU does not deal with differences in AIL support in the host. The solution to that is a spapr capability and corresponding KVM CAP, but this patch does not break things more than before (the host behaviour already differs, this change just disallows some modes that are not implemented properly). By happy coincidence, this allows PR Linux guests that are using the SCV facility to boot and run, because Linux disables the use of SCV if AIL can not be set to 3. This does not fix the underlying problem of missing SCV support (an OS could implement real-mode SCV vectors and try to enable the facility). The true fix for that is for KVM PR to emulate scv interrupts from the facility unavailable interrupt. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20220222064727.2314380-3-npiggin@gmail.com --- arch/powerpc/kvm/book3s_pr_papr.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c index 1f10e7dfcdd0..dc4f51ac84bc 100644 --- a/arch/powerpc/kvm/book3s_pr_papr.c +++ b/arch/powerpc/kvm/book3s_pr_papr.c @@ -281,6 +281,22 @@ static int kvmppc_h_pr_logical_ci_store(struct kvm_vcpu *vcpu) return EMULATE_DONE; } +static int kvmppc_h_pr_set_mode(struct kvm_vcpu *vcpu) +{ + unsigned long mflags = kvmppc_get_gpr(vcpu, 4); + unsigned long resource = kvmppc_get_gpr(vcpu, 5); + + if (resource == H_SET_MODE_RESOURCE_ADDR_TRANS_MODE) { + /* KVM PR does not provide AIL!=0 to guests */ + if (mflags == 0) + kvmppc_set_gpr(vcpu, 3, H_SUCCESS); + else + kvmppc_set_gpr(vcpu, 3, H_UNSUPPORTED_FLAG_START - 63); + return EMULATE_DONE; + } + return EMULATE_FAIL; +} + #ifdef CONFIG_SPAPR_TCE_IOMMU static int kvmppc_h_pr_put_tce(struct kvm_vcpu *vcpu) { @@ -384,6 +400,8 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd) return kvmppc_h_pr_logical_ci_load(vcpu); case H_LOGICAL_CI_STORE: return kvmppc_h_pr_logical_ci_store(vcpu); + case H_SET_MODE: + return kvmppc_h_pr_set_mode(vcpu); case H_XIRR: case H_CPPR: case H_EOI: @@ -421,6 +439,7 @@ int kvmppc_hcall_impl_pr(unsigned long cmd) case H_CEDE: case H_LOGICAL_CI_LOAD: case H_LOGICAL_CI_STORE: + case H_SET_MODE: #ifdef CONFIG_KVM_XICS case H_XIRR: case H_CPPR: @@ -447,6 +466,7 @@ static unsigned int default_hcall_list[] = { H_BULK_REMOVE, H_PUT_TCE, H_CEDE, + H_SET_MODE, #ifdef CONFIG_KVM_XICS H_XIRR, H_CPPR, -- cgit From f771b55731fc82b1e8e9ef123f6f1b8d8c92bc63 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 7 Mar 2022 13:26:25 +1100 Subject: KVM: PPC: Use KVM_CAP_PPC_AIL_MODE_3 Use KVM_CAP_PPC_AIL_MODE_3 to advertise the capability to set the AIL resource mode to 3 with the H_SET_MODE hypercall. This capability differs between processor types and KVM types (PR, HV, Nested HV), and affects guest-visible behaviour. QEMU will implement a cap-ail-mode-3 to control this behaviour[1], and use the KVM CAP if available to determine KVM support[2]. [1] https://lists.nongnu.org/archive/html/qemu-ppc/2022-02/msg00437.html [2] https://lists.nongnu.org/archive/html/qemu-ppc/2022-02/msg00439.html Signed-off-by: Nicholas Piggin Reviewed-by: Fabiano Rosas [mpe: Rebase onto 93b71801a827 from kvm-ppc-cap-210 branch, add EXPORT_SYMBOL] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20220222064727.2314380-4-npiggin@gmail.com --- arch/powerpc/include/asm/setup.h | 2 ++ arch/powerpc/kvm/powerpc.c | 17 +++++++++++++++++ arch/powerpc/platforms/pseries/setup.c | 13 ++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h index d0d3dd531c7f..a555fb77258a 100644 --- a/arch/powerpc/include/asm/setup.h +++ b/arch/powerpc/include/asm/setup.h @@ -28,11 +28,13 @@ void setup_panic(void); #define ARCH_PANIC_TIMEOUT 180 #ifdef CONFIG_PPC_PSERIES +extern bool pseries_reloc_on_exception(void); extern bool pseries_enable_reloc_on_exc(void); extern void pseries_disable_reloc_on_exc(void); extern void pseries_big_endian_exceptions(void); void __init pseries_little_endian_exceptions(void); #else +static inline bool pseries_reloc_on_exception(void) { return false; } static inline bool pseries_enable_reloc_on_exc(void) { return false; } static inline void pseries_disable_reloc_on_exc(void) {} static inline void pseries_big_endian_exceptions(void) {} diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 9772b176e406..875c30c12db0 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -705,6 +705,23 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = 1; break; #endif + case KVM_CAP_PPC_AIL_MODE_3: + r = 0; + /* + * KVM PR, POWER7, and some POWER9s don't support AIL=3 mode. + * The POWER9s can support it if the guest runs in hash mode, + * but QEMU doesn't necessarily query the capability in time. + */ + if (hv_enabled) { + if (kvmhv_on_pseries()) { + if (pseries_reloc_on_exception()) + r = 1; + } else if (cpu_has_feature(CPU_FTR_ARCH_207S) && + !cpu_has_feature(CPU_FTR_P9_RADIX_PREFETCH_BUG)) { + r = 1; + } + } + break; default: r = 0; break; diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 83a04d967a59..5bdbbe2151b1 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -353,6 +353,14 @@ static void pseries_lpar_idle(void) pseries_idle_epilog(); } +static bool pseries_reloc_on_exception_enabled; + +bool pseries_reloc_on_exception(void) +{ + return pseries_reloc_on_exception_enabled; +} +EXPORT_SYMBOL_GPL(pseries_reloc_on_exception); + /* * Enable relocation on during exceptions. This has partition wide scope and * may take a while to complete, if it takes longer than one second we will @@ -377,6 +385,7 @@ bool pseries_enable_reloc_on_exc(void) " on exceptions: %ld\n", rc); return false; } + pseries_reloc_on_exception_enabled = true; return true; } @@ -404,7 +413,9 @@ void pseries_disable_reloc_on_exc(void) break; mdelay(get_longbusy_msecs(rc)); } - if (rc != H_SUCCESS) + if (rc == H_SUCCESS) + pseries_reloc_on_exception_enabled = false; + else pr_warn("Warning: Failed to disable relocation on exceptions: %ld\n", rc); } -- cgit From 8f2a3970c969d0d8d7289a4c65edcedafc16fd92 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Mon, 7 Mar 2022 17:56:12 +0800 Subject: drm/omap: fix NULL but dereferenced coccicheck error Fix the following coccicheck warning: ./drivers/gpu/drm/omapdrm/omap_overlay.c:89:22-25: ERROR: r_ovl is NULL but dereferenced. Here should be ovl->idx rather than r_ovl->idx. Fixes: e02b5cc9e898ad ("drm/omap: Add a 'right overlay' to plane state") Signed-off-by: Wan Jiabing Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20220307095612.409090-1-wanjiabing@vivo.com --- drivers/gpu/drm/omapdrm/omap_overlay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.c b/drivers/gpu/drm/omapdrm/omap_overlay.c index 10730c9b2752..b0bc9ad2ef73 100644 --- a/drivers/gpu/drm/omapdrm/omap_overlay.c +++ b/drivers/gpu/drm/omapdrm/omap_overlay.c @@ -86,7 +86,7 @@ int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, r_ovl = omap_plane_find_free_overlay(s->dev, overlay_map, caps, fourcc); if (!r_ovl) { - overlay_map[r_ovl->idx] = NULL; + overlay_map[ovl->idx] = NULL; *overlay = NULL; return -ENOMEM; } -- cgit From 8a0982b60f5a4a66c8a28eed37769021c20d8c50 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 21 Feb 2022 10:58:57 +0100 Subject: drm/komeda: plane: switch to plane reset helper komeda_plane_reset() does the state initialisation by copying a lot of the code found in the __drm_atomic_helper_plane_reset(). Let's switch to that helper and reduce the boilerplate. Cc: Brian Starkey Cc: "James (Qian) Wang" Cc: Liviu Dudau Cc: Mihail Atanassov Signed-off-by: Maxime Ripard Acked-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220221095918.18763-2-maxime@cerno.tech --- drivers/gpu/drm/arm/display/komeda/komeda_plane.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c index d63d83800a8a..1778f6e1ea56 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c @@ -145,14 +145,10 @@ static void komeda_plane_reset(struct drm_plane *plane) state = kzalloc(sizeof(*state), GFP_KERNEL); if (state) { - state->base.rotation = DRM_MODE_ROTATE_0; - state->base.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; - state->base.alpha = DRM_BLEND_ALPHA_OPAQUE; + __drm_atomic_helper_plane_reset(plane, &state->base); state->base.zpos = kplane->layer->base.id; state->base.color_encoding = DRM_COLOR_YCBCR_BT601; state->base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; - plane->state = &state->base; - plane->state->plane = plane; } } -- cgit From c410976a72823015341ffeeebede36e4a9a08eb2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 21 Feb 2022 10:59:05 +0100 Subject: drm/komeda: plane: Remove redundant zpos initialisation The komeda KMS driver will call drm_plane_create_zpos_property() with an init value of the plane index. Since the initial value wasn't carried over in the state, the driver had to set it again in komeda_plane_reset(). However, the helpers have been adjusted to set it properly at reset, so this is not needed anymore. Cc: Brian Starkey Cc: "James (Qian) Wang" Cc: Liviu Dudau Cc: Mihail Atanassov Signed-off-by: Maxime Ripard Acked-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220221095918.18763-10-maxime@cerno.tech --- drivers/gpu/drm/arm/display/komeda/komeda_plane.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c index 1778f6e1ea56..383bb2039bd4 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c @@ -135,7 +135,6 @@ static void komeda_plane_destroy(struct drm_plane *plane) static void komeda_plane_reset(struct drm_plane *plane) { struct komeda_plane_state *state; - struct komeda_plane *kplane = to_kplane(plane); if (plane->state) __drm_atomic_helper_plane_destroy_state(plane->state); @@ -146,7 +145,6 @@ static void komeda_plane_reset(struct drm_plane *plane) state = kzalloc(sizeof(*state), GFP_KERNEL); if (state) { __drm_atomic_helper_plane_reset(plane, &state->base); - state->base.zpos = kplane->layer->base.id; state->base.color_encoding = DRM_COLOR_YCBCR_BT601; state->base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; } -- cgit From e026f2880235025643fd7d68dbe972d544d7f44f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 21 Feb 2022 10:59:15 +0100 Subject: drm/komeda: plane: Remove redundant color encoding and range initialisation The komeda KMS driver will call drm_plane_create_color_properties() with a default encoding and range values of BT601 and Limited Range, respectively. Since the initial value wasn't carried over in the state, the driver had to set it again in komeda_plane_reset(). However, the helpers have been adjusted to set it properly at reset, so this is not needed anymore. Cc: Brian Starkey Cc: "James (Qian) Wang" Cc: Liviu Dudau Cc: Mihail Atanassov Signed-off-by: Maxime Ripard Acked-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220221095918.18763-20-maxime@cerno.tech --- drivers/gpu/drm/arm/display/komeda/komeda_plane.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c index 383bb2039bd4..541949f2d44a 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c @@ -143,11 +143,8 @@ static void komeda_plane_reset(struct drm_plane *plane) plane->state = NULL; state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) { + if (state) __drm_atomic_helper_plane_reset(plane, &state->base); - state->base.color_encoding = DRM_COLOR_YCBCR_BT601; - state->base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; - } } static struct drm_plane_state * -- cgit From bbefe708816a6c559bf170031e99bca32c0743e4 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:26:00 +0100 Subject: drm/bridge: nxp-ptn3460: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220228182600.22463-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/nxp-ptn3460.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index e941c1132598..1ab91f4e057b 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -263,7 +263,6 @@ static int ptn3460_probe(struct i2c_client *client, struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; struct drm_bridge *panel_bridge; - struct drm_panel *panel; int ret; ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); @@ -271,11 +270,7 @@ static int ptn3460_probe(struct i2c_client *client, return -ENOMEM; } - ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL); - if (ret) - return ret; - - panel_bridge = devm_drm_panel_bridge_add(dev, panel); + panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); -- cgit From 55ad1a56b4f991f9879f4f5019f485fa713426ae Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:29:04 +0100 Subject: drm/bridge: parade-ps8622: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220228182904.22982-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/parade-ps8622.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 614b19f0f1b7..37b308850b4e 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -452,18 +452,13 @@ static int ps8622_probe(struct i2c_client *client, struct device *dev = &client->dev; struct ps8622_bridge *ps8622; struct drm_bridge *panel_bridge; - struct drm_panel *panel; int ret; ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL); if (!ps8622) return -ENOMEM; - ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, NULL); - if (ret) - return ret; - - panel_bridge = devm_drm_panel_bridge_add(dev, panel); + panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); -- cgit From 5486eac81004febf512c6d344cd8f612a1e597ae Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:31:31 +0100 Subject: drm/bridge: parade-ps8640: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220228183131.23494-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/parade-ps8640.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 3f17337ee389..9766cbbd62ad 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -589,7 +589,6 @@ static int ps8640_probe(struct i2c_client *client) struct device *dev = &client->dev; struct device_node *np = dev->of_node; struct ps8640 *ps_bridge; - struct drm_panel *panel; int ret; u32 i; @@ -674,13 +673,7 @@ static int ps8640_probe(struct i2c_client *client) devm_of_dp_aux_populate_ep_devices(&ps_bridge->aux); /* port@1 is ps8640 output port */ - ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); - if (ret < 0) - return ret; - if (!panel) - return -ENODEV; - - ps_bridge->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); if (IS_ERR(ps_bridge->panel_bridge)) return PTR_ERR(ps_bridge->panel_bridge); -- cgit From 0d9c5e6733a773ce590a1b08a446daca04781c57 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:33:42 +0100 Subject: drm/bridge: tc358762: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220228183342.24129-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/tc358762.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index 1bfdfc6affaf..7870aceb0a6d 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -179,15 +179,8 @@ static int tc358762_parse_dt(struct tc358762 *ctx) { struct drm_bridge *panel_bridge; struct device *dev = ctx->dev; - struct drm_panel *panel; - int ret; - - ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL); - if (ret) - return ret; - - panel_bridge = devm_drm_panel_bridge_add(dev, panel); + panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); -- cgit From aec9a8576eecaf4a75f29ade79e679f171edf622 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:35:37 +0100 Subject: drm/bridge: tc358775: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220228183537.24600-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/tc358775.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 695af3badcc7..b987e5ac80f0 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -649,7 +649,6 @@ static int tc_attach_host(struct tc_data *tc) static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct drm_panel *panel; struct tc_data *tc; int ret; @@ -660,14 +659,8 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) tc->dev = dev; tc->i2c = client; - ret = drm_of_find_panel_or_bridge(dev->of_node, TC358775_LVDS_OUT0, - 0, &panel, NULL); - if (ret < 0) - return ret; - if (!panel) - return -ENODEV; - - tc->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + tc->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, + TC358775_LVDS_OUT0, 0); if (IS_ERR(tc->panel_bridge)) return PTR_ERR(tc->panel_bridge); -- cgit From c1b16fbd1698d07bbb0b0ddc3da5a7f1f0854edd Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:37:24 +0100 Subject: drm/bridge: ti-sn65dsi83: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220228183724.25030-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 19daaddd29a4..c2afef97c2b2 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -565,7 +565,6 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) struct drm_bridge *panel_bridge; struct device *dev = ctx->dev; struct device_node *endpoint; - struct drm_panel *panel; int ret; endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); @@ -605,15 +604,10 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) } } - ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, &panel_bridge); - if (ret < 0) + panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0); + if (IS_ERR(panel_bridge)) { + ret = PTR_ERR(panel_bridge); goto err_put_node; - if (panel) { - panel_bridge = devm_drm_panel_bridge_add(dev, panel); - if (IS_ERR(panel_bridge)) { - ret = PTR_ERR(panel_bridge); - goto err_put_node; - } } ctx->panel_bridge = panel_bridge; -- cgit From 07b58d61275ebbb39b678d18f7c4e472050b13eb Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Tue, 8 Feb 2022 15:12:28 +0000 Subject: drm/doc: pull in drm_buddy.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we pull in the kernel-doc for this. Reported-by: Daniel Vetter Signed-off-by: Matthew Auld Cc: Arunpravin Cc: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220208151228.344997-1-matthew.auld@intel.com Signed-off-by: Christian König Reviewed-by: Christian König --- Documentation/gpu/drm-mm.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index 198bcc1affa1..f32ccce5722d 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -466,6 +466,15 @@ DRM MM Range Allocator Function References .. kernel-doc:: drivers/gpu/drm/drm_mm.c :export: +DRM Buddy Allocator +=================== + +DRM Buddy Function References +----------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_buddy.c + :export: + DRM Cache Handling and Fast WC memcpy() ======================================= -- cgit From 471e201f543559e2cb19b182b680ebf04d80ee31 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Wed, 24 Nov 2021 16:07:52 +0100 Subject: dt-bindings: display: sitronix, st7735r: Fix backlight in example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backlight property was lost during conversion to yaml in commit abdd9e3705c8 ("dt-bindings: display: sitronix,st7735r: Convert to DT schema"). Put it back. Fixes: abdd9e3705c8 ("dt-bindings: display: sitronix,st7735r: Convert to DT schema") Signed-off-by: Noralf Trønnes Acked-by: Rob Herring Reviewed-by: Geert Uytterhoeven Acked-by: David Lechner Link: https://patchwork.freedesktop.org/patch/msgid/20211124150757.17929-2-noralf@tronnes.org --- Documentation/devicetree/bindings/display/sitronix,st7735r.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml index 0cebaaefda03..419c3b2ac5a6 100644 --- a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml +++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml @@ -72,6 +72,7 @@ examples: dc-gpios = <&gpio 43 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio 80 GPIO_ACTIVE_HIGH>; rotation = <270>; + backlight = <&backlight>; }; }; -- cgit From a2e8cbb27722b0992aceb93b35e9d9433d8a758e Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Wed, 24 Nov 2021 16:07:53 +0100 Subject: dt-bindings: display: sitronix, st7735r: Make reset-gpios optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are other ways than using a gpio to reset the controller so make this property optional. Signed-off-by: Noralf Trønnes Acked-by: Rob Herring Acked-by: David Lechner Link: https://patchwork.freedesktop.org/patch/msgid/20211124150757.17929-3-noralf@tronnes.org --- Documentation/devicetree/bindings/display/sitronix,st7735r.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml index 419c3b2ac5a6..f81d0d0d51fe 100644 --- a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml +++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml @@ -48,7 +48,6 @@ required: - compatible - reg - dc-gpios - - reset-gpios additionalProperties: false -- cgit From df0a9e8d3008050f61241f471c70438ef1e90a1b Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Wed, 24 Nov 2021 16:07:54 +0100 Subject: dt-bindings: display: sitronix, st7735r: Remove spi-max-frequency limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The datasheet lists the minimum Serial clock cycle (Write) as 66ns which is 15MHz. Mostly it can do much better than that and is in fact often run at 32MHz. With a clever driver that runs configuration commands at a low speed and only the pixel data at the maximum speed the configuration can't be messed up by transfer errors and the speed is only limited by the amount of pixel glitches that one is able to tolerate. Signed-off-by: Noralf Trønnes Acked-by: Rob Herring Acked-by: David Lechner Link: https://patchwork.freedesktop.org/patch/msgid/20211124150757.17929-4-noralf@tronnes.org --- Documentation/devicetree/bindings/display/sitronix,st7735r.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml index f81d0d0d51fe..157b1a7b18f9 100644 --- a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml +++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml @@ -32,15 +32,13 @@ properties: - okaya,rh128128t - const: sitronix,st7715r - spi-max-frequency: - maximum: 32000000 - dc-gpios: maxItems: 1 description: Display data/command selection (D/CX) backlight: true reg: true + spi-max-frequency: true reset-gpios: true rotation: true -- cgit From 1498915233dde830061e008ad639b482fd76f93d Mon Sep 17 00:00:00 2001 From: Rex-BC Chen Date: Wed, 9 Mar 2022 15:36:35 +0800 Subject: drm/dsi: transfer DSI HS packets ending at the same time Since a HS transmission is composed of an arbitrary number of bytes that may not be an integer multiple of lanes, some lanes may run out of data before others. (Defined in 6.1.3 of mipi_DSI_specification_v.01-02-00) However, for some DSI RX devices (for example, anx7625), there is a limitation that packet number should be the same on all DSI lanes. In other words, they need to end a HS at the same time. Because this limitation is for some specific DSI RX devices, it is more reasonable to put the enable control in these DSI RX drivers. If DSI TX driver knows the information, they can adjust the setting for this situation. Signed-off-by: Jitao Shi Signed-off-by: Rex-BC Chen Acked-by: AngeloGioacchino Del Regno Reviewed-by: Andrzej Hajda Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220309073637.3591-2-rex-bc.chen@mediatek.com --- include/drm/drm_mipi_dsi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 147e51b6d241..51e09a1a106a 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -137,6 +137,8 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node); #define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) /* transmit data in low power */ #define MIPI_DSI_MODE_LPM BIT(11) +/* transmit data ending at the same time for all lanes within one hsync */ +#define MIPI_DSI_HS_PKT_END_ALIGNED BIT(12) enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888, -- cgit From 7d8d0b4dcc535357d44ddeff154508f147c14e2a Mon Sep 17 00:00:00 2001 From: Rex-BC Chen Date: Wed, 9 Mar 2022 15:36:36 +0800 Subject: drm/mediatek: implement the DSI HS packets aligned Some DSI RX devices (for example, anx7625) require last alignment of packets on all lanes after each row of data is sent. Otherwise, there will be some issues of shift or scroll for screen. Take horizontal_sync_active_byte for a example, we roundup the HSA packet data to lane number, and the subtraction of 2 is the packet data value added by the roundup operation, making the long packets are integer multiples of lane number. This value (2) varies with the lane number, and that is the reason we do this operation when the lane number is 4. In the previous operation of function "mtk_dsi_config_vdo_timing", the length of HSA and HFP data packets has been adjusted to an integration multiple of lane number. Since the number of RGB data packets cannot be guaranteed to be an integer multiple of lane number, we modify the data packet length of HBP so that the number of HBP + RGB is equal to the lane number. So after sending a line of data (HSA + HBP + RGB + HFP), the data lanes are aligned. Signed-off-by: Jitao Shi Signed-off-by: Rex-BC Chen Signed-off-by: Xinlei Lee Reviewed-by: Andrzej Hajda Reviewed-by: AngeloGioacchino Del Regno Acked-by: Chun-Kuang Hu Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220309073637.3591-3-rex-bc.chen@mediatek.com --- drivers/gpu/drm/mediatek/mtk_dsi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 5d90d2eb0019..e91b3fff4342 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -500,6 +500,18 @@ static void mtk_dsi_config_vdo_timing(struct mtk_dsi *dsi) DRM_WARN("HFP + HBP less than d-phy, FPS will under 60Hz\n"); } + if ((dsi->mode_flags & MIPI_DSI_HS_PKT_END_ALIGNED) && + (dsi->lanes == 4)) { + horizontal_sync_active_byte = + roundup(horizontal_sync_active_byte, dsi->lanes) - 2; + horizontal_frontporch_byte = + roundup(horizontal_frontporch_byte, dsi->lanes) - 2; + horizontal_backporch_byte = + roundup(horizontal_backporch_byte, dsi->lanes) - 2; + horizontal_backporch_byte -= + (vm->hactive * dsi_tmp_buf_bpp + 2) % dsi->lanes; + } + writel(horizontal_sync_active_byte, dsi->regs + DSI_HSA_WC); writel(horizontal_backporch_byte, dsi->regs + DSI_HBP_WC); writel(horizontal_frontporch_byte, dsi->regs + DSI_HFP_WC); -- cgit From 4404cdb59b336279b7dcb40c5ef01dbcdbebc941 Mon Sep 17 00:00:00 2001 From: Rex-BC Chen Date: Wed, 9 Mar 2022 15:36:37 +0800 Subject: drm/bridge: anx7625: config hs packets end aligned to avoid screen shift This device requires the packets on lanes aligned at the end to fix screen shift or scroll. Signed-off-by: Jitao Shi Signed-off-by: Rex-BC Chen Acked-by: AngeloGioacchino Del Regno Reviewed-by: Xin Ji Reviewed-by: Andrzej Hajda Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220309073637.3591-4-rex-bc.chen@mediatek.com --- drivers/gpu/drm/bridge/analogix/anx7625.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 31ecf5626f1d..4043192c0369 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2011,7 +2011,8 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | - MIPI_DSI_MODE_VIDEO_HSE; + MIPI_DSI_MODE_VIDEO_HSE | + MIPI_DSI_HS_PKT_END_ALIGNED; ret = devm_mipi_dsi_attach(dev, dsi); if (ret) { -- cgit From d583e752732421b26fef0d65020565f3bef12248 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Thu, 3 Mar 2022 12:19:43 -0800 Subject: drm/bridge: anx7625: check the return on anx7625_aux_trans Clang static analysis reports this issue anx7625.c:876:13: warning: The left operand of '&' is a garbage value if (!(bcap & 0xOA01)) { ~~~~ ^ bcap is only set by a successful call to anx7625_aux_trans(). So check. Fixes: cd1637c7e480 ("drm/bridge: anx7625: add HDCP support") Signed-off-by: Tom Rix Fixes: adca62ec370c ("drm/bridge: anx7625: Support reading edid through aux channel") Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220303201943.501746-1-trix@redhat.com Reviewed-by: Robert Foss --- drivers/gpu/drm/bridge/analogix/anx7625.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 4043192c0369..69122bea4c1e 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -874,7 +874,10 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx) } /* Read downstream capability */ - anx7625_aux_trans(ctx, DP_AUX_NATIVE_READ, 0x68028, 1, &bcap); + ret = anx7625_aux_trans(ctx, DP_AUX_NATIVE_READ, 0x68028, 1, &bcap); + if (ret < 0) + return ret; + if (!(bcap & 0x01)) { pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap); return 0; -- cgit From 14a28e79f5f6c756f281a021555f1b7578a67098 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 28 Feb 2022 19:39:54 +0100 Subject: drm/bridge: ti-sn65dsi86: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Reviewed-by: Kieran Bingham Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220228183955.25508-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 38616aab12ac..ca7f63d71147 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1188,15 +1188,9 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev, { struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent); struct device_node *np = pdata->dev->of_node; - struct drm_panel *panel; int ret; - ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); - if (ret) - return dev_err_probe(&adev->dev, ret, - "could not find any panel node\n"); - - pdata->next_bridge = devm_drm_panel_bridge_add(pdata->dev, panel); + pdata->next_bridge = devm_drm_of_get_bridge(pdata->dev, np, 1, 0); if (IS_ERR(pdata->next_bridge)) { DRM_ERROR("failed to create panel bridge\n"); return PTR_ERR(pdata->next_bridge); -- cgit From efb37e66b7572ce4696aa0ac21675e17d6b9a17d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 9 Mar 2022 00:07:57 +0800 Subject: drm: ssd130x: Fix COM scan direction register mask The SSD130x's command to toggle COM scan direction uses bit 3 and only bit 3 to set the direction of the scanout. The driver has an incorrect GENMASK(3, 2), causing the setting to be set on bit 2, rendering it ineffective. Fix the mask to only bit 3, so that the requested setting is applied correctly. Fixes: a61732e80867 ("drm: Add driver for Solomon SSD130x OLED displays") Signed-off-by: Chen-Yu Tsai Acked-by: Javier Martinez Canillas Tested-by: Geert Uytterhoeven Reviewed-by: Geert Uytterhoeven Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220308160758.26060-1-wens@kernel.org --- drivers/gpu/drm/solomon/ssd130x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 85cab9271585..73e5a39f7fa3 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -61,7 +61,7 @@ #define SSD130X_SET_COM_PINS_CONFIG 0xda #define SSD130X_SET_VCOMH 0xdb -#define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 2) +#define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 3) #define SSD130X_SET_COM_SCAN_DIR_SET(val) FIELD_PREP(SSD130X_SET_COM_SCAN_DIR_MASK, (val)) #define SSD130X_SET_CLOCK_DIV_MASK GENMASK(3, 0) #define SSD130X_SET_CLOCK_DIV_SET(val) FIELD_PREP(SSD130X_SET_CLOCK_DIV_MASK, (val)) -- cgit From a134109c301736ea2ac5054ba3c29c30c87f6ba7 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 9 Mar 2022 00:07:58 +0800 Subject: drm: ssd130x: Always apply segment remap setting Currently the ssd130x driver only sets the segment remap setting when the device tree requests it; it however does not clear the setting if it is not requested. This leads to the setting incorrectly persisting if the hardware is always on and has no reset GPIO wired. This might happen when a developer is trying to find the correct settings for an unknown module, and cause the developer to get confused because the settings from the device tree are not consistently applied. Make the driver apply the segment remap setting consistently, setting the value correctly based on the device tree setting. This also makes this setting's behavior consistent with the other settings, which are always applied. Fixes: a61732e80867 ("drm: Add driver for Solomon SSD130x OLED displays") Signed-off-by: Chen-Yu Tsai Acked-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220308160758.26060-2-wens@kernel.org --- drivers/gpu/drm/solomon/ssd130x.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 73e5a39f7fa3..a95ab7e5bbf0 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -48,7 +48,7 @@ #define SSD130X_CONTRAST 0x81 #define SSD130X_SET_LOOKUP_TABLE 0x91 #define SSD130X_CHARGE_PUMP 0x8d -#define SSD130X_SEG_REMAP_ON 0xa1 +#define SSD130X_SET_SEG_REMAP 0xa0 #define SSD130X_DISPLAY_OFF 0xae #define SSD130X_SET_MULTIPLEX_RATIO 0xa8 #define SSD130X_DISPLAY_ON 0xaf @@ -61,6 +61,8 @@ #define SSD130X_SET_COM_PINS_CONFIG 0xda #define SSD130X_SET_VCOMH 0xdb +#define SSD130X_SET_SEG_REMAP_MASK GENMASK(0, 0) +#define SSD130X_SET_SEG_REMAP_SET(val) FIELD_PREP(SSD130X_SET_SEG_REMAP_MASK, (val)) #define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 3) #define SSD130X_SET_COM_SCAN_DIR_SET(val) FIELD_PREP(SSD130X_SET_COM_SCAN_DIR_MASK, (val)) #define SSD130X_SET_CLOCK_DIV_MASK GENMASK(3, 0) @@ -235,7 +237,7 @@ static void ssd130x_power_off(struct ssd130x_device *ssd130x) static int ssd130x_init(struct ssd130x_device *ssd130x) { - u32 precharge, dclk, com_invdir, compins, chargepump; + u32 precharge, dclk, com_invdir, compins, chargepump, seg_remap; int ret; /* Set initial contrast */ @@ -244,11 +246,11 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) return ret; /* Set segment re-map */ - if (ssd130x->seg_remap) { - ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_SEG_REMAP_ON); - if (ret < 0) - return ret; - } + seg_remap = (SSD130X_SET_SEG_REMAP | + SSD130X_SET_SEG_REMAP_SET(ssd130x->seg_remap)); + ret = ssd130x_write_cmd(ssd130x, 1, seg_remap); + if (ret < 0) + return ret; /* Set COM direction */ com_invdir = (SSD130X_SET_COM_SCAN_DIR | -- cgit From 9e82ea0fb1dfc6eef917ff69f909b39dbaa38702 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Mon, 21 Feb 2022 08:28:35 +0100 Subject: drm/bridge: anx7625: switch to devm_drm_of_get_bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function "drm_of_find_panel_or_bridge" has been deprecated in favor of "devm_drm_of_get_bridge". Switch to the new function and reduce boilerplate. Signed-off-by: José Expósito Reviewed-by: Maxime Ripard Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220221072835.10032-1-jose.exposito89@gmail.com --- drivers/gpu/drm/bridge/analogix/anx7625.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 69122bea4c1e..9a2a19ad4202 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1611,8 +1611,6 @@ static int anx7625_parse_dt(struct device *dev, struct anx7625_platform_data *pdata) { struct device_node *np = dev->of_node, *ep0; - struct drm_panel *panel; - int ret; int bus_type, mipi_lanes; anx7625_get_swing_setting(dev, pdata); @@ -1649,18 +1647,14 @@ static int anx7625_parse_dt(struct device *dev, if (of_property_read_bool(np, "analogix,audio-enable")) pdata->audio_en = 1; - ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); - if (ret < 0) { - if (ret == -ENODEV) + pdata->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); + if (IS_ERR(pdata->panel_bridge)) { + if (PTR_ERR(pdata->panel_bridge) == -ENODEV) return 0; - return ret; - } - if (!panel) - return -ENODEV; - pdata->panel_bridge = devm_drm_panel_bridge_add(dev, panel); - if (IS_ERR(pdata->panel_bridge)) return PTR_ERR(pdata->panel_bridge); + } + DRM_DEV_DEBUG_DRIVER(dev, "get panel node.\n"); return 0; -- cgit From 73a46da4fa7ca088e7bbf8be817114b9ec40f01b Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 8 Mar 2022 11:06:57 -0800 Subject: drm/bridge: Add MAINTAINERS entry for DRM drivers for bridge chip bindings The bindings for bridge chips should also get the same maintainers entry so the right people get notified about bindings changes. Signed-off-by: Douglas Anderson Reviewed-by: Laurent Pinchart Acked-by: Neil Armstrong Acked-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220308110615.1.I1f1b10daf7361feb6705f789deb680b8d7720de9@changeid --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0216d2ffe728..a73179d55d00 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6403,6 +6403,7 @@ R: Jonas Karlman R: Jernej Skrabec S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc +F: Documentation/devicetree/bindings/display/bridge/ F: drivers/gpu/drm/bridge/ DRM DRIVERS FOR EXYNOS -- cgit From 59c217b3dde5927ceb37dd403d1058fa9b98cc7a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 8 Mar 2022 11:06:58 -0800 Subject: drm/bridge: Add myself as a reviewer for the TI SN65DSI86 bridge chip I've spent quite a bit of time poking at this driver and it's used on several Chromebooks I'm involved with. I'd like to get notified about patches. Add myself as a reviewer. It's expected that changes will still be landed through drm-misc as they always have been. Signed-off-by: Douglas Anderson Reviewed-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220308110615.2.I4485769d5b25a8096508e839b8fea12ce7b836d3@changeid --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a73179d55d00..7d25d0b4dccc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6284,6 +6284,11 @@ DRM DRIVER FOR TDFX VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/tdfx/ +DRM DRIVER FOR TI SN65DSI86 BRIDGE CHIP +R: Douglas Anderson +F: Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.yaml +F: drivers/gpu/drm/bridge/ti-sn65dsi86.c + DRM DRIVER FOR TPO TPG110 PANELS M: Linus Walleij S: Maintained -- cgit From 46db48f25ed116bf028eca583a0d85a8ae86dd0a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 8 Mar 2022 11:06:59 -0800 Subject: drm/bridge: Add myself as a reviewer for the Parade PS8640 bridge chip Though the parade bridge chip is a little bit of a black box, I'm at least interested in hearing about changes to the driver since this bridge chip is used on some Chromebooks that I'm involved with. Signed-off-by: Douglas Anderson Reviewed-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220308110615.3.I04f99fbcc14b8c09610b4b18f0696c992a44d2b7@changeid --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7d25d0b4dccc..db7fe53643c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6171,6 +6171,11 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.yaml F: drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c +DRM DRIVER FOR PARADE PS8640 BRIDGE CHIP +R: Douglas Anderson +F: Documentation/devicetree/bindings/display/bridge/ps8640.yaml +F: drivers/gpu/drm/bridge/parade-ps8640.c + DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS M: Noralf Trønnes S: Maintained -- cgit From 8fb6c44fe8468f92ac7b8bbfcca4404a4e88645f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Mar 2022 18:11:38 -0800 Subject: drm/bridge: analogix_dp: Grab runtime PM reference for DP-AUX If the display is not enable()d, then we aren't holding a runtime PM reference here. Thus, it's easy to accidentally cause a hang, if user space is poking around at /dev/drm_dp_aux0 at the "wrong" time. Let's get a runtime PM reference, and check that we "see" the panel. Don't force any panel power-up, etc., because that can be intrusive, and that's not what other drivers do (see drivers/gpu/drm/bridge/ti-sn65dsi86.c and drivers/gpu/drm/bridge/parade-ps8640.c.) Fixes: 0d97ad03f422 ("drm/bridge: analogix_dp: Remove duplicated code") Cc: Cc: Tomeu Vizoso Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220301181107.v4.1.I773a08785666ebb236917b0c8e6c05e3de471e75@changeid --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index eb590fb8e8d0..d45931913dfc 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1632,8 +1632,19 @@ static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { struct analogix_dp_device *dp = to_dp(aux); + int ret; + + pm_runtime_get_sync(dp->dev); + + ret = analogix_dp_detect_hpd(dp); + if (ret) + goto out; - return analogix_dp_transfer(dp, msg); + ret = analogix_dp_transfer(dp, msg); +out: + pm_runtime_put(dp->dev); + + return ret; } struct analogix_dp_device * -- cgit From f28dd50756757cde90dbf139c100fb45b13dccb3 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Mar 2022 18:11:39 -0800 Subject: drm/bridge: analogix_dp: Enable autosuspend DP AUX transactions can consist of many short operations. There's no need to power things up/down in short intervals. I pick an arbitrary 100ms; for the systems I'm testing (Rockchip RK3399), runtime-PM transitions only take a few microseconds. Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220301181107.v4.2.I48b18ab197c9b649d376cf8cfd934e59d338f86d@changeid --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index d45931913dfc..c5afccd46440 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1119,9 +1119,7 @@ static int analogix_dp_get_modes(struct drm_connector *connector) return 0; } - pm_runtime_get_sync(dp->dev); edid = drm_get_edid(connector, &dp->aux.ddc); - pm_runtime_put(dp->dev); if (edid) { drm_connector_update_edid_property(&dp->connector, edid); @@ -1642,7 +1640,8 @@ static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux, ret = analogix_dp_transfer(dp, msg); out: - pm_runtime_put(dp->dev); + pm_runtime_mark_last_busy(dp->dev); + pm_runtime_put_autosuspend(dp->dev); return ret; } @@ -1775,6 +1774,8 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) if (ret) return ret; + pm_runtime_use_autosuspend(dp->dev); + pm_runtime_set_autosuspend_delay(dp->dev, 100); pm_runtime_enable(dp->dev); ret = analogix_dp_create_bridge(drm_dev, dp); @@ -1786,6 +1787,7 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) return 0; err_disable_pm_runtime: + pm_runtime_dont_use_autosuspend(dp->dev); pm_runtime_disable(dp->dev); drm_dp_aux_unregister(&dp->aux); @@ -1804,6 +1806,7 @@ void analogix_dp_unbind(struct analogix_dp_device *dp) } drm_dp_aux_unregister(&dp->aux); + pm_runtime_dont_use_autosuspend(dp->dev); pm_runtime_disable(dp->dev); } EXPORT_SYMBOL_GPL(analogix_dp_unbind); -- cgit From 485d98d472d53f9617ffdfba5e677ac29ad4fe20 Mon Sep 17 00:00:00 2001 From: Martin Krastev Date: Wed, 2 Mar 2022 10:24:19 -0500 Subject: drm/vmwgfx: Add support for CursorMob and CursorBypass 4 * Add support for CursorMob * Add support for CursorBypass 4 * Refactor vmw_du_cursor_plane_atomic_update to be kms-helper-atomic -- move BO mappings to vmw_du_cursor_plane_prepare_fb -- move BO unmappings to vmw_du_cursor_plane_cleanup_fb Cursor mobs are a new svga feature which enables support for large cursors, e.g. large accessibility cursor on platforms with vmwgfx. It also cleans up the cursor code and makes it more uniform with the rest of modern guest backed objects support. Signed-off-by: Martin Krastev Reviewed-by: Zack Rusin Reviewed-by: Maaz Mombasawala Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-2-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 6 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 399 ++++++++++++++++++++++++++++------- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 28 ++- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 18 +- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 17 +- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 16 +- 7 files changed, 382 insertions(+), 104 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 26eb5478394a..621231c66fd4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2009-2016 VMware, Inc., Palo Alto, CA., USA + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index ea3ecdda561d..780da6147cf9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /************************************************************************** * - * Copyright 2009-2021 VMware, Inc., Palo Alto, CA., USA + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -101,6 +101,7 @@ struct vmw_fpriv { * struct vmw_buffer_object - TTM buffer object with vmwgfx additions * @base: The TTM buffer object * @res_tree: RB tree of resources using this buffer object as a backing MOB + * @base_mapped_count: ttm BO mapping count; used by KMS atomic helpers. * @cpu_writers: Number of synccpu write grabs. Protected by reservation when * increased. May be decreased without reservation. * @dx_query_ctx: DX context if this buffer object is used as a DX query MOB @@ -111,6 +112,9 @@ struct vmw_fpriv { struct vmw_buffer_object { struct ttm_buffer_object base; struct rb_root res_tree; + /* For KMS atomic helpers: ttm bo mapping count */ + atomic_t base_mapped_count; + atomic_t cpu_writers; /* Not ref-counted. Protected by binding_mutex */ struct vmw_resource *dx_query_ctx; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index bbd2f4ec08ec..9d82a7b49aed 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -41,7 +41,7 @@ void vmw_du_cleanup(struct vmw_display_unit *du) struct vmw_private *dev_priv = vmw_priv(du->primary.dev); drm_plane_cleanup(&du->primary); if (vmw_cmd_supported(dev_priv)) - drm_plane_cleanup(&du->cursor); + drm_plane_cleanup(&du->cursor.base); drm_connector_unregister(&du->connector); drm_crtc_cleanup(&du->crtc); @@ -53,23 +53,43 @@ void vmw_du_cleanup(struct vmw_display_unit *du) * Display Unit Cursor functions */ -static int vmw_cursor_update_image(struct vmw_private *dev_priv, - u32 *image, u32 width, u32 height, - u32 hotspotX, u32 hotspotY) +static void vmw_cursor_update_mob(struct vmw_private *dev_priv, + struct ttm_buffer_object *bo, + struct ttm_bo_kmap_obj *map, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY); + +struct vmw_svga_fifo_cmd_define_cursor { + u32 cmd; + SVGAFifoCmdDefineAlphaCursor cursor; +}; + +static void vmw_cursor_update_image(struct vmw_private *dev_priv, + struct ttm_buffer_object *cm_bo, + struct ttm_bo_kmap_obj *cm_map, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) { - struct { - u32 cmd; - SVGAFifoCmdDefineAlphaCursor cursor; - } *cmd; - u32 image_size = width * height * 4; - u32 cmd_size = sizeof(*cmd) + image_size; + struct vmw_svga_fifo_cmd_define_cursor *cmd; + const u32 image_size = width * height * sizeof(*image); + const u32 cmd_size = sizeof(*cmd) + image_size; - if (!image) - return -EINVAL; + if (cm_bo != NULL) { + vmw_cursor_update_mob(dev_priv, cm_bo, cm_map, image, + width, height, + hotspotX, hotspotY); + return; + } + /* Try to reserve fifocmd space and swallow any failures; + such reservations cannot be left unconsumed for long + under the risk of clogging other fifocmd users, so + we treat reservations separtely from the way we treat + other fallible KMS-atomic resources at prepare_fb */ cmd = VMW_CMD_RESERVE(dev_priv, cmd_size); + if (unlikely(cmd == NULL)) - return -ENOMEM; + return; memset(cmd, 0, sizeof(*cmd)); @@ -83,55 +103,158 @@ static int vmw_cursor_update_image(struct vmw_private *dev_priv, cmd->cursor.hotspotY = hotspotY; vmw_cmd_commit_flush(dev_priv, cmd_size); - - return 0; } -static int vmw_cursor_update_bo(struct vmw_private *dev_priv, - struct vmw_buffer_object *bo, - u32 width, u32 height, - u32 hotspotX, u32 hotspotY) -{ - struct ttm_bo_kmap_obj map; - unsigned long kmap_offset; - unsigned long kmap_num; - void *virtual; +/** + * vmw_cursor_update_mob - Update cursor vis CursorMob mechanism + * + * @dev_priv: device to work with + * @bo: BO for the MOB + * @map: kmap obj for the BO + * @image: cursor source data to fill the MOB with + * @width: source data width + * @height: source data height + * @hotspotX: cursor hotspot x + * @hotspotY: cursor hotspot Y + */ +static void vmw_cursor_update_mob(struct vmw_private *dev_priv, + struct ttm_buffer_object *bo, + struct ttm_bo_kmap_obj *map, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) +{ + SVGAGBCursorHeader *header; + SVGAGBAlphaCursorHeader *alpha_header; + const u32 image_size = width * height * sizeof(*image); bool dummy; - int ret; - kmap_offset = 0; - kmap_num = PFN_UP(width*height*4); + BUG_ON(!image); - ret = ttm_bo_reserve(&bo->base, true, false, NULL); - if (unlikely(ret != 0)) { - DRM_ERROR("reserve failed\n"); - return -EINVAL; + header = (SVGAGBCursorHeader *)ttm_kmap_obj_virtual(map, &dummy); + alpha_header = &header->header.alphaHeader; + + header->type = SVGA_ALPHA_CURSOR; + header->sizeInBytes = image_size; + + alpha_header->hotspotX = hotspotX; + alpha_header->hotspotY = hotspotY; + alpha_header->width = width; + alpha_header->height = height; + + memcpy(header + 1, image, image_size); + + vmw_write(dev_priv, SVGA_REG_CURSOR_MOBID, bo->resource->start); +} + +void vmw_du_destroy_cursor_mob_array(struct vmw_cursor_plane *vcp) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(vcp->cursor_mob); i++) { + if (vcp->cursor_mob[i] != NULL) { + ttm_bo_unpin(vcp->cursor_mob[i]); + ttm_bo_put(vcp->cursor_mob[i]); + kfree(vcp->cursor_mob[i]); + vcp->cursor_mob[i] = NULL; + } } +} - ret = ttm_bo_kmap(&bo->base, kmap_offset, kmap_num, &map); - if (unlikely(ret != 0)) - goto err_unreserve; +#define CURSOR_MOB_SIZE(dimension) \ + ((dimension) * (dimension) * sizeof(u32) + sizeof(SVGAGBCursorHeader)) - virtual = ttm_kmap_obj_virtual(&map, &dummy); - ret = vmw_cursor_update_image(dev_priv, virtual, width, height, - hotspotX, hotspotY); +int vmw_du_create_cursor_mob_array(struct vmw_cursor_plane *cursor) +{ + struct vmw_private *dev_priv = cursor->base.dev->dev_private; + uint32_t cursor_max_dim, mob_max_size; + int ret = 0; + size_t i; - ttm_bo_kunmap(&map); -err_unreserve: - ttm_bo_unreserve(&bo->base); + if (!dev_priv->has_mob || (dev_priv->capabilities2 & SVGA_CAP2_CURSOR_MOB) == 0) + return -ENOSYS; + + mob_max_size = vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE); + cursor_max_dim = vmw_read(dev_priv, SVGA_REG_CURSOR_MAX_DIMENSION); + + if (CURSOR_MOB_SIZE(cursor_max_dim) > mob_max_size) + cursor_max_dim = 64; /* Mandatorily-supported cursor dimension */ + + for (i = 0; i < ARRAY_SIZE(cursor->cursor_mob); i++) { + struct ttm_buffer_object **const bo = &cursor->cursor_mob[i]; + + ret = vmw_bo_create_kernel(dev_priv, + CURSOR_MOB_SIZE(cursor_max_dim), + &vmw_mob_placement, bo); + + if (ret != 0) + goto teardown; + + if ((*bo)->resource->mem_type != VMW_PL_MOB) { + DRM_ERROR("Obtained buffer object is not a MOB.\n"); + ret = -ENOSYS; + goto teardown; + } + + /* Fence the mob creation so we are guarateed to have the mob */ + ret = ttm_bo_reserve(*bo, false, false, NULL); + + if (ret != 0) + goto teardown; + + vmw_bo_fence_single(*bo, NULL); + + ttm_bo_unreserve(*bo); + + drm_info(&dev_priv->drm, "Using CursorMob mobid %lu, max dimension %u\n", + (*bo)->resource->start, cursor_max_dim); + } + + return 0; + +teardown: + vmw_du_destroy_cursor_mob_array(cursor); return ret; } +#undef CURSOR_MOB_SIZE + +static void vmw_cursor_update_bo(struct vmw_private *dev_priv, + struct ttm_buffer_object *cm_bo, + struct ttm_bo_kmap_obj *cm_map, + struct vmw_buffer_object *bo, + u32 width, u32 height, + u32 hotspotX, u32 hotspotY) +{ + void *virtual; + bool dummy; + + virtual = ttm_kmap_obj_virtual(&bo->map, &dummy); + if (virtual) { + vmw_cursor_update_image(dev_priv, cm_bo, cm_map, virtual, + width, height, + hotspotX, hotspotY); + atomic_dec(&bo->base_mapped_count); + } +} + static void vmw_cursor_update_position(struct vmw_private *dev_priv, bool show, int x, int y) { + const uint32_t svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW + : SVGA_CURSOR_ON_HIDE; uint32_t count; spin_lock(&dev_priv->cursor_lock); - if (vmw_is_cursor_bypass3_enabled(dev_priv)) { - vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_ON, show ? 1 : 0); + if (dev_priv->capabilities2 & SVGA_CAP2_EXTRA_REGS) { + vmw_write(dev_priv, SVGA_REG_CURSOR4_X, x); + vmw_write(dev_priv, SVGA_REG_CURSOR4_Y, y); + vmw_write(dev_priv, SVGA_REG_CURSOR4_SCREEN_ID, SVGA3D_INVALID_ID); + vmw_write(dev_priv, SVGA_REG_CURSOR4_ON, svga_cursor_on); + vmw_write(dev_priv, SVGA_REG_CURSOR4_SUBMIT, TRUE); + } else if (vmw_is_cursor_bypass3_enabled(dev_priv)) { + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_ON, svga_cursor_on); vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_X, x); vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_Y, y); count = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CURSOR_COUNT); @@ -139,7 +262,7 @@ static void vmw_cursor_update_position(struct vmw_private *dev_priv, } else { vmw_write(dev_priv, SVGA_REG_CURSOR_X, x); vmw_write(dev_priv, SVGA_REG_CURSOR_Y, y); - vmw_write(dev_priv, SVGA_REG_CURSOR_ON, show ? 1 : 0); + vmw_write(dev_priv, SVGA_REG_CURSOR_ON, svga_cursor_on); } spin_unlock(&dev_priv->cursor_lock); } @@ -269,7 +392,7 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) continue; du->cursor_age = du->cursor_surface->snooper.age; - vmw_cursor_update_image(dev_priv, + vmw_cursor_update_image(dev_priv, NULL, NULL, du->cursor_surface->snooper.image, 64, 64, du->hotspot_x + du->core_hotspot_x, @@ -283,7 +406,7 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) void vmw_du_cursor_plane_destroy(struct drm_plane *plane) { vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); - + vmw_du_destroy_cursor_mob_array(vmw_plane_to_vcp(plane)); drm_plane_cleanup(plane); } @@ -321,7 +444,7 @@ void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, /** - * vmw_du_plane_cleanup_fb - Unpins the cursor + * vmw_du_plane_cleanup_fb - Unpins the plane surface * * @plane: display plane * @old_state: Contains the FB to clean up @@ -340,6 +463,55 @@ vmw_du_plane_cleanup_fb(struct drm_plane *plane, } +/** + * vmw_du_cursor_plane_cleanup_fb - Unpins the plane surface + * + * @plane: cursor plane + * @old_state: contains the state to clean up + * + * Unmaps all cursor bo mappings and unpins the cursor surface + * + * Returns 0 on success + */ +void +vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + bool dummy; + + if (vps->bo != NULL && ttm_kmap_obj_virtual(&vps->bo->map, &dummy) != NULL) { + const int ret = ttm_bo_reserve(&vps->bo->base, true, false, NULL); + + if (likely(ret == 0)) { + if (atomic_read(&vps->bo->base_mapped_count) == 0) + ttm_bo_kunmap(&vps->bo->map); + ttm_bo_unreserve(&vps->bo->base); + } + } + + if (vps->cm_bo != NULL && ttm_kmap_obj_virtual(&vps->cm_map, &dummy) != NULL) { + const int ret = ttm_bo_reserve(vps->cm_bo, true, false, NULL); + + if (likely(ret == 0)) { + ttm_bo_kunmap(&vps->cm_map); + ttm_bo_unreserve(vps->cm_bo); + } + } + + vmw_du_plane_unpin_surf(vps, false); + + if (vps->surf) { + vmw_surface_unreference(&vps->surf); + vps->surf = NULL; + } + + if (vps->bo) { + vmw_bo_unreference(&vps->bo); + vps->bo = NULL; + } +} + /** * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it * @@ -353,14 +525,21 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { struct drm_framebuffer *fb = new_state->fb; + struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + struct ttm_buffer_object *cm_bo = NULL; + bool dummy; + int ret = 0; - - if (vps->surf) + if (vps->surf) { vmw_surface_unreference(&vps->surf); + vps->surf = NULL; + } - if (vps->bo) + if (vps->bo) { vmw_bo_unreference(&vps->bo); + vps->bo = NULL; + } if (fb) { if (vmw_framebuffer_to_vfb(fb)->bo) { @@ -372,7 +551,82 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, } } + vps->cm_bo = NULL; + + if (vps->surf == NULL && vps->bo != NULL) { + const u32 size = new_state->crtc_w * new_state->crtc_h * sizeof(u32); + + /* Not using vmw_bo_map_and_cache() helper here as we need to reserve + the ttm_buffer_object first which wmw_bo_map_and_cache() omits. */ + ret = ttm_bo_reserve(&vps->bo->base, true, false, NULL); + + if (unlikely(ret != 0)) + return -ENOMEM; + + ret = ttm_bo_kmap(&vps->bo->base, 0, PFN_UP(size), &vps->bo->map); + + if (likely(ret == 0)) + atomic_inc(&vps->bo->base_mapped_count); + + ttm_bo_unreserve(&vps->bo->base); + + if (unlikely(ret != 0)) + return -ENOMEM; + } + + if (vps->surf || vps->bo) { + unsigned cursor_mob_idx = vps->cursor_mob_idx; + + /* Lazily set up cursor MOBs just once -- no reattempts. */ + if (cursor_mob_idx == 0 && vcp->cursor_mob[0] == NULL) + if (vmw_du_create_cursor_mob_array(vcp) != 0) + vps->cursor_mob_idx = cursor_mob_idx = -1U; + + if (cursor_mob_idx < ARRAY_SIZE(vcp->cursor_mob)) { + const u32 size = sizeof(SVGAGBCursorHeader) + + new_state->crtc_w * new_state->crtc_h * sizeof(u32); + + cm_bo = vcp->cursor_mob[cursor_mob_idx]; + + if (cm_bo->resource->num_pages * PAGE_SIZE < size) { + ret = -EINVAL; + goto error_bo_unmap; + } + + ret = ttm_bo_reserve(cm_bo, false, false, NULL); + + if (unlikely(ret != 0)) { + ret = -ENOMEM; + goto error_bo_unmap; + } + + ret = ttm_bo_kmap(cm_bo, 0, PFN_UP(size), &vps->cm_map); + + ttm_bo_unreserve(cm_bo); + + if (unlikely(ret != 0)) { + ret = -ENOMEM; + goto error_bo_unmap; + } + + vps->cursor_mob_idx = cursor_mob_idx ^ 1; + vps->cm_bo = cm_bo; + } + } + return 0; + +error_bo_unmap: + if (vps->bo != NULL && ttm_kmap_obj_virtual(&vps->bo->map, &dummy) != NULL) { + const int ret = ttm_bo_reserve(&vps->bo->base, true, false, NULL); + if (likely(ret == 0)) { + atomic_dec(&vps->bo->base_mapped_count); + ttm_bo_kunmap(&vps->bo->map); + ttm_bo_unreserve(&vps->bo->base); + } + } + + return ret; } @@ -389,8 +643,6 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, struct vmw_display_unit *du = vmw_crtc_to_du(crtc); struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); s32 hotspot_x, hotspot_y; - int ret = 0; - hotspot_x = du->hotspot_x; hotspot_y = du->hotspot_y; @@ -406,33 +658,31 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, if (vps->surf) { du->cursor_age = du->cursor_surface->snooper.age; - ret = vmw_cursor_update_image(dev_priv, - vps->surf->snooper.image, - 64, 64, hotspot_x, - hotspot_y); + vmw_cursor_update_image(dev_priv, vps->cm_bo, &vps->cm_map, + vps->surf->snooper.image, + new_state->crtc_w, + new_state->crtc_h, + hotspot_x, hotspot_y); } else if (vps->bo) { - ret = vmw_cursor_update_bo(dev_priv, vps->bo, - new_state->crtc_w, - new_state->crtc_h, - hotspot_x, hotspot_y); + vmw_cursor_update_bo(dev_priv, vps->cm_bo, &vps->cm_map, + vps->bo, + new_state->crtc_w, + new_state->crtc_h, + hotspot_x, hotspot_y); } else { vmw_cursor_update_position(dev_priv, false, 0, 0); return; } - if (!ret) { - du->cursor_x = new_state->crtc_x + du->set_gui_x; - du->cursor_y = new_state->crtc_y + du->set_gui_y; + du->cursor_x = new_state->crtc_x + du->set_gui_x; + du->cursor_y = new_state->crtc_y + du->set_gui_y; - vmw_cursor_update_position(dev_priv, true, - du->cursor_x + hotspot_x, - du->cursor_y + hotspot_y); + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + hotspot_x, + du->cursor_y + hotspot_y); - du->core_hotspot_x = hotspot_x - du->hotspot_x; - du->core_hotspot_y = hotspot_y - du->hotspot_y; - } else { - DRM_ERROR("Failed to update cursor image\n"); - } + du->core_hotspot_x = hotspot_x - du->hotspot_x; + du->core_hotspot_y = hotspot_y - du->hotspot_y; } @@ -518,7 +768,7 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", new_state->crtc_w, new_state->crtc_h); - ret = -EINVAL; + return -EINVAL; } if (!vmw_framebuffer_to_vfb(fb)->bo) @@ -526,10 +776,10 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, if (surface && !surface->snooper.image) { DRM_ERROR("surface not suitable for cursor\n"); - ret = -EINVAL; + return -EINVAL; } - return ret; + return 0; } @@ -712,7 +962,6 @@ void vmw_du_plane_reset(struct drm_plane *plane) { struct vmw_plane_state *vps; - if (plane->state) vmw_du_plane_destroy_state(plane, plane->state); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 4d36e8507380..c95be95deb8d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /************************************************************************** * - * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -261,6 +261,7 @@ static const uint32_t __maybe_unused vmw_cursor_plane_formats[] = { #define vmw_plane_state_to_vps(x) container_of(x, struct vmw_plane_state, base) #define vmw_connector_state_to_vcs(x) \ container_of(x, struct vmw_connector_state, base) +#define vmw_plane_to_vcp(x) container_of(x, struct vmw_cursor_plane, base) /** * Derived class for crtc state object @@ -293,6 +294,14 @@ struct vmw_plane_state { /* For CPU Blit */ unsigned int cpp; + + /* CursorMob flipping index; -1 if cursor mobs not used */ + unsigned int cursor_mob_idx; + /* Currently-active CursorMob */ + struct ttm_buffer_object *cm_bo; + /* CursorMob kmap_obj; expected valid at cursor_plane_atomic_update + IFF currently-active CursorMob above is valid */ + struct ttm_bo_kmap_obj cm_map; }; @@ -325,6 +334,17 @@ struct vmw_connector_state { int gui_y; }; +/** + * Derived class for cursor plane object + * + * @base DRM plane object + * @cursor_mob array of two MOBs for CursorMob flipping + */ +struct vmw_cursor_plane { + struct drm_plane base; + struct ttm_buffer_object *cursor_mob[2]; +}; + /** * Base class display unit. * @@ -337,7 +357,7 @@ struct vmw_display_unit { struct drm_encoder encoder; struct drm_connector connector; struct drm_plane primary; - struct drm_plane cursor; + struct vmw_cursor_plane cursor; struct vmw_surface *cursor_surface; struct vmw_buffer_object *cursor_bo; @@ -452,6 +472,8 @@ void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv); /* Universal Plane Helpers */ void vmw_du_primary_plane_destroy(struct drm_plane *plane); void vmw_du_cursor_plane_destroy(struct drm_plane *plane); +int vmw_du_create_cursor_mob_array(struct vmw_cursor_plane *vcp); +void vmw_du_destroy_cursor_mob_array(struct vmw_cursor_plane *vcp); /* Atomic Helpers */ int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, @@ -462,6 +484,8 @@ void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state); int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state); +void vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); void vmw_du_plane_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state); void vmw_du_plane_reset(struct drm_plane *plane); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index fb58a71c458f..643c1608ddfd 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -338,7 +338,7 @@ drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = { .atomic_check = vmw_du_cursor_plane_atomic_check, .atomic_update = vmw_du_cursor_plane_atomic_update, .prepare_fb = vmw_du_cursor_plane_prepare_fb, - .cleanup_fb = vmw_du_plane_cleanup_fb, + .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, }; static const struct @@ -363,7 +363,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = &dev_priv->drm; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_plane *primary, *cursor; + struct drm_plane *primary; + struct vmw_cursor_plane *cursor; struct drm_crtc *crtc; int ret; @@ -392,7 +393,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) ldu->base.is_implicit = true; /* Initialize primary plane */ - ret = drm_universal_plane_init(dev, &ldu->base.primary, + ret = drm_universal_plane_init(dev, primary, 0, &vmw_ldu_plane_funcs, vmw_primary_plane_formats, ARRAY_SIZE(vmw_primary_plane_formats), @@ -409,7 +410,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) */ if (vmw_cmd_supported(dev_priv)) { /* Initialize cursor plane */ - ret = drm_universal_plane_init(dev, &ldu->base.cursor, + ret = drm_universal_plane_init(dev, &cursor->base, 0, &vmw_ldu_cursor_funcs, vmw_cursor_plane_formats, ARRAY_SIZE(vmw_cursor_plane_formats), @@ -420,7 +421,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } - drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs); + drm_plane_helper_add(&cursor->base, &vmw_ldu_cursor_plane_helper_funcs); } ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, @@ -450,9 +451,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } - ret = drm_crtc_init_with_planes( - dev, crtc, &ldu->base.primary, - vmw_cmd_supported(dev_priv) ? &ldu->base.cursor : NULL, + ret = drm_crtc_init_with_planes(dev, crtc, primary, + vmw_cmd_supported(dev_priv) ? &cursor->base : NULL, &vmw_legacy_crtc_funcs, NULL); if (ret) { DRM_ERROR("Failed to initialize CRTC\n"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 3004c7a719e9..c89ad3a2d141 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2011-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright 2011-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -804,7 +804,7 @@ drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = { .atomic_check = vmw_du_cursor_plane_atomic_check, .atomic_update = vmw_du_cursor_plane_atomic_update, .prepare_fb = vmw_du_cursor_plane_prepare_fb, - .cleanup_fb = vmw_du_plane_cleanup_fb, + .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, }; static const struct @@ -832,7 +832,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = &dev_priv->drm; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_plane *primary, *cursor; + struct drm_plane *primary; + struct vmw_cursor_plane *cursor; struct drm_crtc *crtc; int ret; @@ -859,7 +860,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) sou->base.is_implicit = false; /* Initialize primary plane */ - ret = drm_universal_plane_init(dev, &sou->base.primary, + ret = drm_universal_plane_init(dev, primary, 0, &vmw_sou_plane_funcs, vmw_primary_plane_formats, ARRAY_SIZE(vmw_primary_plane_formats), @@ -873,7 +874,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) drm_plane_enable_fb_damage_clips(primary); /* Initialize cursor plane */ - ret = drm_universal_plane_init(dev, &sou->base.cursor, + ret = drm_universal_plane_init(dev, &cursor->base, 0, &vmw_sou_cursor_funcs, vmw_cursor_plane_formats, ARRAY_SIZE(vmw_cursor_plane_formats), @@ -884,7 +885,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } - drm_plane_helper_add(cursor, &vmw_sou_cursor_plane_helper_funcs); + drm_plane_helper_add(&cursor->base, &vmw_sou_cursor_plane_helper_funcs); ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); @@ -913,8 +914,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } - ret = drm_crtc_init_with_planes(dev, crtc, &sou->base.primary, - &sou->base.cursor, + ret = drm_crtc_init_with_planes(dev, crtc, primary, + &cursor->base, &vmw_screen_object_crtc_funcs, NULL); if (ret) { DRM_ERROR("Failed to initialize CRTC\n"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index ae9a6044d448..42b5ecb0c5e9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /****************************************************************************** * - * COPYRIGHT (C) 2014-2015 VMware, Inc., Palo Alto, CA., USA + * COPYRIGHT (C) 2014-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -1685,7 +1685,7 @@ drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = { .atomic_check = vmw_du_cursor_plane_atomic_check, .atomic_update = vmw_du_cursor_plane_atomic_update, .prepare_fb = vmw_du_cursor_plane_prepare_fb, - .cleanup_fb = vmw_du_plane_cleanup_fb, + .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, }; static const struct @@ -1723,11 +1723,11 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) struct drm_device *dev = &dev_priv->drm; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_plane *primary, *cursor; + struct drm_plane *primary; + struct vmw_cursor_plane *cursor; struct drm_crtc *crtc; int ret; - stdu = kzalloc(sizeof(*stdu), GFP_KERNEL); if (!stdu) return -ENOMEM; @@ -1759,7 +1759,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) drm_plane_enable_fb_damage_clips(primary); /* Initialize cursor plane */ - ret = drm_universal_plane_init(dev, cursor, + ret = drm_universal_plane_init(dev, &cursor->base, 0, &vmw_stdu_cursor_funcs, vmw_cursor_plane_formats, ARRAY_SIZE(vmw_cursor_plane_formats), @@ -1770,7 +1770,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free; } - drm_plane_helper_add(cursor, &vmw_stdu_cursor_plane_helper_funcs); + drm_plane_helper_add(&cursor->base, &vmw_stdu_cursor_plane_helper_funcs); ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); @@ -1799,8 +1799,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_encoder; } - ret = drm_crtc_init_with_planes(dev, crtc, &stdu->base.primary, - &stdu->base.cursor, + ret = drm_crtc_init_with_planes(dev, crtc, primary, + &cursor->base, &vmw_stdu_crtc_funcs, NULL); if (ret) { DRM_ERROR("Failed to initialize CRTC\n"); -- cgit From bb30d8d8c1b0002712f112c519c9caf4fdf8c9d1 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Wed, 2 Mar 2022 10:24:20 -0500 Subject: drm/vmwgfx: Cleanup multimon initialization code The results of the legacy display unit initialization were being silently ignored. Unifying the selection of number of display units based on whether the underlying device supports multimon makes it easier to add error checking to all paths. This makes the driver report the errors in ldu initialization paths and try to recover from them. Signed-off-by: Zack Rusin Reviewed-by: Martin Krastev Reviewed-by: Maaz Mombasawala Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-3-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 643c1608ddfd..e4347faccee0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -492,6 +492,8 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) { struct drm_device *dev = &dev_priv->drm; int i, ret; + int num_display_units = (dev_priv->capabilities & SVGA_CAP_MULTIMON) ? + VMWGFX_NUM_DISPLAY_UNITS : 1; if (unlikely(dev_priv->ldu_priv)) { return -EINVAL; @@ -506,21 +508,17 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) dev_priv->ldu_priv->last_num_active = 0; dev_priv->ldu_priv->fb = NULL; - /* for old hardware without multimon only enable one display */ - if (dev_priv->capabilities & SVGA_CAP_MULTIMON) - ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); - else - ret = drm_vblank_init(dev, 1); + ret = drm_vblank_init(dev, num_display_units); if (ret != 0) goto err_free; vmw_kms_create_implicit_placement_property(dev_priv); - if (dev_priv->capabilities & SVGA_CAP_MULTIMON) - for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) - vmw_ldu_init(dev_priv, i); - else - vmw_ldu_init(dev_priv, 0); + for (i = 0; i < num_display_units; ++i) { + ret = vmw_ldu_init(dev_priv, i); + if (ret != 0) + goto err_free; + } dev_priv->active_display_unit = vmw_du_legacy; -- cgit From af326e28380ab858cf7e0c67ba04c4f38a7a41ea Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Wed, 2 Mar 2022 10:24:21 -0500 Subject: drm/vmwgfx: Print capabilities early during the initialization Capabilities were logged at the end of initialization so any early errors would make them not appear in the logs. Which is also when they're needed the most. Print the the capabilities right after fetching them, before the init code starts using them to make sure they always show up in the logs. Signed-off-by: Zack Rusin Reviewed-by: Martin Krastev Reviewed-by: Maaz Mombasawala Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-4-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 621231c66fd4..f43afd56915e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -848,12 +848,16 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); - + vmw_print_bitmap(&dev_priv->drm, "Capabilities", + dev_priv->capabilities, + cap1_names, ARRAY_SIZE(cap1_names)); if (dev_priv->capabilities & SVGA_CAP_CAP2_REGISTER) { dev_priv->capabilities2 = vmw_read(dev_priv, SVGA_REG_CAP2); + vmw_print_bitmap(&dev_priv->drm, "Capabilities2", + dev_priv->capabilities2, + cap2_names, ARRAY_SIZE(cap2_names)); } - ret = vmw_dma_select_mode(dev_priv); if (unlikely(ret != 0)) { drm_info(&dev_priv->drm, @@ -939,14 +943,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) "MOB limits: max mob size = %u kB, max mob pages = %u\n", dev_priv->max_mob_size / 1024, dev_priv->max_mob_pages); - vmw_print_bitmap(&dev_priv->drm, "Capabilities", - dev_priv->capabilities, - cap1_names, ARRAY_SIZE(cap1_names)); - if (dev_priv->capabilities & SVGA_CAP_CAP2_REGISTER) - vmw_print_bitmap(&dev_priv->drm, "Capabilities2", - dev_priv->capabilities2, - cap2_names, ARRAY_SIZE(cap2_names)); - ret = vmw_dma_masks(dev_priv); if (unlikely(ret != 0)) goto out_err0; -- cgit From c593197b6ece417675256e220b5eac25c0b33290 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Wed, 2 Mar 2022 10:24:22 -0500 Subject: drm/vmwgfx: Fix fencing on SVGAv3 Port of the vmwgfx to SVGAv3 lacked support for fencing. SVGAv3 removed FIFO's and replaced them with command buffers and extra registers. The initial version of SVGAv3 lacked support for most advanced features (e.g. 3D) which made fences unnecessary. That is no longer the case, especially as 3D support is being turned on. Switch from FIFO commands and capabilities to command buffers and extra registers to enable fences on SVGAv3. Fixes: 2cd80dbd3551 ("drm/vmwgfx: Add basic support for SVGA3") Signed-off-by: Zack Rusin Reviewed-by: Martin Krastev Reviewed-by: Maaz Mombasawala Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-5-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 8 ++++++++ drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 28 +++++++++++++++++++++------- drivers/gpu/drm/vmwgfx/vmwgfx_irq.c | 26 ++++++++++++++++++-------- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 8 +++++--- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c index a3bfbb6c3e14..bf1b394753da 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c @@ -528,7 +528,7 @@ int vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno) *seqno = atomic_add_return(1, &dev_priv->marker_seq); } while (*seqno == 0); - if (!(vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_FENCE)) { + if (!vmw_has_fences(dev_priv)) { /* * Don't request hardware to send a fence. The diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 780da6147cf9..12eb4de41036 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -1683,4 +1683,12 @@ static inline void vmw_irq_status_write(struct vmw_private *vmw, outl(status, vmw->io_start + SVGA_IRQSTATUS_PORT); } +static inline bool vmw_has_fences(struct vmw_private *vmw) +{ + if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | + SVGA_CAP_CMD_BUFFERS_2)) != 0) + return true; + return (vmw_fifo_caps(vmw) & SVGA_FIFO_CAP_FENCE) != 0; +} + #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 59d6a2dd4c2e..66cc35dc223e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -82,6 +82,22 @@ fman_from_fence(struct vmw_fence_obj *fence) return container_of(fence->base.lock, struct vmw_fence_manager, lock); } +static u32 vmw_fence_goal_read(struct vmw_private *vmw) +{ + if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) + return vmw_read(vmw, SVGA_REG_FENCE_GOAL); + else + return vmw_fifo_mem_read(vmw, SVGA_FIFO_FENCE_GOAL); +} + +static void vmw_fence_goal_write(struct vmw_private *vmw, u32 value) +{ + if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) + vmw_write(vmw, SVGA_REG_FENCE_GOAL, value); + else + vmw_fifo_mem_write(vmw, SVGA_FIFO_FENCE_GOAL, value); +} + /* * Note on fencing subsystem usage of irqs: * Typically the vmw_fences_update function is called @@ -392,7 +408,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, if (likely(!fman->seqno_valid)) return false; - goal_seqno = vmw_fifo_mem_read(fman->dev_priv, SVGA_FIFO_FENCE_GOAL); + goal_seqno = vmw_fence_goal_read(fman->dev_priv); if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP)) return false; @@ -400,9 +416,8 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, list_for_each_entry(fence, &fman->fence_list, head) { if (!list_empty(&fence->seq_passed_actions)) { fman->seqno_valid = true; - vmw_fifo_mem_write(fman->dev_priv, - SVGA_FIFO_FENCE_GOAL, - fence->base.seqno); + vmw_fence_goal_write(fman->dev_priv, + fence->base.seqno); break; } } @@ -434,13 +449,12 @@ static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence) if (dma_fence_is_signaled_locked(&fence->base)) return false; - goal_seqno = vmw_fifo_mem_read(fman->dev_priv, SVGA_FIFO_FENCE_GOAL); + goal_seqno = vmw_fence_goal_read(fman->dev_priv); if (likely(fman->seqno_valid && goal_seqno - fence->base.seqno < VMW_FENCE_WRAP)) return false; - vmw_fifo_mem_write(fman->dev_priv, SVGA_FIFO_FENCE_GOAL, - fence->base.seqno); + vmw_fence_goal_write(fman->dev_priv, fence->base.seqno); fman->seqno_valid = true; return true; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index c5191de365ca..fe4732bf2c9d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -32,6 +32,14 @@ #define VMW_FENCE_WRAP (1 << 24) +static u32 vmw_irqflag_fence_goal(struct vmw_private *vmw) +{ + if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) + return SVGA_IRQFLAG_REG_FENCE_GOAL; + else + return SVGA_IRQFLAG_FENCE_GOAL; +} + /** * vmw_thread_fn - Deferred (process context) irq handler * @@ -96,7 +104,7 @@ static irqreturn_t vmw_irq_handler(int irq, void *arg) wake_up_all(&dev_priv->fifo_queue); if ((masked_status & (SVGA_IRQFLAG_ANY_FENCE | - SVGA_IRQFLAG_FENCE_GOAL)) && + vmw_irqflag_fence_goal(dev_priv))) && !test_and_set_bit(VMW_IRQTHREAD_FENCE, dev_priv->irqthread_pending)) ret = IRQ_WAKE_THREAD; @@ -137,8 +145,7 @@ bool vmw_seqno_passed(struct vmw_private *dev_priv, if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) return true; - if (!(vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_FENCE) && - vmw_fifo_idle(dev_priv, seqno)) + if (!vmw_has_fences(dev_priv) && vmw_fifo_idle(dev_priv, seqno)) return true; /** @@ -160,6 +167,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, unsigned long timeout) { struct vmw_fifo_state *fifo_state = dev_priv->fifo; + bool fifo_down = false; uint32_t count = 0; uint32_t signal_seq; @@ -176,12 +184,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, */ if (fifo_idle) { - down_read(&fifo_state->rwsem); if (dev_priv->cman) { ret = vmw_cmdbuf_idle(dev_priv->cman, interruptible, 10*HZ); if (ret) goto out_err; + } else if (fifo_state) { + down_read(&fifo_state->rwsem); + fifo_down = true; } } @@ -218,12 +228,12 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, } } finish_wait(&dev_priv->fence_queue, &__wait); - if (ret == 0 && fifo_idle) + if (ret == 0 && fifo_idle && fifo_state) vmw_fence_write(dev_priv, signal_seq); wake_up_all(&dev_priv->fence_queue); out_err: - if (fifo_idle) + if (fifo_down) up_read(&fifo_state->rwsem); return ret; @@ -266,13 +276,13 @@ void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) void vmw_goal_waiter_add(struct vmw_private *dev_priv) { - vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_FENCE_GOAL, + vmw_generic_waiter_add(dev_priv, vmw_irqflag_fence_goal(dev_priv), &dev_priv->goal_queue_waiters); } void vmw_goal_waiter_remove(struct vmw_private *dev_priv) { - vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_FENCE_GOAL, + vmw_generic_waiter_remove(dev_priv, vmw_irqflag_fence_goal(dev_priv), &dev_priv->goal_queue_waiters); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 9d82a7b49aed..af252210ef84 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1593,7 +1593,6 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv, ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, mode_cmd, is_bo_proxy); - /* * vmw_create_bo_proxy() adds a reference that is no longer * needed @@ -1634,13 +1633,16 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, ret = vmw_user_lookup_handle(dev_priv, file_priv, mode_cmd->handles[0], &surface, &bo); - if (ret) + if (ret) { + DRM_ERROR("Invalid buffer object handle %u (0x%x).\n", + mode_cmd->handles[0], mode_cmd->handles[0]); goto err_out; + } if (!bo && !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { - DRM_ERROR("Surface size cannot exceed %dx%d", + DRM_ERROR("Surface size cannot exceed %dx%d\n", dev_priv->texture_max_width, dev_priv->texture_max_height); goto err_out; -- cgit From 23b0e695d4ca92e9ebd97689e2e51e497957cb48 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Wed, 2 Mar 2022 10:24:23 -0500 Subject: drm/vmwgfx: Allow querying of the SVGA PCI id from the userspace Mesa3D loaders require knowledge of the devices PCI id. SVGAv2 and v3 have different PCI id's, but the same driver is used to handle them both. To allow Mesa3D svga driver to be loaded automatically for both SVGAv2 and SVGAv3 make the kernel return the PCI id of the currently running device. Signed-off-by: Zack Rusin Reviewed-by: Martin Krastev Reviewed-by: Maaz Mombasawala Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-6-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 27 +++++++++++++++------------ include/uapi/drm/vmwgfx_drm.h | 9 ++++++++- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 471da2b4c177..a1da5678c731 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -27,9 +27,11 @@ #include "vmwgfx_drv.h" #include "vmwgfx_devcaps.h" -#include #include "vmwgfx_kms.h" +#include +#include + int vmw_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -62,17 +64,15 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, break; case DRM_VMW_PARAM_FIFO_HW_VERSION: { - if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) { + if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) param->value = SVGA3D_HWVERSION_WS8_B1; - break; - } - - param->value = - vmw_fifo_mem_read(dev_priv, - ((vmw_fifo_caps(dev_priv) & - SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? - SVGA_FIFO_3D_HWVERSION_REVISED : - SVGA_FIFO_3D_HWVERSION)); + else + param->value = vmw_fifo_mem_read( + dev_priv, + ((vmw_fifo_caps(dev_priv) & + SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ? + SVGA_FIFO_3D_HWVERSION_REVISED : + SVGA_FIFO_3D_HWVERSION)); break; } case DRM_VMW_PARAM_MAX_SURF_MEMORY: @@ -108,6 +108,9 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, case DRM_VMW_PARAM_GL43: param->value = has_gl43_context(dev_priv); break; + case DRM_VMW_PARAM_DEVICE_ID: + param->value = to_pci_dev(dev_priv->drm.dev)->device; + break; default: return -EINVAL; } diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index 8277644c1144..26549c86a91f 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA + * Copyright © 2009-2022 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -92,6 +92,12 @@ extern "C" { * * DRM_VMW_PARAM_SM5 * SM5 support is enabled. + * + * DRM_VMW_PARAM_GL43 + * SM5.1+GL4.3 support is enabled. + * + * DRM_VMW_PARAM_DEVICE_ID + * PCI ID of the underlying SVGA device. */ #define DRM_VMW_PARAM_NUM_STREAMS 0 @@ -111,6 +117,7 @@ extern "C" { #define DRM_VMW_PARAM_SM4_1 14 #define DRM_VMW_PARAM_SM5 15 #define DRM_VMW_PARAM_GL43 16 +#define DRM_VMW_PARAM_DEVICE_ID 17 /** * enum drm_vmw_handle_type - handle type for ref ioctls -- cgit From 5405d25b9e8e6e0d3bdb04833d528a9bb35fe7ce Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Wed, 2 Mar 2022 10:24:24 -0500 Subject: drm/vmwgfx: Initialize drm_mode_fb_cmd2 Transition to drm_mode_fb_cmd2 from drm_mode_fb_cmd left the structure unitialized. drm_mode_fb_cmd2 adds a few additional members, e.g. flags and modifiers which were never initialized. Garbage in those members can cause random failures during the bringup of the fbcon. Initializing the structure fixes random blank screens after bootup due to flags/modifiers mismatches during the fbcon bring up. Fixes: dabdcdc9822a ("drm/vmwgfx: Switch to mode_cmd2") Signed-off-by: Zack Rusin Cc: Daniel Vetter Cc: # v4.10+ Reviewed-by: Martin Krastev Reviewed-by: Maaz Mombasawala Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-7-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 8ee34576c7d0..adf17c740656 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -483,7 +483,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, static int vmw_fb_kms_framebuffer(struct fb_info *info) { - struct drm_mode_fb_cmd2 mode_cmd; + struct drm_mode_fb_cmd2 mode_cmd = {0}; struct vmw_fb_par *par = info->par; struct fb_var_screeninfo *var = &info->var; struct drm_framebuffer *cur_fb; -- cgit From 32160e6a98f33cd96fde52cd56b9b758b59d3603 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Mon, 7 Mar 2022 11:24:12 -0500 Subject: drm/vmwgfx: Implement MSI/MSI-X support for IRQs SVGAv3 deprecates legacy interrupts and adds support for MSI/MSI-X. With MSI the driver visible side remains largely unchanged but with MSI-X each interrupt gets delivered on its own vector. Add support for MSI/MSI-X while preserving the old functionality for SVGAv2. Code between the SVGAv2 and SVGAv3 is exactly the same, only the number of available vectors changes, in particular between legacy and MSI-X interrupts. Signed-off-by: Zack Rusin Reviewed-by: Martin Krastev Reviewed-by: Maaz Mombasawala Link: https://patchwork.freedesktop.org/patch/msgid/20220307162412.1183049-1-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 9 +++++- drivers/gpu/drm/vmwgfx/vmwgfx_irq.c | 55 +++++++++++++++++++++++++++++++++---- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index f43afd56915e..791f9a5f3868 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -980,7 +980,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) } if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { - ret = vmw_irq_install(&dev_priv->drm, pdev->irq); + ret = vmw_irq_install(dev_priv); if (ret != 0) { drm_err(&dev_priv->drm, "Failed installing irq: %d\n", ret); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 12eb4de41036..be19aa6e1f13 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -65,6 +65,11 @@ #define VMWGFX_PCI_ID_SVGA2 0x0405 #define VMWGFX_PCI_ID_SVGA3 0x0406 +/* + * This has to match get_count_order(SVGA_IRQFLAG_MAX) + */ +#define VMWGFX_MAX_NUM_IRQS 6 + /* * Perhaps we should have sysfs entries for these. */ @@ -532,6 +537,8 @@ struct vmw_private { bool has_mob; spinlock_t hw_lock; bool assume_16bpp; + u32 irqs[VMWGFX_MAX_NUM_IRQS]; + u32 num_irq_vectors; enum vmw_sm_type sm_type; @@ -1158,7 +1165,7 @@ bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd); * IRQs and wating - vmwgfx_irq.c */ -extern int vmw_irq_install(struct drm_device *dev, int irq); +extern int vmw_irq_install(struct vmw_private *dev_priv); extern void vmw_irq_uninstall(struct drm_device *dev); extern bool vmw_seqno_passed(struct vmw_private *dev_priv, uint32_t seqno); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index fe4732bf2c9d..086e69a130d4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -300,6 +300,7 @@ void vmw_irq_uninstall(struct drm_device *dev) struct vmw_private *dev_priv = vmw_priv(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); uint32_t status; + u32 i; if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) return; @@ -309,20 +310,62 @@ void vmw_irq_uninstall(struct drm_device *dev) status = vmw_irq_status_read(dev_priv); vmw_irq_status_write(dev_priv, status); - free_irq(pdev->irq, dev); + for (i = 0; i < dev_priv->num_irq_vectors; ++i) + free_irq(dev_priv->irqs[i], dev); + + pci_free_irq_vectors(pdev); + dev_priv->num_irq_vectors = 0; } /** * vmw_irq_install - Install the irq handlers * - * @dev: Pointer to the drm device. - * @irq: The irq number. + * @dev_priv: Pointer to the vmw_private device. * Return: Zero if successful. Negative number otherwise. */ -int vmw_irq_install(struct drm_device *dev, int irq) +int vmw_irq_install(struct vmw_private *dev_priv) { + struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); + struct drm_device *dev = &dev_priv->drm; + int ret; + int nvec; + int i = 0; + + BUILD_BUG_ON((SVGA_IRQFLAG_MAX >> VMWGFX_MAX_NUM_IRQS) != 1); + BUG_ON(VMWGFX_MAX_NUM_IRQS != get_count_order(SVGA_IRQFLAG_MAX)); + + nvec = pci_alloc_irq_vectors(pdev, 1, VMWGFX_MAX_NUM_IRQS, + PCI_IRQ_ALL_TYPES); + + if (nvec <= 0) { + drm_err(&dev_priv->drm, + "IRQ's are unavailable, nvec: %d\n", nvec); + ret = nvec; + goto done; + } + vmw_irq_preinstall(dev); - return request_threaded_irq(irq, vmw_irq_handler, vmw_thread_fn, - IRQF_SHARED, VMWGFX_DRIVER_NAME, dev); + for (i = 0; i < nvec; ++i) { + ret = pci_irq_vector(pdev, i); + if (ret < 0) { + drm_err(&dev_priv->drm, + "failed getting irq vector: %d\n", ret); + goto done; + } + dev_priv->irqs[i] = ret; + + ret = request_threaded_irq(dev_priv->irqs[i], vmw_irq_handler, vmw_thread_fn, + IRQF_SHARED, VMWGFX_DRIVER_NAME, dev); + if (ret != 0) { + drm_err(&dev_priv->drm, + "Failed installing irq(%d): %d\n", + dev_priv->irqs[i], ret); + goto done; + } + } + +done: + dev_priv->num_irq_vectors = i; + return ret; } -- cgit From b861de246912e10c5bda32cb100bff429a5e1ec4 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Wed, 2 Mar 2022 10:24:26 -0500 Subject: drm/vmwgfx: Stop using surface dma commands on most configurations Initial version of guest backed objects in the host had some performance issues that made using surface-dma's instead of direct copies faster. Surface dma's force a migration to vram which at best is slow and at worst is impossible (e.g. on svga3 where there's not enough vram to migrate fb's to it). Slowly migrate away from surface dma's to direct copies by limiting their usage to systems with more than 32MB of vram. Signed-off-by: Zack Rusin Reviewed-by: Maaz Mombasawala Reviewed-by: Martin Krastev Link: https://patchwork.freedesktop.org/patch/msgid/20220302152426.885214-9-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 42b5ecb0c5e9..eb014b97d156 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -138,6 +138,11 @@ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); * Screen Target Display Unit CRTC Functions *****************************************************************************/ +static bool vmw_stdu_use_cpu_blit(const struct vmw_private *vmw) +{ + return !(vmw->capabilities & SVGA_CAP_3D) || vmw->vram_size < (32 * 1024 * 1024); +} + /** * vmw_stdu_crtc_destroy - cleans up the STDU @@ -689,7 +694,7 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, container_of(vfb, struct vmw_framebuffer_bo, base)->buffer; struct vmw_stdu_dirty ddirty; int ret; - bool cpu_blit = !(dev_priv->capabilities & SVGA_CAP_3D); + bool cpu_blit = vmw_stdu_use_cpu_blit(dev_priv); DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); /* @@ -1164,7 +1169,7 @@ vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, * so cache these mappings */ if (vps->content_fb_type == SEPARATE_BO && - !(dev_priv->capabilities & SVGA_CAP_3D)) + vmw_stdu_use_cpu_blit(dev_priv)) vps->cpp = new_fb->pitches[0] / new_fb->width; return 0; @@ -1368,7 +1373,7 @@ static int vmw_stdu_plane_update_bo(struct vmw_private *dev_priv, bo_update.base.vfb = vfb; bo_update.base.out_fence = out_fence; bo_update.base.mutex = NULL; - bo_update.base.cpu_blit = !(dev_priv->capabilities & SVGA_CAP_3D); + bo_update.base.cpu_blit = vmw_stdu_use_cpu_blit(dev_priv); bo_update.base.intr = false; /* -- cgit From 2d3eec8970335148278a6e350bc4840374f06471 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Feb 2022 12:03:42 +0200 Subject: drm: Add drm_mode_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a variant of drm_mode_copy() that explicitly clears out the list head of the destination mode. Helpful to guarantee we don't have stack garbage left in there for on-stack modes. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220218100403.7028-2-ville.syrjala@linux.intel.com Reviewed-by: Andrzej Hajda Reviewed-by: Harry Wentland --- drivers/gpu/drm/drm_modes.c | 17 +++++++++++++++++ include/drm/drm_modes.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3f819c7a021b..6e7e10f16ec0 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -941,6 +941,23 @@ void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode * } EXPORT_SYMBOL(drm_mode_copy); +/** + * drm_mode_init - initialize the mode from another mode + * @dst: mode to overwrite + * @src: mode to copy + * + * Copy an existing mode into another mode, zeroing the + * list head of the destination mode. Typically used + * to guarantee the list head is not left with stack + * garbage in on-stack modes. + */ +void drm_mode_init(struct drm_display_mode *dst, const struct drm_display_mode *src) +{ + memset(dst, 0, sizeof(*dst)); + drm_mode_copy(dst, src); +} +EXPORT_SYMBOL(drm_mode_init); + /** * drm_mode_duplicate - allocate and duplicate an existing mode * @dev: drm_device to allocate the duplicated mode for diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 2fa6b2c33b3f..a80ae9639e96 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -492,6 +492,8 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src); +void drm_mode_init(struct drm_display_mode *dst, + const struct drm_display_mode *src); struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, const struct drm_display_mode *mode); bool drm_mode_match(const struct drm_display_mode *mode1, -- cgit From d008bc331fb135ea1a5cddc3c4e283ba74596ad2 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Feb 2022 12:03:47 +0200 Subject: drm/bridge: Use drm_mode_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct drm_display_mode embeds a list head, so overwriting the full struct with another one will corrupt the list (if the destination mode is on a list). Use drm_mode_copy() instead which explicitly preserves the list head of the destination mode. Even if we know the destination mode is not on any list using drm_mode_copy() seems decent as it sets a good example. Bad examples of not using it might eventually get copied into code where preserving the list head actually matters. Obviously one case not covered here is when the mode itself is embedded in a larger structure and the whole structure is copied. But if we are careful when copying into modes embedded in structures I think we can be a little more reassured that bogus list heads haven't been propagated in. @is_mode_copy@ @@ drm_mode_copy(...) { ... } @depends on !is_mode_copy@ struct drm_display_mode *mode; expression E, S; @@ ( - *mode = E + drm_mode_copy(mode, &E) | - memcpy(mode, E, S) + drm_mode_copy(mode, E) ) @depends on !is_mode_copy@ struct drm_display_mode mode; expression E; @@ ( - mode = E + drm_mode_copy(&mode, &E) | - memcpy(&mode, E, S) + drm_mode_copy(&mode, E) ) @@ struct drm_display_mode *mode; @@ - &*mode + mode Cc: Andrzej Hajda Cc: Neil Armstrong Cc: Robert Foss Cc: Laurent Pinchart Cc: Jonas Karlman Cc: Jernej Skrabec Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220218100403.7028-7-ville.syrjala@linux.intel.com Reviewed-by: Andrzej Hajda Reviewed-by: Laurent Pinchart --- drivers/gpu/drm/bridge/nwl-dsi.c | 2 +- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 2 +- drivers/gpu/drm/bridge/tc358767.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index 14c763e1fa6f..e34fb09b90b9 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -855,7 +855,7 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, /* Save the new desired phy config */ memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg)); - memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); + drm_mode_copy(&dsi->mode, adjusted_mode); drm_mode_debug_printmodeline(adjusted_mode); if (pm_runtime_resume_and_get(dev) < 0) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 4befc104d220..a563460f8d20 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2830,7 +2830,7 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, mutex_lock(&hdmi->mutex); /* Store the display mode for plugin/DKMS poweron events */ - memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); + drm_mode_copy(&hdmi->previous_mode, mode); mutex_unlock(&hdmi->mutex); } diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index c23e0abc65e8..7f9574b17caa 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1312,7 +1312,7 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge, { struct tc_data *tc = bridge_to_tc(bridge); - tc->mode = *mode; + drm_mode_copy(&tc->mode, mode); } static struct edid *tc_get_edid(struct drm_bridge *bridge, -- cgit From 193c4df5b0dcbb7ea0efb99ca13bd05cd1adaddb Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Feb 2022 12:03:50 +0200 Subject: drm/imx: Use drm_mode_duplicate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hand rolled drm_mode_duplicate() with the real thing. @is_dup@ @@ drm_mode_duplicate(...) { ... } @depends on !is_dup@ expression dev, oldmode; identifier newmode; @@ - newmode = drm_mode_create(dev); + newmode = drm_mode_duplicate(dev, oldmode); ... - drm_mode_copy(newmode, oldmode); Cc: Philipp Zabel Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220218100403.7028-10-ville.syrjala@linux.intel.com Reviewed-by: Philipp Zabel --- drivers/gpu/drm/imx/imx-ldb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index e5078d03020d..c8cf819f39ce 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -150,10 +150,9 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) if (imx_ldb_ch->mode_valid) { struct drm_display_mode *mode; - mode = drm_mode_create(connector->dev); + mode = drm_mode_duplicate(connector->dev, &imx_ldb_ch->mode); if (!mode) return -EINVAL; - drm_mode_copy(mode, &imx_ldb_ch->mode); mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); num_modes++; -- cgit From ec5d4a723b57721ca0f3c1c86576412e32a50579 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Feb 2022 12:04:01 +0200 Subject: drm/panel: Use drm_mode_duplicate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hand rolled drm_mode_duplicate() with the real thing. @is_dup@ @@ drm_mode_duplicate(...) { ... } @depends on !is_dup@ expression dev, oldmode; identifier newmode; @@ - newmode = drm_mode_create(dev); + newmode = drm_mode_duplicate(dev, oldmode); ... - drm_mode_copy(newmode, oldmode); Signed-off-by: Ville Syrjälä Cc: Thierry Reding Cc: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220218100403.7028-21-ville.syrjala@linux.intel.com Acked-by: Sam Ravnborg --- drivers/gpu/drm/panel/panel-truly-nt35597.c | 3 +-- drivers/gpu/drm/panel/panel-visionox-rm69299.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-truly-nt35597.c b/drivers/gpu/drm/panel/panel-truly-nt35597.c index b24b92d93ea5..9ca5c7ff41d6 100644 --- a/drivers/gpu/drm/panel/panel-truly-nt35597.c +++ b/drivers/gpu/drm/panel/panel-truly-nt35597.c @@ -446,7 +446,7 @@ static int truly_nt35597_get_modes(struct drm_panel *panel, const struct nt35597_config *config; config = ctx->config; - mode = drm_mode_create(connector->dev); + mode = drm_mode_duplicate(connector->dev, config->dm); if (!mode) { dev_err(ctx->dev, "failed to create a new display mode\n"); return 0; @@ -454,7 +454,6 @@ static int truly_nt35597_get_modes(struct drm_panel *panel, connector->display_info.width_mm = config->width_mm; connector->display_info.height_mm = config->height_mm; - drm_mode_copy(mode, config->dm); mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index eb43503ec97b..db2443ac81d3 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -168,7 +168,8 @@ static int visionox_rm69299_get_modes(struct drm_panel *panel, struct visionox_rm69299 *ctx = panel_to_ctx(panel); struct drm_display_mode *mode; - mode = drm_mode_create(connector->dev); + mode = drm_mode_duplicate(connector->dev, + &visionox_rm69299_1080x2248_60hz); if (!mode) { dev_err(ctx->panel.dev, "failed to create a new display mode\n"); return 0; @@ -176,7 +177,6 @@ static int visionox_rm69299_get_modes(struct drm_panel *panel, connector->display_info.width_mm = 74; connector->display_info.height_mm = 131; - drm_mode_copy(mode, &visionox_rm69299_1080x2248_60hz); mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); -- cgit From a4f24ada636dc0262621213988f0d15beb5596bb Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 16 Mar 2022 10:46:48 +0300 Subject: drm/blend: fix typo in the comment The documentation for drm_rotation_simplify() uses DRM_MODE_ROTATE_X, while it's clear the comment should mention DRM_MODE_REFLECT_X instead. The example passes all flags except the DRM_MODE_REFLECT_X as supported and expects to eliminate this flag. Fixes: c2c446ad2943 ("drm: Add DRM_MODE_ROTATE_ and DRM_MODE_REFLECT_ to UAPI") Signed-off-by: Dmitry Baryshkov Reviewed-by: Simon Ser Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220316074648.7009-1-dmitry.baryshkov@linaro.org --- drivers/gpu/drm/drm_blend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index ec37cbfabb50..46ee5d5df6b4 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -317,7 +317,7 @@ EXPORT_SYMBOL(drm_plane_create_rotation_property); * DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_180 | * DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_Y); * - * to eliminate the DRM_MODE_ROTATE_X flag. Depending on what kind of + * to eliminate the DRM_MODE_REFLECT_X flag. Depending on what kind of * transforms the hardware supports, this function may not * be able to produce a supported transform, so the caller should * check the result afterwards. -- cgit From 47042e0ddd218f100292cebc5208cb1eff7473b6 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 12 Mar 2022 14:34:37 +0800 Subject: drm/solomon: Make DRM_SSD130X depends on MMU WARNING: unmet direct dependencies detected for DRM_GEM_SHMEM_HELPER Depends on [n]: HAS_IOMEM [=y] && DRM [=m] && MMU [=n] Selected by [m]: - DRM_SSD130X [=m] && HAS_IOMEM [=y] && DRM [=m] DRM_GEM_SHMEM_HELPER depends on MMU, DRM_SSD130X should also depends on MMU. Fixes: a61732e80867 ("drm: Add driver for Solomon SSD130x OLED displays") Signed-off-by: YueHaibing Acked-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220312063437.19160-1-yuehaibing@huawei.com --- drivers/gpu/drm/solomon/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/solomon/Kconfig b/drivers/gpu/drm/solomon/Kconfig index 5861c3ab7c45..6230369505c9 100644 --- a/drivers/gpu/drm/solomon/Kconfig +++ b/drivers/gpu/drm/solomon/Kconfig @@ -1,6 +1,6 @@ config DRM_SSD130X tristate "DRM support for Solomon SSD130x OLED displays" - depends on DRM + depends on DRM && MMU select BACKLIGHT_CLASS_DEVICE select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER -- cgit From de2d1822f9489268e0707b2d188dd789d5effd08 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:11 +0100 Subject: drm/gma500: Remove struct psb_gem_object.npage Calculate the number of pages in the BO's backing storage from the size. Remove the npage field. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-2-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gem.c | 9 +++++---- drivers/gpu/drm/gma500/gem.h | 1 - drivers/gpu/drm/gma500/gma_display.c | 8 +++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 8d65af80bb08..fe7d242567c0 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -51,7 +51,6 @@ int psb_gem_pin(struct psb_gem_object *pobj) (gpu_base + pobj->offset), npages, 0, 0, PSB_MMU_CACHED_MEMORY); - pobj->npage = npages; pobj->pages = pages; out: @@ -71,6 +70,7 @@ void psb_gem_unpin(struct psb_gem_object *pobj) struct drm_device *dev = obj->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 gpu_base = dev_priv->gtt.gatt_start; + unsigned long npages; mutex_lock(&dev_priv->gtt_mutex); @@ -81,16 +81,17 @@ void psb_gem_unpin(struct psb_gem_object *pobj) if (pobj->in_gart || pobj->stolen) goto out; + npages = obj->size / PAGE_SIZE; + psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu), - (gpu_base + pobj->offset), pobj->npage, 0, 0); + (gpu_base + pobj->offset), npages, 0, 0); psb_gtt_remove_pages(dev_priv, &pobj->resource); /* Reset caching flags */ - set_pages_array_wb(pobj->pages, pobj->npage); + set_pages_array_wb(pobj->pages, npages); drm_gem_put_pages(obj, pobj->pages, true, false); pobj->pages = NULL; - pobj->npage = 0; out: mutex_unlock(&dev_priv->gtt_mutex); diff --git a/drivers/gpu/drm/gma500/gem.h b/drivers/gpu/drm/gma500/gem.h index 79cced40c87f..96ef864b4bf1 100644 --- a/drivers/gpu/drm/gma500/gem.h +++ b/drivers/gpu/drm/gma500/gem.h @@ -23,7 +23,6 @@ struct psb_gem_object { bool stolen; /* Backed from stolen RAM */ bool mmapping; /* Is mmappable */ struct page **pages; /* Backing pages if present */ - int npage; /* Number of backing pages */ }; static inline struct psb_gem_object *to_psb_gem_object(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 60ba7de59139..dd801404cf99 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -391,11 +391,9 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc, goto unref_cursor; } - /* Prevent overflow */ - if (pobj->npage > 4) - cursor_pages = 4; - else - cursor_pages = pobj->npage; + cursor_pages = obj->size / PAGE_SIZE; + if (cursor_pages > 4) + cursor_pages = 4; /* Prevent overflow */ /* Copy the cursor to cursor mem */ tmp_dst = dev_priv->vram_addr + cursor_pobj->offset; -- cgit From 16dad99de8f2abfcec9836e2031613a713e339d2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:12 +0100 Subject: drm/gma500: Acquire reservation lock for GEM objects Protect concurrent access to struct psb_gem_object by acquiring the GEM object's reservation lock; as it's supposed to be. The use of the GTT mutex can now be moved into GTT code. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-3-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index fe7d242567c0..026ce11f7460 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -31,6 +31,10 @@ int psb_gem_pin(struct psb_gem_object *pobj) unsigned int npages; int ret; + ret = dma_resv_lock(obj->resv, NULL); + if (drm_WARN_ONCE(dev, ret, "dma_resv_lock() failed, ret=%d\n", ret)) + return ret; + mutex_lock(&dev_priv->gtt_mutex); if (pobj->in_gart || pobj->stolen) @@ -56,11 +60,13 @@ int psb_gem_pin(struct psb_gem_object *pobj) out: ++pobj->in_gart; mutex_unlock(&dev_priv->gtt_mutex); + dma_resv_unlock(obj->resv); return 0; err_mutex_unlock: mutex_unlock(&dev_priv->gtt_mutex); + dma_resv_unlock(obj->resv); return ret; } @@ -71,6 +77,11 @@ void psb_gem_unpin(struct psb_gem_object *pobj) struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 gpu_base = dev_priv->gtt.gatt_start; unsigned long npages; + int ret; + + ret = dma_resv_lock(obj->resv, NULL); + if (drm_WARN_ONCE(dev, ret, "dma_resv_lock() failed, ret=%d\n", ret)) + return; mutex_lock(&dev_priv->gtt_mutex); @@ -95,6 +106,7 @@ void psb_gem_unpin(struct psb_gem_object *pobj) out: mutex_unlock(&dev_priv->gtt_mutex); + dma_resv_unlock(obj->resv); } static vm_fault_t psb_gem_fault(struct vm_fault *vmf); -- cgit From 14e92dd104cde77db5612b111baf2f41f1eba127 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:13 +0100 Subject: drm/gma500: Move GTT locking into GTT helpers Acquire the GTT mutex in psb_gtt_{insert,remove}_pages(). Remove locking from callers. Also remove the GTT locking around the resume code. Resume does not run concurrently with other GTT operations. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-4-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gem.c | 11 ++--------- drivers/gpu/drm/gma500/gtt.c | 24 +++++++++++------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 026ce11f7460..2f44535f58e7 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -35,15 +35,13 @@ int psb_gem_pin(struct psb_gem_object *pobj) if (drm_WARN_ONCE(dev, ret, "dma_resv_lock() failed, ret=%d\n", ret)) return ret; - mutex_lock(&dev_priv->gtt_mutex); - if (pobj->in_gart || pobj->stolen) goto out; /* already mapped */ pages = drm_gem_get_pages(obj); if (IS_ERR(pages)) { ret = PTR_ERR(pages); - goto err_mutex_unlock; + goto err_dma_resv_unlock; } npages = obj->size / PAGE_SIZE; @@ -59,13 +57,11 @@ int psb_gem_pin(struct psb_gem_object *pobj) out: ++pobj->in_gart; - mutex_unlock(&dev_priv->gtt_mutex); dma_resv_unlock(obj->resv); return 0; -err_mutex_unlock: - mutex_unlock(&dev_priv->gtt_mutex); +err_dma_resv_unlock: dma_resv_unlock(obj->resv); return ret; } @@ -83,8 +79,6 @@ void psb_gem_unpin(struct psb_gem_object *pobj) if (drm_WARN_ONCE(dev, ret, "dma_resv_lock() failed, ret=%d\n", ret)) return; - mutex_lock(&dev_priv->gtt_mutex); - WARN_ON(!pobj->in_gart); --pobj->in_gart; @@ -105,7 +99,6 @@ void psb_gem_unpin(struct psb_gem_object *pobj) pobj->pages = NULL; out: - mutex_unlock(&dev_priv->gtt_mutex); dma_resv_unlock(obj->resv); } diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 309ffe921bfd..4202e88e5f84 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -74,11 +74,7 @@ static u32 __iomem *psb_gtt_entry(struct drm_psb_private *pdev, const struct res return pdev->gtt_map + (offset >> PAGE_SHIFT); } -/* - * Take our preallocated GTT range and insert the GEM object into - * the GTT. This is protected via the gtt mutex which the caller - * must hold. - */ +/* Acquires GTT mutex internally. */ void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *res, struct page **pages) { @@ -86,6 +82,8 @@ void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *r u32 __iomem *gtt_slot; u32 pte; + mutex_lock(&pdev->gtt_mutex); + /* Write our page entries into the GTT itself */ npages = resource_size(res) >> PAGE_SHIFT; @@ -98,19 +96,19 @@ void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *r /* Make sure all the entries are set before we return */ ioread32(gtt_slot - 1); + + mutex_unlock(&pdev->gtt_mutex); } -/* - * Remove a preallocated GTT range from the GTT. Overwrite all the - * page table entries with the dummy page. This is protected via the gtt - * mutex which the caller must hold. - */ +/* Acquires GTT mutex internally. */ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *res) { resource_size_t npages, i; u32 __iomem *gtt_slot; u32 pte; + mutex_lock(&pdev->gtt_mutex); + /* Install scratch page for the resource */ pte = psb_gtt_mask_pte(page_to_pfn(pdev->scratch_page), PSB_MMU_CACHED_MEMORY); @@ -123,6 +121,8 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r /* Make sure all the entries are set before we return */ ioread32(gtt_slot - 1); + + mutex_unlock(&pdev->gtt_mutex); } static void psb_gtt_alloc(struct drm_device *dev) @@ -306,8 +306,6 @@ int psb_gtt_restore(struct drm_device *dev) struct psb_gem_object *pobj; unsigned int restored = 0, total = 0, size = 0; - /* On resume, the gtt_mutex is already initialized */ - mutex_lock(&dev_priv->gtt_mutex); psb_gtt_init(dev, 1); while (r != NULL) { @@ -325,7 +323,7 @@ int psb_gtt_restore(struct drm_device *dev) r = r->sibling; total++; } - mutex_unlock(&dev_priv->gtt_mutex); + DRM_DEBUG_DRIVER("Restored %u of %u gtt ranges (%u KB)", restored, total, (size / 1024)); -- cgit From e160b70ba1604b0c88f97f7f8756d126515ed039 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:14 +0100 Subject: drm/gma500: Remove struct psb_gtt.sem sempahore The semaphore at struct psb_mmu_driver.sem protects access to the MMU fields. Additional locking with struct psb_gtt.sem is unnecessary. Remove the field and related code. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-5-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 7 ------- drivers/gpu/drm/gma500/gtt.h | 1 - drivers/gpu/drm/gma500/psb_drv.c | 4 ---- 3 files changed, 12 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 4202e88e5f84..c7b7cb1f2d13 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -125,12 +125,6 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r mutex_unlock(&pdev->gtt_mutex); } -static void psb_gtt_alloc(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - init_rwsem(&dev_priv->gtt.sem); -} - void psb_gtt_takedown(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -166,7 +160,6 @@ int psb_gtt_init(struct drm_device *dev, int resume) if (!resume) { mutex_init(&dev_priv->gtt_mutex); mutex_init(&dev_priv->mmap_mutex); - psb_gtt_alloc(dev); } pg = &dev_priv->gtt; diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index ff1dcdd1ff52..31500533ac45 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -22,7 +22,6 @@ struct psb_gtt { unsigned gatt_pages; unsigned long stolen_size; unsigned long vram_stolen_size; - struct rw_semaphore sem; }; /* Exported functions */ diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index eeb681be9c95..7227a8e44d23 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -184,13 +184,11 @@ static void psb_driver_unload(struct drm_device *dev) if (dev_priv->mmu) { struct psb_gtt *pg = &dev_priv->gtt; - down_read(&pg->sem); psb_mmu_remove_pfn_sequence( psb_mmu_get_default_pd (dev_priv->mmu), pg->mmu_gatt_start, dev_priv->vram_stolen_size >> PAGE_SHIFT); - up_read(&pg->sem); psb_mmu_driver_takedown(dev_priv->mmu); dev_priv->mmu = NULL; } @@ -345,12 +343,10 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) return ret; /* Add stolen memory to SGX MMU */ - down_read(&pg->sem); ret = psb_mmu_insert_pfn_sequence(psb_mmu_get_default_pd(dev_priv->mmu), dev_priv->stolen_base >> PAGE_SHIFT, pg->gatt_start, pg->stolen_size >> PAGE_SHIFT, 0); - up_read(&pg->sem); psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); psb_mmu_set_pd_context(dev_priv->pf_pd, 1); -- cgit From 60a78f9ea0e6940cf14422eadfd537292e4562cf Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:15 +0100 Subject: drm/gma500: Move GTT setup and restoration into helper funtions The GTT init and restore functions contain logic to populate the GTT entries. Move the code into helper functions. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-6-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 115 +++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index c7b7cb1f2d13..acd50ee26b03 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -144,18 +144,79 @@ void psb_gtt_takedown(struct drm_device *dev) iounmap(dev_priv->gtt_map); } +/* Clear GTT. Use a scratch page to avoid accidents or scribbles. */ +static void psb_gtt_clear(struct drm_psb_private *pdev) +{ + resource_size_t pfn_base; + unsigned long i; + uint32_t pte; + + pfn_base = page_to_pfn(pdev->scratch_page); + pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY); + + for (i = 0; i < pdev->gtt.gtt_pages; ++i) + iowrite32(pte, pdev->gtt_map + i); + + (void)ioread32(pdev->gtt_map + i - 1); +} + +/* Insert vram stolen pages into the GTT. */ +static void psb_gtt_populate_stolen(struct drm_psb_private *pdev) +{ + struct drm_device *dev = &pdev->dev; + unsigned int pfn_base; + unsigned int i, num_pages; + uint32_t pte; + + pfn_base = pdev->stolen_base >> PAGE_SHIFT; + num_pages = pdev->vram_stolen_size >> PAGE_SHIFT; + + drm_dbg(dev, "Set up %u stolen pages starting at 0x%08x, GTT offset %dK\n", + num_pages, pfn_base << PAGE_SHIFT, 0); + + for (i = 0; i < num_pages; ++i) { + pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY); + iowrite32(pte, pdev->gtt_map + i); + } + + (void)ioread32(pdev->gtt_map + i - 1); +} + +/* Re-insert all pinned GEM objects into GTT. */ +static void psb_gtt_populate_resources(struct drm_psb_private *pdev) +{ + unsigned int restored = 0, total = 0, size = 0; + struct resource *r = pdev->gtt_mem->child; + struct drm_device *dev = &pdev->dev; + struct psb_gem_object *pobj; + + while (r) { + /* + * TODO: GTT restoration needs a refactoring, so that we don't have to touch + * struct psb_gem_object here. The type represents a GEM object and is + * not related to the GTT itself. + */ + pobj = container_of(r, struct psb_gem_object, resource); + if (pobj->pages) { + psb_gtt_insert_pages(pdev, &pobj->resource, pobj->pages); + size += resource_size(&pobj->resource); + ++restored; + } + r = r->sibling; + ++total; + } + + drm_dbg(dev, "Restored %u of %u gtt ranges (%u KB)", restored, total, (size / 1024)); +} + int psb_gtt_init(struct drm_device *dev, int resume) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); unsigned gtt_pages; unsigned long stolen_size, vram_stolen_size; - unsigned i, num_pages; - unsigned pfn_base; struct psb_gtt *pg; - int ret = 0; - uint32_t pte; if (!resume) { mutex_init(&dev_priv->gtt_mutex); @@ -262,29 +323,9 @@ int psb_gtt_init(struct drm_device *dev, int resume) goto out_err; } - /* - * Insert vram stolen pages into the GTT - */ - - pfn_base = dev_priv->stolen_base >> PAGE_SHIFT; - num_pages = vram_stolen_size >> PAGE_SHIFT; - dev_dbg(dev->dev, "Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", - num_pages, pfn_base << PAGE_SHIFT, 0); - for (i = 0; i < num_pages; ++i) { - pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY); - iowrite32(pte, dev_priv->gtt_map + i); - } - - /* - * Init rest of GTT to the scratch page to avoid accidents or scribbles - */ - - pfn_base = page_to_pfn(dev_priv->scratch_page); - pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY); - for (; i < gtt_pages; ++i) - iowrite32(pte, dev_priv->gtt_map + i); + psb_gtt_clear(dev_priv); + psb_gtt_populate_stolen(dev_priv); - (void) ioread32(dev_priv->gtt_map + i - 1); return 0; out_err: @@ -295,30 +336,10 @@ out_err: int psb_gtt_restore(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct resource *r = dev_priv->gtt_mem->child; - struct psb_gem_object *pobj; - unsigned int restored = 0, total = 0, size = 0; psb_gtt_init(dev, 1); - while (r != NULL) { - /* - * TODO: GTT restoration needs a refactoring, so that we don't have to touch - * struct psb_gem_object here. The type represents a GEM object and is - * not related to the GTT itself. - */ - pobj = container_of(r, struct psb_gem_object, resource); - if (pobj->pages) { - psb_gtt_insert_pages(dev_priv, &pobj->resource, pobj->pages); - size += pobj->resource.end - pobj->resource.start; - restored++; - } - r = r->sibling; - total++; - } - - DRM_DEBUG_DRIVER("Restored %u of %u gtt ranges (%u KB)", restored, - total, (size / 1024)); + psb_gtt_populate_resources(dev_priv); return 0; } -- cgit From 97bd66c46d8bfbc02406d848ebb53c6a2992e03f Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:16 +0100 Subject: drm/gma500: Move GTT resume logic out of psb_gtt_init() The current implementation of psb_gtt_init() also does resume handling. Move the resume code into its own helper. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-7-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 122 ++++++++++++++++++++++++++++++++------- drivers/gpu/drm/gma500/gtt.h | 2 +- drivers/gpu/drm/gma500/psb_drv.c | 2 +- 3 files changed, 104 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index acd50ee26b03..43ad3ec38c80 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -209,7 +209,7 @@ static void psb_gtt_populate_resources(struct drm_psb_private *pdev) drm_dbg(dev, "Restored %u of %u gtt ranges (%u KB)", restored, total, (size / 1024)); } -int psb_gtt_init(struct drm_device *dev, int resume) +int psb_gtt_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); @@ -218,10 +218,8 @@ int psb_gtt_init(struct drm_device *dev, int resume) struct psb_gtt *pg; int ret = 0; - if (!resume) { - mutex_init(&dev_priv->gtt_mutex); - mutex_init(&dev_priv->mmap_mutex); - } + mutex_init(&dev_priv->gtt_mutex); + mutex_init(&dev_priv->mmap_mutex); pg = &dev_priv->gtt; @@ -290,13 +288,6 @@ int psb_gtt_init(struct drm_device *dev, int resume) dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", dev_priv->stolen_base, vram_stolen_size / 1024); - if (resume && (gtt_pages != pg->gtt_pages) && - (stolen_size != pg->stolen_size)) { - dev_err(dev->dev, "GTT resume error.\n"); - ret = -EINVAL; - goto out_err; - } - pg->gtt_pages = gtt_pages; pg->stolen_size = stolen_size; dev_priv->vram_stolen_size = vram_stolen_size; @@ -304,19 +295,14 @@ int psb_gtt_init(struct drm_device *dev, int resume) /* * Map the GTT and the stolen memory area */ - if (!resume) - dev_priv->gtt_map = ioremap(pg->gtt_phys_start, - gtt_pages << PAGE_SHIFT); + dev_priv->gtt_map = ioremap(pg->gtt_phys_start, gtt_pages << PAGE_SHIFT); if (!dev_priv->gtt_map) { dev_err(dev->dev, "Failure to map gtt.\n"); ret = -ENOMEM; goto out_err; } - if (!resume) - dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, - stolen_size); - + dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); if (!dev_priv->vram_addr) { dev_err(dev->dev, "Failure to map stolen base.\n"); ret = -ENOMEM; @@ -333,11 +319,107 @@ out_err: return ret; } +static int psb_gtt_resume(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned int gtt_pages; + unsigned long stolen_size, vram_stolen_size; + struct psb_gtt *pg; + int ret = 0; + + pg = &dev_priv->gtt; + + /* Enable the GTT */ + pci_read_config_word(pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); + pci_write_config_word(pdev, PSB_GMCH_CTRL, + dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); + + dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); + PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); + (void) PSB_RVDC32(PSB_PGETBL_CTL); + + /* The root resource we allocate address space from */ + dev_priv->gtt_initialized = 1; + + pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; + + /* + * The video mmu has a hw bug when accessing 0x0D0000000. + * Make gatt start at 0x0e000,0000. This doesn't actually + * matter for us but may do if the video acceleration ever + * gets opened up. + */ + pg->mmu_gatt_start = 0xE0000000; + + pg->gtt_start = pci_resource_start(pdev, PSB_GTT_RESOURCE); + gtt_pages = pci_resource_len(pdev, PSB_GTT_RESOURCE) >> PAGE_SHIFT; + /* CDV doesn't report this. In which case the system has 64 gtt pages */ + if (pg->gtt_start == 0 || gtt_pages == 0) { + dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n"); + gtt_pages = 64; + pg->gtt_start = dev_priv->pge_ctl; + } + + pg->gatt_start = pci_resource_start(pdev, PSB_GATT_RESOURCE); + pg->gatt_pages = pci_resource_len(pdev, PSB_GATT_RESOURCE) >> PAGE_SHIFT; + dev_priv->gtt_mem = &pdev->resource[PSB_GATT_RESOURCE]; + + if (pg->gatt_pages == 0 || pg->gatt_start == 0) { + static struct resource fudge; /* Preferably peppermint */ + /* + * This can occur on CDV systems. Fudge it in this case. We + * really don't care what imaginary space is being allocated + * at this point. + */ + dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n"); + pg->gatt_start = 0x40000000; + pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; + /* + * This is a little confusing but in fact the GTT is providing + * a view from the GPU into memory and not vice versa. As such + * this is really allocating space that is not the same as the + * CPU address space on CDV. + */ + fudge.start = 0x40000000; + fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; + fudge.name = "fudge"; + fudge.flags = IORESOURCE_MEM; + dev_priv->gtt_mem = &fudge; + } + + pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); + vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; + stolen_size = vram_stolen_size; + + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", + dev_priv->stolen_base, vram_stolen_size / 1024); + + if ((gtt_pages != pg->gtt_pages) && (stolen_size != pg->stolen_size)) { + dev_err(dev->dev, "GTT resume error.\n"); + ret = -EINVAL; + goto out_err; + } + + pg->gtt_pages = gtt_pages; + pg->stolen_size = stolen_size; + dev_priv->vram_stolen_size = vram_stolen_size; + + psb_gtt_clear(dev_priv); + psb_gtt_populate_stolen(dev_priv); + + return 0; + +out_err: + psb_gtt_takedown(dev); + return ret; +} + int psb_gtt_restore(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - psb_gtt_init(dev, 1); + psb_gtt_resume(dev); psb_gtt_populate_resources(dev_priv); diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index 31500533ac45..cb270ea40794 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -25,7 +25,7 @@ struct psb_gtt { }; /* Exported functions */ -extern int psb_gtt_init(struct drm_device *dev, int resume); +int psb_gtt_init(struct drm_device *dev); extern void psb_gtt_takedown(struct drm_device *dev); extern int psb_gtt_restore(struct drm_device *dev); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 7227a8e44d23..2891a3dc8d2e 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -324,7 +324,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) set_pages_uc(dev_priv->scratch_page, 1); - ret = psb_gtt_init(dev, 0); + ret = psb_gtt_init(dev); if (ret) goto out_err; -- cgit From 6069fd815d287e786a5ac1b0f875fa92aac798ff Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:17 +0100 Subject: drm/gma500: Cleanup GTT uninit and error handling Replace psb_gtt_takedown() with finalizer function that is only called for unloading the driver. Use roll-back pattern for error handling in psb_gtt_init() and _resume(). Also fixes a bug where vmap_addr was never unmapped. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-8-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 49 ++++++++++++++++++++-------------------- drivers/gpu/drm/gma500/gtt.h | 2 +- drivers/gpu/drm/gma500/psb_drv.c | 2 +- drivers/gpu/drm/gma500/psb_drv.h | 1 - 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 43ad3ec38c80..99c644a5c5cb 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -125,23 +125,20 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r mutex_unlock(&pdev->gtt_mutex); } -void psb_gtt_takedown(struct drm_device *dev) +void psb_gtt_fini(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); - if (dev_priv->gtt_map) { - iounmap(dev_priv->gtt_map); - dev_priv->gtt_map = NULL; - } - if (dev_priv->gtt_initialized) { - pci_write_config_word(pdev, PSB_GMCH_CTRL, - dev_priv->gmch_ctrl); - PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); - (void) PSB_RVDC32(PSB_PGETBL_CTL); - } - if (dev_priv->vram_addr) - iounmap(dev_priv->gtt_map); + iounmap(dev_priv->vram_addr); + iounmap(dev_priv->gtt_map); + + pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); + PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); + (void)PSB_RVDC32(PSB_PGETBL_CTL); + + mutex_destroy(&dev_priv->mmap_mutex); + mutex_destroy(&dev_priv->gtt_mutex); } /* Clear GTT. Use a scratch page to avoid accidents or scribbles. */ @@ -233,8 +230,6 @@ int psb_gtt_init(struct drm_device *dev) (void) PSB_RVDC32(PSB_PGETBL_CTL); /* The root resource we allocate address space from */ - dev_priv->gtt_initialized = 1; - pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; /* @@ -299,14 +294,14 @@ int psb_gtt_init(struct drm_device *dev) if (!dev_priv->gtt_map) { dev_err(dev->dev, "Failure to map gtt.\n"); ret = -ENOMEM; - goto out_err; + goto err_gtt_disable; } dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); if (!dev_priv->vram_addr) { dev_err(dev->dev, "Failure to map stolen base.\n"); ret = -ENOMEM; - goto out_err; + goto err_iounmap; } psb_gtt_clear(dev_priv); @@ -314,8 +309,14 @@ int psb_gtt_init(struct drm_device *dev) return 0; -out_err: - psb_gtt_takedown(dev); +err_iounmap: + iounmap(dev_priv->gtt_map); +err_gtt_disable: + pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); + PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); + (void)PSB_RVDC32(PSB_PGETBL_CTL); + mutex_destroy(&dev_priv->mmap_mutex); + mutex_destroy(&dev_priv->gtt_mutex); return ret; } @@ -340,8 +341,6 @@ static int psb_gtt_resume(struct drm_device *dev) (void) PSB_RVDC32(PSB_PGETBL_CTL); /* The root resource we allocate address space from */ - dev_priv->gtt_initialized = 1; - pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; /* @@ -398,7 +397,7 @@ static int psb_gtt_resume(struct drm_device *dev) if ((gtt_pages != pg->gtt_pages) && (stolen_size != pg->stolen_size)) { dev_err(dev->dev, "GTT resume error.\n"); ret = -EINVAL; - goto out_err; + goto err_gtt_disable; } pg->gtt_pages = gtt_pages; @@ -410,8 +409,10 @@ static int psb_gtt_resume(struct drm_device *dev) return 0; -out_err: - psb_gtt_takedown(dev); +err_gtt_disable: + pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); + PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); + (void)PSB_RVDC32(PSB_PGETBL_CTL); return ret; } diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index cb270ea40794..5839d5d06adc 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -26,7 +26,7 @@ struct psb_gtt { /* Exported functions */ int psb_gtt_init(struct drm_device *dev); -extern void psb_gtt_takedown(struct drm_device *dev); +void psb_gtt_fini(struct drm_device *dev); extern int psb_gtt_restore(struct drm_device *dev); int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res, diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 2891a3dc8d2e..41be6e1ac7f9 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -192,7 +192,7 @@ static void psb_driver_unload(struct drm_device *dev) psb_mmu_driver_takedown(dev_priv->mmu); dev_priv->mmu = NULL; } - psb_gtt_takedown(dev); + psb_gtt_fini(dev); if (dev_priv->scratch_page) { set_pages_wb(dev_priv->scratch_page, 1); __free_page(dev_priv->scratch_page); diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 0439b10d3db5..553d03190ce1 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -408,7 +408,6 @@ struct drm_psb_private { uint32_t stolen_base; u8 __iomem *vram_addr; unsigned long vram_stolen_size; - int gtt_initialized; u16 gmch_ctrl; /* Saved GTT setup */ u32 pge_ctl; -- cgit From d00f44ddfbb18d73fab98492e5fcb43281564f4f Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:18 +0100 Subject: drm/gma500: Split GTT init/resume/fini into GTT and GEM functions The GTT init, fini and resume functions contain both, GTT and GEM, code. Split each into a separate GTT and a GEM function. The GEM code is responsible for mmap_mutex and the stolen memory area. The rest of the functionality is left in GTT functions. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-9-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 118 +++++++++++++++++++++++++-------------- drivers/gpu/drm/gma500/gtt.h | 3 + drivers/gpu/drm/gma500/psb_drv.c | 4 ++ 3 files changed, 83 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 99c644a5c5cb..0e99774c7e59 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -125,19 +125,26 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r mutex_unlock(&pdev->gtt_mutex); } +void psb_gem_mm_fini(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + + iounmap(dev_priv->vram_addr); + + mutex_destroy(&dev_priv->mmap_mutex); +} + void psb_gtt_fini(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); - iounmap(dev_priv->vram_addr); iounmap(dev_priv->gtt_map); pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); (void)PSB_RVDC32(PSB_PGETBL_CTL); - mutex_destroy(&dev_priv->mmap_mutex); mutex_destroy(&dev_priv->gtt_mutex); } @@ -211,12 +218,10 @@ int psb_gtt_init(struct drm_device *dev) struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); unsigned gtt_pages; - unsigned long stolen_size, vram_stolen_size; struct psb_gtt *pg; int ret = 0; mutex_init(&dev_priv->gtt_mutex); - mutex_init(&dev_priv->mmap_mutex); pg = &dev_priv->gtt; @@ -274,22 +279,8 @@ int psb_gtt_init(struct drm_device *dev) dev_priv->gtt_mem = &fudge; } - pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); - vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - - PAGE_SIZE; - - stolen_size = vram_stolen_size; - - dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", - dev_priv->stolen_base, vram_stolen_size / 1024); - pg->gtt_pages = gtt_pages; - pg->stolen_size = stolen_size; - dev_priv->vram_stolen_size = vram_stolen_size; - /* - * Map the GTT and the stolen memory area - */ dev_priv->gtt_map = ioremap(pg->gtt_phys_start, gtt_pages << PAGE_SHIFT); if (!dev_priv->gtt_map) { dev_err(dev->dev, "Failure to map gtt.\n"); @@ -297,26 +288,54 @@ int psb_gtt_init(struct drm_device *dev) goto err_gtt_disable; } + psb_gtt_clear(dev_priv); + + return 0; + +err_gtt_disable: + pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); + PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); + (void)PSB_RVDC32(PSB_PGETBL_CTL); + mutex_destroy(&dev_priv->gtt_mutex); + return ret; +} + +int psb_gem_mm_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned long stolen_size, vram_stolen_size; + struct psb_gtt *pg; + int ret; + + mutex_init(&dev_priv->mmap_mutex); + + pg = &dev_priv->gtt; + + pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); + vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; + + stolen_size = vram_stolen_size; + + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", + dev_priv->stolen_base, vram_stolen_size / 1024); + + pg->stolen_size = stolen_size; + dev_priv->vram_stolen_size = vram_stolen_size; + dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); if (!dev_priv->vram_addr) { dev_err(dev->dev, "Failure to map stolen base.\n"); ret = -ENOMEM; - goto err_iounmap; + goto err_mutex_destroy; } - psb_gtt_clear(dev_priv); psb_gtt_populate_stolen(dev_priv); return 0; -err_iounmap: - iounmap(dev_priv->gtt_map); -err_gtt_disable: - pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); - PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); - (void)PSB_RVDC32(PSB_PGETBL_CTL); +err_mutex_destroy: mutex_destroy(&dev_priv->mmap_mutex); - mutex_destroy(&dev_priv->gtt_mutex); return ret; } @@ -325,9 +344,8 @@ static int psb_gtt_resume(struct drm_device *dev) struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); unsigned int gtt_pages; - unsigned long stolen_size, vram_stolen_size; struct psb_gtt *pg; - int ret = 0; + int ret; pg = &dev_priv->gtt; @@ -387,27 +405,15 @@ static int psb_gtt_resume(struct drm_device *dev) dev_priv->gtt_mem = &fudge; } - pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); - vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; - stolen_size = vram_stolen_size; - - dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", - dev_priv->stolen_base, vram_stolen_size / 1024); - - if ((gtt_pages != pg->gtt_pages) && (stolen_size != pg->stolen_size)) { + if (gtt_pages != pg->gtt_pages) { dev_err(dev->dev, "GTT resume error.\n"); ret = -EINVAL; goto err_gtt_disable; } pg->gtt_pages = gtt_pages; - pg->stolen_size = stolen_size; - dev_priv->vram_stolen_size = vram_stolen_size; psb_gtt_clear(dev_priv); - psb_gtt_populate_stolen(dev_priv); - - return 0; err_gtt_disable: pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); @@ -416,11 +422,39 @@ err_gtt_disable: return ret; } +static int psb_gem_mm_resume(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned long stolen_size, vram_stolen_size; + struct psb_gtt *pg; + + pg = &dev_priv->gtt; + + pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); + vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; + + stolen_size = vram_stolen_size; + + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", dev_priv->stolen_base, + vram_stolen_size / 1024); + + if (stolen_size != pg->stolen_size) { + dev_err(dev->dev, "GTT resume error.\n"); + return -EINVAL; + } + + psb_gtt_populate_stolen(dev_priv); + + return 0; +} + int psb_gtt_restore(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); psb_gtt_resume(dev); + psb_gem_mm_resume(dev); psb_gtt_populate_resources(dev_priv); diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index 5839d5d06adc..45e1926dfce1 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -37,4 +37,7 @@ void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *r struct page **pages); void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *res); +int psb_gem_mm_init(struct drm_device *dev); +void psb_gem_mm_fini(struct drm_device *dev); + #endif diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 41be6e1ac7f9..6547967b51e1 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -192,6 +192,7 @@ static void psb_driver_unload(struct drm_device *dev) psb_mmu_driver_takedown(dev_priv->mmu); dev_priv->mmu = NULL; } + psb_gem_mm_fini(dev); psb_gtt_fini(dev); if (dev_priv->scratch_page) { set_pages_wb(dev_priv->scratch_page, 1); @@ -325,6 +326,9 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) set_pages_uc(dev_priv->scratch_page, 1); ret = psb_gtt_init(dev); + if (ret) + goto out_err; + ret = psb_gem_mm_init(dev); if (ret) goto out_err; -- cgit From 42ceddb696a01e6e31e90c456b882392f1572d69 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:19 +0100 Subject: drm/gma500: Inline psb_gtt_restore() Inline psb_gtt_restore() into its only caller in power.c. Perform the GTT restoration in psb_gem_mm_resume(). The restoration step is part of GEM anyway and will be moved over at some point. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-10-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 15 ++------------- drivers/gpu/drm/gma500/gtt.h | 3 ++- drivers/gpu/drm/gma500/power.c | 4 +++- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 0e99774c7e59..9e1b19fcea28 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -339,7 +339,7 @@ err_mutex_destroy: return ret; } -static int psb_gtt_resume(struct drm_device *dev) +int psb_gtt_resume(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); @@ -422,7 +422,7 @@ err_gtt_disable: return ret; } -static int psb_gem_mm_resume(struct drm_device *dev) +int psb_gem_mm_resume(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); @@ -445,17 +445,6 @@ static int psb_gem_mm_resume(struct drm_device *dev) } psb_gtt_populate_stolen(dev_priv); - - return 0; -} - -int psb_gtt_restore(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - - psb_gtt_resume(dev); - psb_gem_mm_resume(dev); - psb_gtt_populate_resources(dev_priv); return 0; diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index 45e1926dfce1..9a6d79200dce 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -27,7 +27,7 @@ struct psb_gtt { /* Exported functions */ int psb_gtt_init(struct drm_device *dev); void psb_gtt_fini(struct drm_device *dev); -extern int psb_gtt_restore(struct drm_device *dev); +int psb_gtt_resume(struct drm_device *dev); int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res, const char *name, resource_size_t size, resource_size_t align, @@ -39,5 +39,6 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r int psb_gem_mm_init(struct drm_device *dev); void psb_gem_mm_fini(struct drm_device *dev); +int psb_gem_mm_resume(struct drm_device *dev); #endif diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c index d2a46d96e746..28e472b31698 100644 --- a/drivers/gpu/drm/gma500/power.c +++ b/drivers/gpu/drm/gma500/power.c @@ -112,7 +112,9 @@ static void gma_resume_display(struct pci_dev *pdev) pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); - psb_gtt_restore(dev); /* Rebuild our GTT mappings */ + /* Rebuild our GTT mappings */ + psb_gtt_resume(dev); + psb_gem_mm_resume(dev); dev_priv->ops->restore_regs(dev); } -- cgit From d339386c9ffa69d20fcc34f3636dea80ea71818c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:20 +0100 Subject: drm/gma500: Move GEM memory management functions to gem.c Move GEM functions from gtt.c to gem.c. Adapt some names. No functional changes. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-11-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gem.c | 133 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/gma500/gem.h | 12 ++++ drivers/gpu/drm/gma500/gtt.c | 127 +------------------------------------ drivers/gpu/drm/gma500/gtt.h | 5 +- drivers/gpu/drm/gma500/power.c | 1 + drivers/gpu/drm/gma500/psb_drv.c | 1 + 6 files changed, 149 insertions(+), 130 deletions(-) diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 2f44535f58e7..dffe37490206 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -21,6 +21,10 @@ #include "gem.h" #include "psb_drv.h" +/* + * PSB GEM object + */ + int psb_gem_pin(struct psb_gem_object *pobj) { struct drm_gem_object *obj = &pobj->base; @@ -296,3 +300,132 @@ fail: return ret; } + +/* + * Memory management + */ + +/* Insert vram stolen pages into the GTT. */ +static void psb_gem_mm_populate_stolen(struct drm_psb_private *pdev) +{ + struct drm_device *dev = &pdev->dev; + unsigned int pfn_base; + unsigned int i, num_pages; + uint32_t pte; + + pfn_base = pdev->stolen_base >> PAGE_SHIFT; + num_pages = pdev->vram_stolen_size >> PAGE_SHIFT; + + drm_dbg(dev, "Set up %u stolen pages starting at 0x%08x, GTT offset %dK\n", + num_pages, pfn_base << PAGE_SHIFT, 0); + + for (i = 0; i < num_pages; ++i) { + pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY); + iowrite32(pte, pdev->gtt_map + i); + } + + (void)ioread32(pdev->gtt_map + i - 1); +} + +int psb_gem_mm_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned long stolen_size, vram_stolen_size; + struct psb_gtt *pg; + int ret; + + mutex_init(&dev_priv->mmap_mutex); + + pg = &dev_priv->gtt; + + pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); + vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; + + stolen_size = vram_stolen_size; + + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", + dev_priv->stolen_base, vram_stolen_size / 1024); + + pg->stolen_size = stolen_size; + dev_priv->vram_stolen_size = vram_stolen_size; + + dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); + if (!dev_priv->vram_addr) { + dev_err(dev->dev, "Failure to map stolen base.\n"); + ret = -ENOMEM; + goto err_mutex_destroy; + } + + psb_gem_mm_populate_stolen(dev_priv); + + return 0; + +err_mutex_destroy: + mutex_destroy(&dev_priv->mmap_mutex); + return ret; +} + +void psb_gem_mm_fini(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + + iounmap(dev_priv->vram_addr); + + mutex_destroy(&dev_priv->mmap_mutex); +} + +/* Re-insert all pinned GEM objects into GTT. */ +static void psb_gem_mm_populate_resources(struct drm_psb_private *pdev) +{ + unsigned int restored = 0, total = 0, size = 0; + struct resource *r = pdev->gtt_mem->child; + struct drm_device *dev = &pdev->dev; + struct psb_gem_object *pobj; + + while (r) { + /* + * TODO: GTT restoration needs a refactoring, so that we don't have to touch + * struct psb_gem_object here. The type represents a GEM object and is + * not related to the GTT itself. + */ + pobj = container_of(r, struct psb_gem_object, resource); + if (pobj->pages) { + psb_gtt_insert_pages(pdev, &pobj->resource, pobj->pages); + size += resource_size(&pobj->resource); + ++restored; + } + r = r->sibling; + ++total; + } + + drm_dbg(dev, "Restored %u of %u gtt ranges (%u KB)", restored, total, (size / 1024)); +} + +int psb_gem_mm_resume(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned long stolen_size, vram_stolen_size; + struct psb_gtt *pg; + + pg = &dev_priv->gtt; + + pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); + vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; + + stolen_size = vram_stolen_size; + + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", dev_priv->stolen_base, + vram_stolen_size / 1024); + + if (stolen_size != pg->stolen_size) { + dev_err(dev->dev, "GTT resume error.\n"); + return -EINVAL; + } + + psb_gem_mm_populate_stolen(dev_priv); + psb_gem_mm_populate_resources(dev_priv); + + return 0; +} diff --git a/drivers/gpu/drm/gma500/gem.h b/drivers/gpu/drm/gma500/gem.h index 96ef864b4bf1..27df6ff4ed37 100644 --- a/drivers/gpu/drm/gma500/gem.h +++ b/drivers/gpu/drm/gma500/gem.h @@ -14,6 +14,10 @@ struct drm_device; +/* + * PSB GEM object + */ + struct psb_gem_object { struct drm_gem_object base; @@ -36,4 +40,12 @@ psb_gem_create(struct drm_device *dev, u64 size, const char *name, bool stolen, int psb_gem_pin(struct psb_gem_object *pobj); void psb_gem_unpin(struct psb_gem_object *pobj); +/* + * Memory management + */ + +int psb_gem_mm_init(struct drm_device *dev); +void psb_gem_mm_fini(struct drm_device *dev); +int psb_gem_mm_resume(struct drm_device *dev); + #endif diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 9e1b19fcea28..b03feec64f01 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -49,7 +49,7 @@ int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res * * Set the GTT entry for the appropriate memory type. */ -static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) +uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) { uint32_t mask = PSB_PTE_VALID; @@ -125,15 +125,6 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r mutex_unlock(&pdev->gtt_mutex); } -void psb_gem_mm_fini(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - - iounmap(dev_priv->vram_addr); - - mutex_destroy(&dev_priv->mmap_mutex); -} - void psb_gtt_fini(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -164,55 +155,6 @@ static void psb_gtt_clear(struct drm_psb_private *pdev) (void)ioread32(pdev->gtt_map + i - 1); } -/* Insert vram stolen pages into the GTT. */ -static void psb_gtt_populate_stolen(struct drm_psb_private *pdev) -{ - struct drm_device *dev = &pdev->dev; - unsigned int pfn_base; - unsigned int i, num_pages; - uint32_t pte; - - pfn_base = pdev->stolen_base >> PAGE_SHIFT; - num_pages = pdev->vram_stolen_size >> PAGE_SHIFT; - - drm_dbg(dev, "Set up %u stolen pages starting at 0x%08x, GTT offset %dK\n", - num_pages, pfn_base << PAGE_SHIFT, 0); - - for (i = 0; i < num_pages; ++i) { - pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY); - iowrite32(pte, pdev->gtt_map + i); - } - - (void)ioread32(pdev->gtt_map + i - 1); -} - -/* Re-insert all pinned GEM objects into GTT. */ -static void psb_gtt_populate_resources(struct drm_psb_private *pdev) -{ - unsigned int restored = 0, total = 0, size = 0; - struct resource *r = pdev->gtt_mem->child; - struct drm_device *dev = &pdev->dev; - struct psb_gem_object *pobj; - - while (r) { - /* - * TODO: GTT restoration needs a refactoring, so that we don't have to touch - * struct psb_gem_object here. The type represents a GEM object and is - * not related to the GTT itself. - */ - pobj = container_of(r, struct psb_gem_object, resource); - if (pobj->pages) { - psb_gtt_insert_pages(pdev, &pobj->resource, pobj->pages); - size += resource_size(&pobj->resource); - ++restored; - } - r = r->sibling; - ++total; - } - - drm_dbg(dev, "Restored %u of %u gtt ranges (%u KB)", restored, total, (size / 1024)); -} - int psb_gtt_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -300,45 +242,6 @@ err_gtt_disable: return ret; } -int psb_gem_mm_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); - unsigned long stolen_size, vram_stolen_size; - struct psb_gtt *pg; - int ret; - - mutex_init(&dev_priv->mmap_mutex); - - pg = &dev_priv->gtt; - - pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); - vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; - - stolen_size = vram_stolen_size; - - dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", - dev_priv->stolen_base, vram_stolen_size / 1024); - - pg->stolen_size = stolen_size; - dev_priv->vram_stolen_size = vram_stolen_size; - - dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); - if (!dev_priv->vram_addr) { - dev_err(dev->dev, "Failure to map stolen base.\n"); - ret = -ENOMEM; - goto err_mutex_destroy; - } - - psb_gtt_populate_stolen(dev_priv); - - return 0; - -err_mutex_destroy: - mutex_destroy(&dev_priv->mmap_mutex); - return ret; -} - int psb_gtt_resume(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -421,31 +324,3 @@ err_gtt_disable: (void)PSB_RVDC32(PSB_PGETBL_CTL); return ret; } - -int psb_gem_mm_resume(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); - unsigned long stolen_size, vram_stolen_size; - struct psb_gtt *pg; - - pg = &dev_priv->gtt; - - pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base); - vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE; - - stolen_size = vram_stolen_size; - - dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", dev_priv->stolen_base, - vram_stolen_size / 1024); - - if (stolen_size != pg->stolen_size) { - dev_err(dev->dev, "GTT resume error.\n"); - return -EINVAL; - } - - psb_gtt_populate_stolen(dev_priv); - psb_gtt_populate_resources(dev_priv); - - return 0; -} diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index 9a6d79200dce..d5a5fd4466c1 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -33,12 +33,9 @@ int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res const char *name, resource_size_t size, resource_size_t align, bool stolen, u32 *offset); +uint32_t psb_gtt_mask_pte(uint32_t pfn, int type); void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *res, struct page **pages); void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *res); -int psb_gem_mm_init(struct drm_device *dev); -void psb_gem_mm_fini(struct drm_device *dev); -int psb_gem_mm_resume(struct drm_device *dev); - #endif diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c index 28e472b31698..6f917cfef65b 100644 --- a/drivers/gpu/drm/gma500/power.c +++ b/drivers/gpu/drm/gma500/power.c @@ -28,6 +28,7 @@ * Alan Cox */ +#include "gem.h" #include "power.h" #include "psb_drv.h" #include "psb_reg.h" diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 6547967b51e1..e30b58184156 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -28,6 +28,7 @@ #include #include "framebuffer.h" +#include "gem.h" #include "intel_bios.h" #include "mid_bios.h" #include "power.h" -- cgit From 5169f3592d48be8fd84b2a9e23c07a1c2977d19c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:21 +0100 Subject: drm/gma500: Move GTT enable and disable code into helpers Move the code for enabling and disabling the GTT into helpers and call the functions in psb_gtt_init(), psb_gtt_fini() and psb_gtt_resume(). Removes code duplication. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-12-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 81 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index b03feec64f01..83d9a9f7c73c 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -125,17 +125,44 @@ void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *r mutex_unlock(&pdev->gtt_mutex); } -void psb_gtt_fini(struct drm_device *dev) +static int psb_gtt_enable(struct drm_psb_private *dev_priv) { - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct drm_device *dev = &dev_priv->dev; struct pci_dev *pdev = to_pci_dev(dev->dev); + int ret; - iounmap(dev_priv->gtt_map); + ret = pci_read_config_word(pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); + if (ret) + return pcibios_err_to_errno(ret); + ret = pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); + if (ret) + return pcibios_err_to_errno(ret); + + dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); + PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); + + (void)PSB_RVDC32(PSB_PGETBL_CTL); + + return 0; +} + +static void psb_gtt_disable(struct drm_psb_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); + (void)PSB_RVDC32(PSB_PGETBL_CTL); +} +void psb_gtt_fini(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + + iounmap(dev_priv->gtt_map); + psb_gtt_disable(dev_priv); mutex_destroy(&dev_priv->gtt_mutex); } @@ -159,22 +186,15 @@ int psb_gtt_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); + struct psb_gtt *pg = &dev_priv->gtt; unsigned gtt_pages; - struct psb_gtt *pg; - int ret = 0; + int ret; mutex_init(&dev_priv->gtt_mutex); - pg = &dev_priv->gtt; - - /* Enable the GTT */ - pci_read_config_word(pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); - pci_write_config_word(pdev, PSB_GMCH_CTRL, - dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); - - dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); - PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); - (void) PSB_RVDC32(PSB_PGETBL_CTL); + ret = psb_gtt_enable(dev_priv); + if (ret) + goto err_mutex_destroy; /* The root resource we allocate address space from */ pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; @@ -227,17 +247,16 @@ int psb_gtt_init(struct drm_device *dev) if (!dev_priv->gtt_map) { dev_err(dev->dev, "Failure to map gtt.\n"); ret = -ENOMEM; - goto err_gtt_disable; + goto err_psb_gtt_disable; } psb_gtt_clear(dev_priv); return 0; -err_gtt_disable: - pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); - PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); - (void)PSB_RVDC32(PSB_PGETBL_CTL); +err_psb_gtt_disable: + psb_gtt_disable(dev_priv); +err_mutex_destroy: mutex_destroy(&dev_priv->gtt_mutex); return ret; } @@ -246,20 +265,14 @@ int psb_gtt_resume(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); + struct psb_gtt *pg = &dev_priv->gtt; unsigned int gtt_pages; - struct psb_gtt *pg; int ret; - pg = &dev_priv->gtt; - /* Enable the GTT */ - pci_read_config_word(pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); - pci_write_config_word(pdev, PSB_GMCH_CTRL, - dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); - - dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); - PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); - (void) PSB_RVDC32(PSB_PGETBL_CTL); + ret = psb_gtt_enable(dev_priv); + if (ret) + return ret; /* The root resource we allocate address space from */ pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; @@ -311,16 +324,14 @@ int psb_gtt_resume(struct drm_device *dev) if (gtt_pages != pg->gtt_pages) { dev_err(dev->dev, "GTT resume error.\n"); ret = -EINVAL; - goto err_gtt_disable; + goto err_psb_gtt_disable; } pg->gtt_pages = gtt_pages; psb_gtt_clear(dev_priv); -err_gtt_disable: - pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); - PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); - (void)PSB_RVDC32(PSB_PGETBL_CTL); +err_psb_gtt_disable: + psb_gtt_disable(dev_priv); return ret; } -- cgit From 07739597b4d0eae539c9020ea321f636afd878ee Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 8 Mar 2022 20:52:22 +0100 Subject: drm/gma500: Move GTT memory-range setup into helper Move the setup code for GTT/GATT memory ranges into a new helper and call the function from psb_gtt_init() and psb_gtt_resume(). Removes code duplication. Signed-off-by: Thomas Zimmermann Acked-by: Patrik Jakobsson Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220308195222.13471-13-tzimmermann@suse.de --- drivers/gpu/drm/gma500/gtt.c | 153 ++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 89 deletions(-) diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 83d9a9f7c73c..379bc218aa6b 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -182,68 +182,91 @@ static void psb_gtt_clear(struct drm_psb_private *pdev) (void)ioread32(pdev->gtt_map + i - 1); } -int psb_gtt_init(struct drm_device *dev) +static void psb_gtt_init_ranges(struct drm_psb_private *dev_priv) { - struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct drm_device *dev = &dev_priv->dev; struct pci_dev *pdev = to_pci_dev(dev->dev); struct psb_gtt *pg = &dev_priv->gtt; - unsigned gtt_pages; - int ret; - - mutex_init(&dev_priv->gtt_mutex); - - ret = psb_gtt_enable(dev_priv); - if (ret) - goto err_mutex_destroy; + resource_size_t gtt_phys_start, mmu_gatt_start, gtt_start, gtt_pages, + gatt_start, gatt_pages; + struct resource *gtt_mem; /* The root resource we allocate address space from */ - pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; + gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; /* - * The video mmu has a hw bug when accessing 0x0D0000000. - * Make gatt start at 0x0e000,0000. This doesn't actually - * matter for us but may do if the video acceleration ever - * gets opened up. + * The video MMU has a HW bug when accessing 0x0d0000000. Make + * GATT start at 0x0e0000000. This doesn't actually matter for + * us now, but maybe will if the video acceleration ever gets + * opened up. */ - pg->mmu_gatt_start = 0xE0000000; + mmu_gatt_start = 0xe0000000; + + gtt_start = pci_resource_start(pdev, PSB_GTT_RESOURCE); + gtt_pages = pci_resource_len(pdev, PSB_GTT_RESOURCE) >> PAGE_SHIFT; - pg->gtt_start = pci_resource_start(pdev, PSB_GTT_RESOURCE); - gtt_pages = pci_resource_len(pdev, PSB_GTT_RESOURCE) - >> PAGE_SHIFT; /* CDV doesn't report this. In which case the system has 64 gtt pages */ - if (pg->gtt_start == 0 || gtt_pages == 0) { + if (!gtt_start || !gtt_pages) { dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n"); gtt_pages = 64; - pg->gtt_start = dev_priv->pge_ctl; + gtt_start = dev_priv->pge_ctl; } - pg->gatt_start = pci_resource_start(pdev, PSB_GATT_RESOURCE); - pg->gatt_pages = pci_resource_len(pdev, PSB_GATT_RESOURCE) - >> PAGE_SHIFT; - dev_priv->gtt_mem = &pdev->resource[PSB_GATT_RESOURCE]; + gatt_start = pci_resource_start(pdev, PSB_GATT_RESOURCE); + gatt_pages = pci_resource_len(pdev, PSB_GATT_RESOURCE) >> PAGE_SHIFT; - if (pg->gatt_pages == 0 || pg->gatt_start == 0) { + if (!gatt_pages || !gatt_start) { static struct resource fudge; /* Preferably peppermint */ - /* This can occur on CDV systems. Fudge it in this case. - We really don't care what imaginary space is being allocated - at this point */ + + /* + * This can occur on CDV systems. Fudge it in this case. We + * really don't care what imaginary space is being allocated + * at this point. + */ dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n"); - pg->gatt_start = 0x40000000; - pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; - /* This is a little confusing but in fact the GTT is providing - a view from the GPU into memory and not vice versa. As such - this is really allocating space that is not the same as the - CPU address space on CDV */ + gatt_start = 0x40000000; + gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; + + /* + * This is a little confusing but in fact the GTT is providing + * a view from the GPU into memory and not vice versa. As such + * this is really allocating space that is not the same as the + * CPU address space on CDV. + */ fudge.start = 0x40000000; fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; fudge.name = "fudge"; fudge.flags = IORESOURCE_MEM; - dev_priv->gtt_mem = &fudge; + + gtt_mem = &fudge; + } else { + gtt_mem = &pdev->resource[PSB_GATT_RESOURCE]; } + pg->gtt_phys_start = gtt_phys_start; + pg->mmu_gatt_start = mmu_gatt_start; + pg->gtt_start = gtt_start; pg->gtt_pages = gtt_pages; + pg->gatt_start = gatt_start; + pg->gatt_pages = gatt_pages; + dev_priv->gtt_mem = gtt_mem; +} + +int psb_gtt_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct psb_gtt *pg = &dev_priv->gtt; + int ret; + + mutex_init(&dev_priv->gtt_mutex); + + ret = psb_gtt_enable(dev_priv); + if (ret) + goto err_mutex_destroy; - dev_priv->gtt_map = ioremap(pg->gtt_phys_start, gtt_pages << PAGE_SHIFT); + psb_gtt_init_ranges(dev_priv); + + dev_priv->gtt_map = ioremap(pg->gtt_phys_start, pg->gtt_pages << PAGE_SHIFT); if (!dev_priv->gtt_map) { dev_err(dev->dev, "Failure to map gtt.\n"); ret = -ENOMEM; @@ -264,9 +287,8 @@ err_mutex_destroy: int psb_gtt_resume(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); struct psb_gtt *pg = &dev_priv->gtt; - unsigned int gtt_pages; + unsigned int old_gtt_pages = pg->gtt_pages; int ret; /* Enable the GTT */ @@ -274,61 +296,14 @@ int psb_gtt_resume(struct drm_device *dev) if (ret) return ret; - /* The root resource we allocate address space from */ - pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; - - /* - * The video mmu has a hw bug when accessing 0x0D0000000. - * Make gatt start at 0x0e000,0000. This doesn't actually - * matter for us but may do if the video acceleration ever - * gets opened up. - */ - pg->mmu_gatt_start = 0xE0000000; - - pg->gtt_start = pci_resource_start(pdev, PSB_GTT_RESOURCE); - gtt_pages = pci_resource_len(pdev, PSB_GTT_RESOURCE) >> PAGE_SHIFT; - /* CDV doesn't report this. In which case the system has 64 gtt pages */ - if (pg->gtt_start == 0 || gtt_pages == 0) { - dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n"); - gtt_pages = 64; - pg->gtt_start = dev_priv->pge_ctl; - } - - pg->gatt_start = pci_resource_start(pdev, PSB_GATT_RESOURCE); - pg->gatt_pages = pci_resource_len(pdev, PSB_GATT_RESOURCE) >> PAGE_SHIFT; - dev_priv->gtt_mem = &pdev->resource[PSB_GATT_RESOURCE]; + psb_gtt_init_ranges(dev_priv); - if (pg->gatt_pages == 0 || pg->gatt_start == 0) { - static struct resource fudge; /* Preferably peppermint */ - /* - * This can occur on CDV systems. Fudge it in this case. We - * really don't care what imaginary space is being allocated - * at this point. - */ - dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n"); - pg->gatt_start = 0x40000000; - pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; - /* - * This is a little confusing but in fact the GTT is providing - * a view from the GPU into memory and not vice versa. As such - * this is really allocating space that is not the same as the - * CPU address space on CDV. - */ - fudge.start = 0x40000000; - fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; - fudge.name = "fudge"; - fudge.flags = IORESOURCE_MEM; - dev_priv->gtt_mem = &fudge; - } - - if (gtt_pages != pg->gtt_pages) { + if (old_gtt_pages != pg->gtt_pages) { dev_err(dev->dev, "GTT resume error.\n"); - ret = -EINVAL; + ret = -ENODEV; goto err_psb_gtt_disable; } - pg->gtt_pages = gtt_pages; - psb_gtt_clear(dev_priv); err_psb_gtt_disable: -- cgit From f33b21f1397ee0f22f1d514cf6c3344c6c731a8b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 15 Mar 2022 22:19:29 +0000 Subject: drm/nouveau: Fix spelling mistake "endianess" -> "endianness" There is a spelling mistake in a nvdev_error error message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20220315221929.2959700-1-colin.i.king@gmail.com --- drivers/gpu/drm/nouveau/nvkm/engine/device/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 88d262ba648c..62efbd0f3846 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2935,7 +2935,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func, /* switch mmio to cpu's native endianness */ if (!nvkm_device_endianness(device)) { nvdev_error(device, - "Couldn't switch GPU to CPUs endianess\n"); + "Couldn't switch GPU to CPUs endianness\n"); ret = -ENOSYS; goto done; } -- cgit From aa457ff1b2be72c0d89ee2118769859e513e6fc8 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 7 Mar 2022 08:32:36 -0700 Subject: drm/doc: Clarify what ioctls can be used on render nodes The documentation for render nodes indicates that only "PRIME-related" ioctls are valid on render nodes, but the documentation does not clarify what that means. If the reader is not familiar with PRIME, they may beleive this to be only the ioctls with "PRIME" in the name and not other ioctls such as set of syncobj ioctls. Clarify the situation for the reader by referencing where the reader will find a current list of valid ioctls. Signed-off-by: Jeffrey Hugo Acked-by: Pekka Paalanen Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1646667156-16366-1-git-send-email-quic_jhugo@quicinc.com --- Documentation/gpu/drm-uapi.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 199afb503ab1..ce47b4292481 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -148,7 +148,9 @@ clients together with the legacy drmAuth authentication procedure. If a driver advertises render node support, DRM core will create a separate render node called renderD. There will be one render node per device. No ioctls except PRIME-related ioctls will be allowed on -this node. Especially GEM_OPEN will be explicitly prohibited. Render +this node. Especially GEM_OPEN will be explicitly prohibited. For a +complete list of driver-independent ioctls that can be used on render +nodes, see the ioctls marked DRM_RENDER_ALLOW in drm_ioctl.c Render nodes are designed to avoid the buffer-leaks, which occur if clients guess the flink names or mmap offsets on the legacy interface. Additionally to this basic interface, drivers must mark their -- cgit From 9b13a3fcd35fc24045d2fd0f0e13ddd8d7985b4b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 17 Mar 2022 09:18:26 +0100 Subject: drm/format-helper: Rename drm_fb_xrgb8888_to_mono_reversed() There is no "reversed" handling in drm_fb_xrgb8888_to_mono_reversed(): the function just converts from color to grayscale, and reduces the number of grayscale levels from 256 to 2 (i.e. brightness 0-127 is mapped to 0, 128-255 to 1). All "reversed" handling is done in the repaper driver, where this function originated. Hence make this clear by renaming drm_fb_xrgb8888_to_mono_reversed() to drm_fb_xrgb8888_to_mono(), and documenting the black/white pixel mapping. Fixes: bcf8b616deb87941 ("drm/format-helper: Add drm_fb_xrgb8888_to_mono_reversed()") Signed-off-by: Geert Uytterhoeven Acked-by: Javier Martinez Canillas Reviewed-by: Andy Shevchenko Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220317081830.1211400-2-geert@linux-m68k.org --- drivers/gpu/drm/drm_format_helper.c | 31 +++++++++++++++---------------- drivers/gpu/drm/solomon/ssd130x.c | 2 +- drivers/gpu/drm/tiny/repaper.c | 2 +- include/drm/drm_format_helper.h | 5 ++--- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index bc0f49773868..5d9d0c695845 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -594,8 +594,8 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for } EXPORT_SYMBOL(drm_fb_blit_toio); -static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, unsigned int pixels, - unsigned int start_offset, unsigned int end_len) +static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixels, + unsigned int start_offset, unsigned int end_len) { unsigned int xb, i; @@ -621,8 +621,8 @@ static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, unsigned } /** - * drm_fb_xrgb8888_to_mono_reversed - Convert XRGB8888 to reversed monochrome - * @dst: reversed monochrome destination buffer + * drm_fb_xrgb8888_to_mono - Convert XRGB8888 to monochrome + * @dst: monochrome destination buffer (0=black, 1=white) * @dst_pitch: Number of bytes between two consecutive scanlines within dst * @src: XRGB8888 source buffer * @fb: DRM framebuffer @@ -633,10 +633,10 @@ static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, unsigned * and use this function to convert to the native format. * * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and - * then the result is converted from grayscale to reversed monohrome. + * then the result is converted from grayscale to monochrome. */ -void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *vaddr, - const struct drm_framebuffer *fb, const struct drm_rect *clip) +void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vaddr, + const struct drm_framebuffer *fb, const struct drm_rect *clip) { unsigned int linepixels = drm_rect_width(clip); unsigned int lines = clip->y2 - clip->y1; @@ -652,8 +652,8 @@ void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const v return; /* - * The reversed mono destination buffer contains 1 bit per pixel - * and destination scanlines have to be in multiple of 8 pixels. + * The mono destination buffer contains 1 bit per pixel and + * destination scanlines have to be in multiple of 8 pixels. */ if (!dst_pitch) dst_pitch = DIV_ROUND_UP(linepixels, 8); @@ -664,9 +664,9 @@ void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const v * The cma memory is write-combined so reads are uncached. * Speed up by fetching one line at a time. * - * Also, format conversion from XR24 to reversed monochrome - * are done line-by-line but are converted to 8-bit grayscale - * as an intermediate step. + * Also, format conversion from XR24 to monochrome are done + * line-by-line but are converted to 8-bit grayscale as an + * intermediate step. * * Allocate a buffer to be used for both copying from the cma * memory and to store the intermediate grayscale line pixels. @@ -683,7 +683,7 @@ void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const v * are not aligned to multiple of 8. * * Calculate if the start and end pixels are not aligned and set the - * offsets for the reversed mono line conversion function to adjust. + * offsets for the mono line conversion function to adjust. */ start_offset = clip->x1 % 8; end_len = clip->x2 % 8; @@ -692,12 +692,11 @@ void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const v for (y = 0; y < lines; y++) { src32 = memcpy(src32, vaddr, len_src32); drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels); - drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch, - start_offset, end_len); + drm_fb_gray8_to_mono_line(mono, gray8, dst_pitch, start_offset, end_len); vaddr += fb->pitches[0]; mono += dst_pitch; } kfree(src32); } -EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed); +EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono); diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index a95ab7e5bbf0..33e5bacbdf76 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -458,7 +458,7 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf if (!buf) return -ENOMEM; - drm_fb_xrgb8888_to_mono_reversed(buf, 0, vmap, fb, rect); + drm_fb_xrgb8888_to_mono(buf, 0, vmap, fb, rect); ssd130x_update_rect(ssd130x, buf, rect); diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index 5c74e236b16d..c7018dd1b831 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -540,7 +540,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb) if (ret) goto out_free; - drm_fb_xrgb8888_to_mono_reversed(buf, 0, cma_obj->vaddr, fb, &clip); + drm_fb_xrgb8888_to_mono(buf, 0, cma_obj->vaddr, fb, &clip); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h index 0b0937c0b2f6..55145eca0782 100644 --- a/include/drm/drm_format_helper.h +++ b/include/drm/drm_format_helper.h @@ -43,8 +43,7 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for const void *vmap, const struct drm_framebuffer *fb, const struct drm_rect *rect); -void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, - const struct drm_framebuffer *fb, - const struct drm_rect *clip); +void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_framebuffer *fb, const struct drm_rect *clip); #endif /* __LINUX_DRM_FORMAT_HELPER_H */ -- cgit From 7392f2459eefcdab1d998af002d2b8b16fe4a2fd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 17 Mar 2022 09:18:27 +0100 Subject: drm/format-helper: Fix XRGB888 to monochrome conversion The conversion functions drm_fb_xrgb8888_to_mono() and drm_fb_gray8_to_mono_line() do not behave correctly when the horizontal boundaries of the clip rectangle are not multiples of 8: a. When x1 % 8 != 0, the calculated pitch is not correct, b. When x2 % 8 != 0, the pixel data for the last byte is wrong. Simplify the code and fix (a) by: 1. Removing start_offset, and always storing the first pixel in the first bit of the monochrome destination buffer. Drivers that require the first pixel in a byte to be located at an x-coordinate that is a multiple of 8 can always align the clip rectangle before calling drm_fb_xrgb8888_to_mono(). Note that: - The ssd130x driver does not need the alignment, as the monochrome buffer is a temporary format, - The repaper driver always updates the full screen, so the clip rectangle is always aligned. 2. Passing the number of pixels to drm_fb_gray8_to_mono_line(), instead of the number of bytes, and the number of pixels in the last byte. Fix (b) by explicitly setting the target bit, instead of always setting bit 7 and shifting the value in each loop iteration. Remove the bogus pitch check, which operates on bytes instead of pixels, and triggers when e.g. flashing the cursor on a text console with a font that is 8 pixels wide. Drop the confusing comment about scanlines, as a pitch in bytes always contains a multiple of 8 pixels. While at it, use the drm_rect_height() helper instead of open-coding the same operation. Update the comments accordingly. Fixes: bcf8b616deb87941 ("drm/format-helper: Add drm_fb_xrgb8888_to_mono_reversed()") Signed-off-by: Geert Uytterhoeven Acked-by: Javier Martinez Canillas Reviewed-by: Andy Shevchenko Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220317081830.1211400-3-geert@linux-m68k.org --- drivers/gpu/drm/drm_format_helper.c | 55 ++++++++++++------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 5d9d0c695845..e085f855a199 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -594,27 +594,16 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for } EXPORT_SYMBOL(drm_fb_blit_toio); -static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixels, - unsigned int start_offset, unsigned int end_len) -{ - unsigned int xb, i; - - for (xb = 0; xb < pixels; xb++) { - unsigned int start = 0, end = 8; - u8 byte = 0x00; - - if (xb == 0 && start_offset) - start = start_offset; - if (xb == pixels - 1 && end_len) - end = end_len; - - for (i = start; i < end; i++) { - unsigned int x = xb * 8 + i; +static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixels) +{ + while (pixels) { + unsigned int i, bits = min(pixels, 8U); + u8 byte = 0; - byte >>= 1; - if (src[x] >> 7) - byte |= BIT(7); + for (i = 0; i < bits; i++, pixels--) { + if (*src++ >= 128) + byte |= BIT(i); } *dst++ = byte; } @@ -634,16 +623,22 @@ static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixel * * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and * then the result is converted from grayscale to monochrome. + * + * The first pixel (upper left corner of the clip rectangle) will be converted + * and copied to the first bit (LSB) in the first byte of the monochrome + * destination buffer. + * If the caller requires that the first pixel in a byte must be located at an + * x-coordinate that is a multiple of 8, then the caller must take care itself + * of supplying a suitable clip rectangle. */ void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vaddr, const struct drm_framebuffer *fb, const struct drm_rect *clip) { unsigned int linepixels = drm_rect_width(clip); - unsigned int lines = clip->y2 - clip->y1; + unsigned int lines = drm_rect_height(clip); unsigned int cpp = fb->format->cpp[0]; unsigned int len_src32 = linepixels * cpp; struct drm_device *dev = fb->dev; - unsigned int start_offset, end_len; unsigned int y; u8 *mono = dst, *gray8; u32 *src32; @@ -652,14 +647,11 @@ void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vadd return; /* - * The mono destination buffer contains 1 bit per pixel and - * destination scanlines have to be in multiple of 8 pixels. + * The mono destination buffer contains 1 bit per pixel */ if (!dst_pitch) dst_pitch = DIV_ROUND_UP(linepixels, 8); - drm_WARN_ONCE(dev, dst_pitch % 8 != 0, "dst_pitch is not a multiple of 8\n"); - /* * The cma memory is write-combined so reads are uncached. * Speed up by fetching one line at a time. @@ -677,22 +669,11 @@ void drm_fb_xrgb8888_to_mono(void *dst, unsigned int dst_pitch, const void *vadd gray8 = (u8 *)src32 + len_src32; - /* - * For damage handling, it is possible that only parts of the source - * buffer is copied and this could lead to start and end pixels that - * are not aligned to multiple of 8. - * - * Calculate if the start and end pixels are not aligned and set the - * offsets for the mono line conversion function to adjust. - */ - start_offset = clip->x1 % 8; - end_len = clip->x2 % 8; - vaddr += clip_offset(clip, fb->pitches[0], cpp); for (y = 0; y < lines; y++) { src32 = memcpy(src32, vaddr, len_src32); drm_fb_xrgb8888_to_gray8_line(gray8, src32, linepixels); - drm_fb_gray8_to_mono_line(mono, gray8, dst_pitch, start_offset, end_len); + drm_fb_gray8_to_mono_line(mono, gray8, linepixels); vaddr += fb->pitches[0]; mono += dst_pitch; } -- cgit From a97e753fd358e23155ae42c61292dfd57eb54c4a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 17 Mar 2022 09:18:28 +0100 Subject: drm/ssd130x: Fix rectangle updates The rectangle update functions ssd130x_fb_blit_rect() and ssd130x_update_rect() do not behave correctly when x1 != 0 or y1 != 0, or when y1 or y2 are not aligned to display page boundaries. E.g. when used as a text console, only the first line of text is shown on the display. 1. The buffer passed by ssd130x_fb_blit_rect() points to the first byte of monochrome bitmap data, and thus has its origin at (x1, y1), while ssd130x_update_rect() assumes it is at (0, 0). Fix ssd130x_update_rect() by changing the vertical and horizontal loop ranges, and adding the offsets only when needed. 2. In ssd130x_fb_blit_rect(), align y1 and y2 to the display page boundaries before doing the color conversion, so the full page is converted and updated. Remove the correction for an unaligned y1 from ssd130x_update_rect(), and add a check to make sure y1 is aligned. Fixes: a61732e808672cfa ("drm: Add driver for Solomon SSD130x OLED displays") Signed-off-by: Geert Uytterhoeven Acked-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220317081830.1211400-4-geert@linux-m68k.org --- drivers/gpu/drm/solomon/ssd130x.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 33e5bacbdf76..5156c4c2c076 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -355,11 +355,14 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, unsigned int width = drm_rect_width(rect); unsigned int height = drm_rect_height(rect); unsigned int line_length = DIV_ROUND_UP(width, 8); - unsigned int pages = DIV_ROUND_UP(y % 8 + height, 8); + unsigned int pages = DIV_ROUND_UP(height, 8); + struct drm_device *drm = &ssd130x->drm; u32 array_idx = 0; int ret, i, j, k; u8 *data_array = NULL; + drm_WARN_ONCE(drm, y % 8 != 0, "y must be aligned to screen page\n"); + data_array = kcalloc(width, pages, GFP_KERNEL); if (!data_array) return -ENOMEM; @@ -401,13 +404,13 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, if (ret < 0) goto out_free; - for (i = y / 8; i < y / 8 + pages; i++) { + for (i = 0; i < pages; i++) { int m = 8; /* Last page may be partial */ - if (8 * (i + 1) > ssd130x->height) + if (8 * (y / 8 + i + 1) > ssd130x->height) m = ssd130x->height % 8; - for (j = x; j < x + width; j++) { + for (j = 0; j < width; j++) { u8 data = 0; for (k = 0; k < m; k++) { @@ -454,6 +457,10 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf int ret = 0; u8 *buf = NULL; + /* Align y to display page boundaries */ + rect->y1 = round_down(rect->y1, 8); + rect->y2 = min_t(unsigned int, round_up(rect->y2, 8), ssd130x->height); + buf = kcalloc(fb->width, fb->height, GFP_KERNEL); if (!buf) return -ENOMEM; -- cgit From 4442ac1af10442d6e7e824fdc226f89ed94d5b53 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 17 Mar 2022 09:18:29 +0100 Subject: drm/ssd130x: Reduce temporary buffer sizes ssd130x_clear_screen() allocates a temporary buffer sized to hold one byte per pixel, while it only needs to hold one bit per pixel. ssd130x_fb_blit_rect() allocates a temporary buffer sized to hold one byte per pixel for the whole frame buffer, while it only needs to hold one bit per pixel for the part that is to be updated. Pass dst_pitch to drm_fb_xrgb8888_to_mono(), as we have already calculated it anyway. Fixes: a61732e808672cfa ("drm: Add driver for Solomon SSD130x OLED displays") Signed-off-by: Geert Uytterhoeven Acked-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220317081830.1211400-5-geert@linux-m68k.org --- drivers/gpu/drm/solomon/ssd130x.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 5156c4c2c076..ddff4561120e 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -440,7 +440,8 @@ static void ssd130x_clear_screen(struct ssd130x_device *ssd130x) .y2 = ssd130x->height, }; - buf = kcalloc(ssd130x->width, ssd130x->height, GFP_KERNEL); + buf = kcalloc(DIV_ROUND_UP(ssd130x->width, 8), ssd130x->height, + GFP_KERNEL); if (!buf) return; @@ -454,6 +455,7 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf { struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ + unsigned int dst_pitch; int ret = 0; u8 *buf = NULL; @@ -461,11 +463,12 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf rect->y1 = round_down(rect->y1, 8); rect->y2 = min_t(unsigned int, round_up(rect->y2, 8), ssd130x->height); - buf = kcalloc(fb->width, fb->height, GFP_KERNEL); + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); + buf = kcalloc(dst_pitch, drm_rect_height(rect), GFP_KERNEL); if (!buf) return -ENOMEM; - drm_fb_xrgb8888_to_mono(buf, 0, vmap, fb, rect); + drm_fb_xrgb8888_to_mono(buf, dst_pitch, vmap, fb, rect); ssd130x_update_rect(ssd130x, buf, rect); -- cgit From 044feb974801140d923ecae66bc1685592b19865 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Tue, 1 Feb 2022 20:26:51 -0100 Subject: drm/vc4: add tracepoints for CL submissions Trace submit_cl_ioctl and related IRQs for CL submission and bin/render jobs execution. It might be helpful to get a rendering timeline and track job throttling. Signed-off-by: Melissa Wen Reviewed-by: Jose Maria Casanova Crespo Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220201212651.zhltjmaokisffq3x@mail.igalia.com --- drivers/gpu/drm/vc4/vc4_gem.c | 7 +++ drivers/gpu/drm/vc4/vc4_irq.c | 5 +++ drivers/gpu/drm/vc4/vc4_trace.h | 95 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 445d3bab89e0..4abf10b66fe8 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -485,6 +485,8 @@ again: * immediately move it to the to-be-rendered queue. */ if (exec->ct0ca != exec->ct0ea) { + trace_vc4_submit_cl(dev, false, exec->seqno, exec->ct0ca, + exec->ct0ea); submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); } else { struct vc4_exec_info *next; @@ -519,6 +521,7 @@ vc4_submit_next_render_job(struct drm_device *dev) */ vc4_flush_texture_caches(dev); + trace_vc4_submit_cl(dev, true, exec->seqno, exec->ct1ca, exec->ct1ea); submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); } @@ -1135,6 +1138,10 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct dma_fence *in_fence; int ret = 0; + trace_vc4_submit_cl_ioctl(dev, args->bin_cl_size, + args->shader_rec_size, + args->bo_handle_count); + if (!vc4->v3d) { DRM_DEBUG("VC4_SUBMIT_CL with no VC4 V3D probed\n"); return -ENODEV; diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 20fa8e34c20b..4342fb43e8c1 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -51,6 +51,7 @@ #include "vc4_drv.h" #include "vc4_regs.h" +#include "vc4_trace.h" #define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ V3D_INT_FLDONE | \ @@ -123,6 +124,8 @@ vc4_irq_finish_bin_job(struct drm_device *dev) if (!exec) return; + trace_vc4_bcl_end_irq(dev, exec->seqno); + vc4_move_job_to_render(dev, exec); next = vc4_first_bin_job(vc4); @@ -161,6 +164,8 @@ vc4_irq_finish_render_job(struct drm_device *dev) if (!exec) return; + trace_vc4_rcl_end_irq(dev, exec->seqno); + vc4->finished_seqno++; list_move_tail(&exec->head, &vc4->job_done_list); diff --git a/drivers/gpu/drm/vc4/vc4_trace.h b/drivers/gpu/drm/vc4/vc4_trace.h index 1cccde0b09a7..7f4c49e7e011 100644 --- a/drivers/gpu/drm/vc4/vc4_trace.h +++ b/drivers/gpu/drm/vc4/vc4_trace.h @@ -52,6 +52,101 @@ TRACE_EVENT(vc4_wait_for_seqno_end, __entry->dev, __entry->seqno) ); +TRACE_EVENT(vc4_submit_cl_ioctl, + TP_PROTO(struct drm_device *dev, u32 bin_cl_size, u32 shader_rec_size, u32 bo_count), + TP_ARGS(dev, bin_cl_size, shader_rec_size, bo_count), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, bin_cl_size) + __field(u32, shader_rec_size) + __field(u32, bo_count) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->bin_cl_size = bin_cl_size; + __entry->shader_rec_size = shader_rec_size; + __entry->bo_count = bo_count; + ), + + TP_printk("dev=%u, bin_cl_size=%u, shader_rec_size=%u, bo_count=%u", + __entry->dev, + __entry->bin_cl_size, + __entry->shader_rec_size, + __entry->bo_count) +); + +TRACE_EVENT(vc4_submit_cl, + TP_PROTO(struct drm_device *dev, bool is_render, + uint64_t seqno, + u32 ctnqba, u32 ctnqea), + TP_ARGS(dev, is_render, seqno, ctnqba, ctnqea), + + TP_STRUCT__entry( + __field(u32, dev) + __field(bool, is_render) + __field(u64, seqno) + __field(u32, ctnqba) + __field(u32, ctnqea) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->is_render = is_render; + __entry->seqno = seqno; + __entry->ctnqba = ctnqba; + __entry->ctnqea = ctnqea; + ), + + TP_printk("dev=%u, %s, seqno=%llu, 0x%08x..0x%08x", + __entry->dev, + __entry->is_render ? "RCL" : "BCL", + __entry->seqno, + __entry->ctnqba, + __entry->ctnqea) +); + +TRACE_EVENT(vc4_bcl_end_irq, + TP_PROTO(struct drm_device *dev, + uint64_t seqno), + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%llu", + __entry->dev, + __entry->seqno) +); + +TRACE_EVENT(vc4_rcl_end_irq, + TP_PROTO(struct drm_device *dev, + uint64_t seqno), + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%llu", + __entry->dev, + __entry->seqno) +); + #endif /* _VC4_TRACE_H_ */ /* This part must be outside protection */ -- cgit From 3138ba6e0653f62828d02e4d99e7e033e628076e Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Thu, 17 Mar 2022 10:25:52 +0100 Subject: drm/gma500: Remove unused declarations and other cruft Most of these are old leftovers from one of the driver merges. This is all dead code. Signed-off-by: Patrik Jakobsson Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220317092555.17882-1-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/psb_drv.h | 75 +--------------------------------------- 1 file changed, 1 insertion(+), 74 deletions(-) diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 553d03190ce1..66f61909a8c8 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -36,12 +36,6 @@ /* Append new drm mode definition here, align with libdrm definition */ #define DRM_MODE_SCALE_NO_SCALE 2 -enum { - CHIP_PSB_8108 = 0, /* Poulsbo */ - CHIP_PSB_8109 = 1, /* Poulsbo */ - CHIP_MRST_4100 = 2, /* Moorestown/Oaktrail */ -}; - #define IS_PSB(drm) ((to_pci_dev((drm)->dev)->device & 0xfffe) == 0x8108) #define IS_MRST(drm) ((to_pci_dev((drm)->dev)->device & 0xfff0) == 0x4100) #define IS_CDV(drm) ((to_pci_dev((drm)->dev)->device & 0xfff0) == 0x0be0) @@ -617,15 +611,7 @@ struct psb_ops { int i2c_bus; /* I2C bus identifier for Moorestown */ }; - - -extern int drm_crtc_probe_output_modes(struct drm_device *dev, int, int); -extern int drm_pick_crtcs(struct drm_device *dev); - /* psb_irq.c */ -extern void psb_irq_uninstall_islands(struct drm_device *dev, int hw_islands); -extern int psb_vblank_wait2(struct drm_device *dev, unsigned int *sequence); -extern int psb_vblank_wait(struct drm_device *dev, unsigned int *sequence); extern int psb_enable_vblank(struct drm_crtc *crtc); extern void psb_disable_vblank(struct drm_crtc *crtc); void @@ -636,17 +622,9 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); extern u32 psb_get_vblank_counter(struct drm_crtc *crtc); -/* framebuffer.c */ -extern int psbfb_probed(struct drm_device *dev); -extern int psbfb_remove(struct drm_device *dev, - struct drm_framebuffer *fb); -/* psb_drv.c */ -extern void psb_spank(struct drm_psb_private *dev_priv); - -/* psb_reset.c */ +/* psb_lid.c */ extern void psb_lid_timer_init(struct drm_psb_private *dev_priv); extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv); -extern void psb_print_pagefault(struct drm_psb_private *dev_priv); /* modesetting */ extern void psb_modeset_init(struct drm_device *dev); @@ -689,43 +667,7 @@ extern const struct psb_ops oaktrail_chip_ops; /* cdv_device.c */ extern const struct psb_ops cdv_chip_ops; -/* Debug print bits setting */ -#define PSB_D_GENERAL (1 << 0) -#define PSB_D_INIT (1 << 1) -#define PSB_D_IRQ (1 << 2) -#define PSB_D_ENTRY (1 << 3) -/* debug the get H/V BP/FP count */ -#define PSB_D_HV (1 << 4) -#define PSB_D_DBI_BF (1 << 5) -#define PSB_D_PM (1 << 6) -#define PSB_D_RENDER (1 << 7) -#define PSB_D_REG (1 << 8) -#define PSB_D_MSVDX (1 << 9) -#define PSB_D_TOPAZ (1 << 10) - -extern int drm_idle_check_interval; - /* Utilities */ -static inline u32 MRST_MSG_READ32(int domain, uint port, uint offset) -{ - int mcr = (0xD0<<24) | (port << 16) | (offset << 8); - uint32_t ret_val = 0; - struct pci_dev *pci_root = pci_get_domain_bus_and_slot(domain, 0, 0); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_read_config_dword(pci_root, 0xD4, &ret_val); - pci_dev_put(pci_root); - return ret_val; -} -static inline void MRST_MSG_WRITE32(int domain, uint port, uint offset, - u32 value) -{ - int mcr = (0xE0<<24) | (port << 16) | (offset << 8) | 0xF0; - struct pci_dev *pci_root = pci_get_domain_bus_and_slot(domain, 0, 0); - pci_write_config_dword(pci_root, 0xD4, value); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_dev_put(pci_root); -} - static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -806,24 +748,9 @@ static inline void REGISTER_WRITE8(struct drm_device *dev, #define PSB_WVDC32(_val, _offs) iowrite32(_val, dev_priv->vdc_reg + (_offs)) #define PSB_RVDC32(_offs) ioread32(dev_priv->vdc_reg + (_offs)) -/* #define TRAP_SGX_PM_FAULT 1 */ -#ifdef TRAP_SGX_PM_FAULT -#define PSB_RSGX32(_offs) \ -({ \ - if (inl(dev_priv->apm_base + PSB_APM_STS) & 0x3) { \ - pr_err("access sgx when it's off!! (READ) %s, %d\n", \ - __FILE__, __LINE__); \ - melay(1000); \ - } \ - ioread32(dev_priv->sgx_reg + (_offs)); \ -}) -#else #define PSB_RSGX32(_offs) ioread32(dev_priv->sgx_reg + (_offs)) -#endif #define PSB_WSGX32(_val, _offs) iowrite32(_val, dev_priv->sgx_reg + (_offs)) -#define MSVDX_REG_DUMP 0 - #define PSB_WMSVDX32(_val, _offs) iowrite32(_val, dev_priv->msvdx_reg + (_offs)) #define PSB_RMSVDX32(_offs) ioread32(dev_priv->msvdx_reg + (_offs)) -- cgit From a29c6f882418ecc7a2046d8cf9b5a8aa92b68693 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Thu, 17 Mar 2022 10:25:53 +0100 Subject: drm/gma500: Move gma_intel_crtc_funcs into gma_display.c All functions live in gma_display.c already so move the vtable. Also shorten the name to gma_crtc_funcs. Signed-off-by: Patrik Jakobsson Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220317092555.17882-2-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/cdv_device.c | 2 +- drivers/gpu/drm/gma500/gma_display.c | 12 ++++++++++++ drivers/gpu/drm/gma500/gma_display.h | 10 ++-------- drivers/gpu/drm/gma500/oaktrail_device.c | 2 +- drivers/gpu/drm/gma500/psb_device.c | 2 +- drivers/gpu/drm/gma500/psb_drv.h | 2 -- drivers/gpu/drm/gma500/psb_intel_display.c | 12 ------------ 7 files changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index d7c6cca23e94..887c157d75f4 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -603,7 +603,7 @@ const struct psb_ops cdv_chip_ops = { .errata = cdv_errata, .crtc_helper = &cdv_intel_helper_funcs, - .crtc_funcs = &gma_intel_crtc_funcs, + .crtc_funcs = &gma_crtc_funcs, .clock_funcs = &cdv_clock_funcs, .output_init = cdv_output_init, diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index dd801404cf99..931ffb192fc4 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -565,6 +565,18 @@ int gma_crtc_set_config(struct drm_mode_set *set, return ret; } +const struct drm_crtc_funcs gma_crtc_funcs = { + .cursor_set = gma_crtc_cursor_set, + .cursor_move = gma_crtc_cursor_move, + .gamma_set = gma_crtc_gamma_set, + .set_config = gma_crtc_set_config, + .destroy = gma_crtc_destroy, + .page_flip = gma_crtc_page_flip, + .enable_vblank = psb_enable_vblank, + .disable_vblank = psb_disable_vblank, + .get_vblank_counter = psb_get_vblank_counter, +}; + /* * Save HW states of given crtc */ diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h index 7bd6c1ee8b21..113cf048105e 100644 --- a/drivers/gpu/drm/gma500/gma_display.h +++ b/drivers/gpu/drm/gma500/gma_display.h @@ -58,15 +58,7 @@ extern bool gma_pipe_has_type(struct drm_crtc *crtc, int type); extern void gma_wait_for_vblank(struct drm_device *dev); extern int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); -extern int gma_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height); -extern int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); extern void gma_crtc_load_lut(struct drm_crtc *crtc); -extern int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, u32 size, - struct drm_modeset_acquire_ctx *ctx); extern void gma_crtc_dpms(struct drm_crtc *crtc, int mode); extern void gma_crtc_prepare(struct drm_crtc *crtc); extern void gma_crtc_commit(struct drm_crtc *crtc); @@ -83,6 +75,8 @@ extern int gma_crtc_set_config(struct drm_mode_set *set, extern void gma_crtc_save(struct drm_crtc *crtc); extern void gma_crtc_restore(struct drm_crtc *crtc); +extern const struct drm_crtc_funcs gma_crtc_funcs; + extern void gma_encoder_prepare(struct drm_encoder *encoder); extern void gma_encoder_commit(struct drm_encoder *encoder); extern void gma_encoder_destroy(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 5c75eae630b5..40f1bc736125 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -545,7 +545,7 @@ const struct psb_ops oaktrail_chip_ops = { .chip_setup = oaktrail_chip_setup, .chip_teardown = oaktrail_teardown, .crtc_helper = &oaktrail_helper_funcs, - .crtc_funcs = &gma_intel_crtc_funcs, + .crtc_funcs = &gma_crtc_funcs, .output_init = oaktrail_output_init, diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index 3030f18ba022..e93e4191c0ca 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -329,7 +329,7 @@ const struct psb_ops psb_chip_ops = { .chip_teardown = psb_chip_teardown, .crtc_helper = &psb_intel_helper_funcs, - .crtc_funcs = &gma_intel_crtc_funcs, + .crtc_funcs = &gma_crtc_funcs, .clock_funcs = &psb_clock_funcs, .output_init = psb_output_init, diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 66f61909a8c8..88f44dbbc4eb 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -13,7 +13,6 @@ #include -#include "gma_display.h" #include "gtt.h" #include "intel_bios.h" #include "mmu.h" @@ -647,7 +646,6 @@ extern void oaktrail_lvds_init(struct drm_device *dev, /* psb_intel_display.c */ extern const struct drm_crtc_helper_funcs psb_intel_helper_funcs; -extern const struct drm_crtc_funcs gma_intel_crtc_funcs; /* psb_intel_lvds.c */ extern const struct drm_connector_helper_funcs diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index d5f95212934e..6df62fe7c1e0 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -427,18 +427,6 @@ const struct drm_crtc_helper_funcs psb_intel_helper_funcs = { .disable = gma_crtc_disable, }; -const struct drm_crtc_funcs gma_intel_crtc_funcs = { - .cursor_set = gma_crtc_cursor_set, - .cursor_move = gma_crtc_cursor_move, - .gamma_set = gma_crtc_gamma_set, - .set_config = gma_crtc_set_config, - .destroy = gma_crtc_destroy, - .page_flip = gma_crtc_page_flip, - .enable_vblank = psb_enable_vblank, - .disable_vblank = psb_disable_vblank, - .get_vblank_counter = psb_get_vblank_counter, -}; - const struct gma_clock_funcs psb_clock_funcs = { .clock = psb_intel_clock, .limit = psb_intel_limit, -- cgit From 76feffd16cc9728a4d6adf2080fe7f79242822c7 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Thu, 17 Mar 2022 10:25:54 +0100 Subject: drm/gma500: Don't store crtc_funcs in psb_ops The drm_crtc_funcs are all generic and no chip specific functions are necessary. We can therefore directly put gma_crtc_funcs into the drm_crtc. Signed-off-by: Patrik Jakobsson Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220317092555.17882-3-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/cdv_device.c | 1 - drivers/gpu/drm/gma500/oaktrail_device.c | 1 - drivers/gpu/drm/gma500/psb_device.c | 1 - drivers/gpu/drm/gma500/psb_drv.h | 1 - drivers/gpu/drm/gma500/psb_intel_display.c | 3 +-- 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 887c157d75f4..f854f58bcbb3 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -603,7 +603,6 @@ const struct psb_ops cdv_chip_ops = { .errata = cdv_errata, .crtc_helper = &cdv_intel_helper_funcs, - .crtc_funcs = &gma_crtc_funcs, .clock_funcs = &cdv_clock_funcs, .output_init = cdv_output_init, diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 40f1bc736125..5923a9c89312 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -545,7 +545,6 @@ const struct psb_ops oaktrail_chip_ops = { .chip_setup = oaktrail_chip_setup, .chip_teardown = oaktrail_teardown, .crtc_helper = &oaktrail_helper_funcs, - .crtc_funcs = &gma_crtc_funcs, .output_init = oaktrail_output_init, diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index e93e4191c0ca..59f325165667 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -329,7 +329,6 @@ const struct psb_ops psb_chip_ops = { .chip_teardown = psb_chip_teardown, .crtc_helper = &psb_intel_helper_funcs, - .crtc_funcs = &gma_crtc_funcs, .clock_funcs = &psb_clock_funcs, .output_init = psb_output_init, diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 88f44dbbc4eb..aed167af13c5 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -578,7 +578,6 @@ struct psb_ops { /* Sub functions */ struct drm_crtc_helper_funcs const *crtc_helper; - struct drm_crtc_funcs const *crtc_funcs; const struct gma_clock_funcs *clock_funcs; /* Setup hooks */ diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 6df62fe7c1e0..a99859b5b13a 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -488,8 +488,7 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe, return; } - /* Set the CRTC operations from the chip specific data */ - drm_crtc_init(dev, &gma_crtc->base, dev_priv->ops->crtc_funcs); + drm_crtc_init(dev, &gma_crtc->base, &gma_crtc_funcs); /* Set the CRTC clock functions from chip specific data */ gma_crtc->clock_funcs = dev_priv->ops->clock_funcs; -- cgit From 5d5fb74630c1896aa80f01a551d0a0e82e4d5765 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Thu, 17 Mar 2022 10:25:55 +0100 Subject: drm/gma500: Cosmetic cleanup of irq code Use the gma_ prefix instead of psb_ since the code is common for all chips. Various coding style fixes. Removal of unused code. Removal of duplicate function declarations. Signed-off-by: Patrik Jakobsson Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220317092555.17882-4-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/gma_display.c | 8 +-- drivers/gpu/drm/gma500/opregion.c | 5 +- drivers/gpu/drm/gma500/power.c | 10 ++-- drivers/gpu/drm/gma500/psb_drv.c | 2 +- drivers/gpu/drm/gma500/psb_drv.h | 11 ----- drivers/gpu/drm/gma500/psb_irq.c | 94 ++++++++++++++---------------------- drivers/gpu/drm/gma500/psb_irq.h | 19 ++++---- 7 files changed, 57 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 931ffb192fc4..1d7964c339f4 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -17,7 +17,7 @@ #include "framebuffer.h" #include "gem.h" #include "gma_display.h" -#include "psb_drv.h" +#include "psb_irq.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" @@ -572,9 +572,9 @@ const struct drm_crtc_funcs gma_crtc_funcs = { .set_config = gma_crtc_set_config, .destroy = gma_crtc_destroy, .page_flip = gma_crtc_page_flip, - .enable_vblank = psb_enable_vblank, - .disable_vblank = psb_disable_vblank, - .get_vblank_counter = psb_get_vblank_counter, + .enable_vblank = gma_enable_vblank, + .disable_vblank = gma_disable_vblank, + .get_vblank_counter = gma_get_vblank_counter, }; /* diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c index fef04ff8c3a9..dc494df71a48 100644 --- a/drivers/gpu/drm/gma500/opregion.c +++ b/drivers/gpu/drm/gma500/opregion.c @@ -23,6 +23,7 @@ */ #include #include "psb_drv.h" +#include "psb_irq.h" #include "psb_intel_reg.h" #define PCI_ASLE 0xe4 @@ -217,8 +218,8 @@ void psb_intel_opregion_enable_asle(struct drm_device *dev) if (asle && system_opregion ) { /* Don't do this on Medfield or other non PC like devices, they use the bit for something different altogether */ - psb_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); - psb_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); + gma_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); + gma_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | ASLE_PFMB_EN; diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c index 6f917cfef65b..b91de6d36e41 100644 --- a/drivers/gpu/drm/gma500/power.c +++ b/drivers/gpu/drm/gma500/power.c @@ -201,7 +201,7 @@ int gma_power_suspend(struct device *_dev) dev_err(dev->dev, "GPU hardware busy, cannot suspend\n"); return -EBUSY; } - psb_irq_uninstall(dev); + gma_irq_uninstall(dev); gma_suspend_display(dev); gma_suspend_pci(pdev); } @@ -223,8 +223,8 @@ int gma_power_resume(struct device *_dev) mutex_lock(&power_mutex); gma_resume_pci(pdev); gma_resume_display(pdev); - psb_irq_preinstall(dev); - psb_irq_postinstall(dev); + gma_irq_preinstall(dev); + gma_irq_postinstall(dev); mutex_unlock(&power_mutex); return 0; } @@ -270,8 +270,8 @@ bool gma_power_begin(struct drm_device *dev, bool force_on) /* Ok power up needed */ ret = gma_resume_pci(pdev); if (ret == 0) { - psb_irq_preinstall(dev); - psb_irq_postinstall(dev); + gma_irq_preinstall(dev); + gma_irq_postinstall(dev); pm_runtime_get(dev->dev); dev_priv->display_count++; spin_unlock_irqrestore(&power_ctrl_lock, flags); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index e30b58184156..82d51e9821ad 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -380,7 +380,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - psb_irq_install(dev, pdev->irq); + gma_irq_install(dev, pdev->irq); dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index aed167af13c5..0ddfec1a0851 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -609,17 +609,6 @@ struct psb_ops { int i2c_bus; /* I2C bus identifier for Moorestown */ }; -/* psb_irq.c */ -extern int psb_enable_vblank(struct drm_crtc *crtc); -extern void psb_disable_vblank(struct drm_crtc *crtc); -void -psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); - -void -psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); - -extern u32 psb_get_vblank_counter(struct drm_crtc *crtc); - /* psb_lid.c */ extern void psb_lid_timer_init(struct drm_psb_private *dev_priv); extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv); diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c index ccf402007beb..2e8ded532930 100644 --- a/drivers/gpu/drm/gma500/psb_irq.c +++ b/drivers/gpu/drm/gma500/psb_irq.c @@ -21,8 +21,7 @@ * inline functions */ -static inline u32 -psb_pipestat(int pipe) +static inline u32 gma_pipestat(int pipe) { if (pipe == 0) return PIPEASTAT; @@ -33,8 +32,7 @@ psb_pipestat(int pipe) BUG(); } -static inline u32 -mid_pipe_event(int pipe) +static inline u32 gma_pipe_event(int pipe) { if (pipe == 0) return _PSB_PIPEA_EVENT_FLAG; @@ -45,20 +43,7 @@ mid_pipe_event(int pipe) BUG(); } -static inline u32 -mid_pipe_vsync(int pipe) -{ - if (pipe == 0) - return _PSB_VSYNC_PIPEA_FLAG; - if (pipe == 1) - return _PSB_VSYNC_PIPEB_FLAG; - if (pipe == 2) - return _MDFLD_PIPEC_VBLANK_FLAG; - BUG(); -} - -static inline u32 -mid_pipeconf(int pipe) +static inline u32 gma_pipeconf(int pipe) { if (pipe == 0) return PIPEACONF; @@ -69,11 +54,10 @@ mid_pipeconf(int pipe) BUG(); } -void -psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) +void gma_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) { if ((dev_priv->pipestat[pipe] & mask) != mask) { - u32 reg = psb_pipestat(pipe); + u32 reg = gma_pipestat(pipe); dev_priv->pipestat[pipe] |= mask; /* Enable the interrupt, clear any pending status */ if (gma_power_begin(&dev_priv->dev, false)) { @@ -86,11 +70,10 @@ psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) } } -void -psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) +void gma_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) { if ((dev_priv->pipestat[pipe] & mask) != 0) { - u32 reg = psb_pipestat(pipe); + u32 reg = gma_pipestat(pipe); dev_priv->pipestat[pipe] &= ~mask; if (gma_power_begin(&dev_priv->dev, false)) { u32 writeVal = PSB_RVDC32(reg); @@ -105,12 +88,12 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) /* * Display controller interrupt handler for pipe event. */ -static void mid_pipe_event_handler(struct drm_device *dev, int pipe) +static void gma_pipe_event_handler(struct drm_device *dev, int pipe) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); uint32_t pipe_stat_val = 0; - uint32_t pipe_stat_reg = psb_pipestat(pipe); + uint32_t pipe_stat_reg = gma_pipestat(pipe); uint32_t pipe_enable = dev_priv->pipestat[pipe]; uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16; uint32_t pipe_clear; @@ -160,22 +143,22 @@ static void mid_pipe_event_handler(struct drm_device *dev, int pipe) /* * Display controller interrupt handler. */ -static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) +static void gma_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) { if (vdc_stat & _PSB_IRQ_ASLE) psb_intel_opregion_asle_intr(dev); if (vdc_stat & _PSB_VSYNC_PIPEA_FLAG) - mid_pipe_event_handler(dev, 0); + gma_pipe_event_handler(dev, 0); if (vdc_stat & _PSB_VSYNC_PIPEB_FLAG) - mid_pipe_event_handler(dev, 1); + gma_pipe_event_handler(dev, 1); } /* * SGX interrupt handler */ -static void psb_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2) +static void gma_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 val, addr; @@ -222,7 +205,7 @@ static void psb_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2) PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR2); } -static irqreturn_t psb_irq_handler(int irq, void *arg) +static irqreturn_t gma_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -246,14 +229,14 @@ static irqreturn_t psb_irq_handler(int irq, void *arg) spin_unlock(&dev_priv->irqmask_lock); if (dsp_int && gma_power_is_on(dev)) { - psb_vdc_interrupt(dev, vdc_stat); + gma_vdc_interrupt(dev, vdc_stat); handled = 1; } if (sgx_int) { sgx_stat_1 = PSB_RSGX32(PSB_CR_EVENT_STATUS); sgx_stat_2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); - psb_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2); + gma_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2); handled = 1; } @@ -274,7 +257,7 @@ static irqreturn_t psb_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -void psb_irq_preinstall(struct drm_device *dev) +void gma_irq_preinstall(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); unsigned long irqflags; @@ -303,7 +286,7 @@ void psb_irq_preinstall(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); } -void psb_irq_postinstall(struct drm_device *dev) +void gma_irq_postinstall(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); unsigned long irqflags; @@ -322,9 +305,9 @@ void psb_irq_postinstall(struct drm_device *dev) for (i = 0; i < dev->num_crtcs; ++i) { if (dev->vblank[i].enabled) - psb_enable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); + gma_enable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); else - psb_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); + gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); } if (dev_priv->ops->hotplug_enable) @@ -333,26 +316,26 @@ void psb_irq_postinstall(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); } -int psb_irq_install(struct drm_device *dev, unsigned int irq) +int gma_irq_install(struct drm_device *dev, unsigned int irq) { int ret; if (irq == IRQ_NOTCONNECTED) return -ENOTCONN; - psb_irq_preinstall(dev); + gma_irq_preinstall(dev); /* PCI devices require shared interrupts. */ - ret = request_irq(irq, psb_irq_handler, IRQF_SHARED, dev->driver->name, dev); + ret = request_irq(irq, gma_irq_handler, IRQF_SHARED, dev->driver->name, dev); if (ret) return ret; - psb_irq_postinstall(dev); + gma_irq_postinstall(dev); return 0; } -void psb_irq_uninstall(struct drm_device *dev) +void gma_irq_uninstall(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); @@ -368,7 +351,7 @@ void psb_irq_uninstall(struct drm_device *dev) for (i = 0; i < dev->num_crtcs; ++i) { if (dev->vblank[i].enabled) - psb_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); + gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); } dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG | @@ -388,17 +371,14 @@ void psb_irq_uninstall(struct drm_device *dev) free_irq(pdev->irq, dev); } -/* - * It is used to enable VBLANK interrupt - */ -int psb_enable_vblank(struct drm_crtc *crtc) +int gma_enable_vblank(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; unsigned int pipe = crtc->index; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); unsigned long irqflags; uint32_t reg_val = 0; - uint32_t pipeconf_reg = mid_pipeconf(pipe); + uint32_t pipeconf_reg = gma_pipeconf(pipe); if (gma_power_begin(dev, false)) { reg_val = REG_READ(pipeconf_reg); @@ -417,17 +397,14 @@ int psb_enable_vblank(struct drm_crtc *crtc) PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - psb_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); + gma_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); return 0; } -/* - * It is used to disable VBLANK interrupt - */ -void psb_disable_vblank(struct drm_crtc *crtc) +void gma_disable_vblank(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; unsigned int pipe = crtc->index; @@ -443,7 +420,7 @@ void psb_disable_vblank(struct drm_crtc *crtc) PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - psb_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); + gma_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); } @@ -451,7 +428,7 @@ void psb_disable_vblank(struct drm_crtc *crtc) /* Called from drm generic code, passed a 'crtc', which * we use as a pipe index */ -u32 psb_get_vblank_counter(struct drm_crtc *crtc) +u32 gma_get_vblank_counter(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; unsigned int pipe = crtc->index; @@ -486,8 +463,8 @@ u32 psb_get_vblank_counter(struct drm_crtc *crtc) if (!(reg_val & PIPEACONF_ENABLE)) { dev_err(dev->dev, "trying to get vblank count for disabled pipe %u\n", - pipe); - goto psb_get_vblank_counter_exit; + pipe); + goto err_gma_power_end; } /* @@ -506,8 +483,7 @@ u32 psb_get_vblank_counter(struct drm_crtc *crtc) count = (high1 << 8) | low; -psb_get_vblank_counter_exit: - +err_gma_power_end: gma_power_end(dev); return count; diff --git a/drivers/gpu/drm/gma500/psb_irq.h b/drivers/gpu/drm/gma500/psb_irq.h index a97cb49393d8..c22878914f5b 100644 --- a/drivers/gpu/drm/gma500/psb_irq.h +++ b/drivers/gpu/drm/gma500/psb_irq.h @@ -15,16 +15,15 @@ struct drm_crtc; struct drm_device; -bool sysirq_init(struct drm_device *dev); -void sysirq_uninit(struct drm_device *dev); +void gma_irq_preinstall(struct drm_device *dev); +void gma_irq_postinstall(struct drm_device *dev); +int gma_irq_install(struct drm_device *dev, unsigned int irq); +void gma_irq_uninstall(struct drm_device *dev); -void psb_irq_preinstall(struct drm_device *dev); -void psb_irq_postinstall(struct drm_device *dev); -int psb_irq_install(struct drm_device *dev, unsigned int irq); -void psb_irq_uninstall(struct drm_device *dev); - -int psb_enable_vblank(struct drm_crtc *crtc); -void psb_disable_vblank(struct drm_crtc *crtc); -u32 psb_get_vblank_counter(struct drm_crtc *crtc); +int gma_enable_vblank(struct drm_crtc *crtc); +void gma_disable_vblank(struct drm_crtc *crtc); +u32 gma_get_vblank_counter(struct drm_crtc *crtc); +void gma_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); +void gma_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); #endif /* _PSB_IRQ_H_ */ -- cgit From 61bfcb6a3b981e8f19e044ac8c3de6edbe6caf70 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Sun, 13 Mar 2022 20:29:51 +0100 Subject: fbdev: Fix sys_imageblit() for arbitrary image widths Commit 6f29e04938bf ("fbdev: Improve performance of sys_imageblit()") broke sys_imageblit() for image width that are not aligned to 8-bit boundaries. Fix this by handling the trailing pixels on each line separately. The performance improvements in the original commit do not regress by this change. Signed-off-by: Thomas Zimmermann Fixes: 6f29e04938bf ("fbdev: Improve performance of sys_imageblit()") Reviewed-by: Javier Martinez Canillas Acked-by: Daniel Vetter Tested-by: Geert Uytterhoeven Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220313192952.12058-2-tzimmermann@suse.de --- drivers/video/fbdev/core/sysimgblt.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/core/sysimgblt.c b/drivers/video/fbdev/core/sysimgblt.c index 722c327a381b..335e92b813fc 100644 --- a/drivers/video/fbdev/core/sysimgblt.c +++ b/drivers/video/fbdev/core/sysimgblt.c @@ -188,7 +188,7 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p, { u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel; u32 ppw = 32/bpp, spitch = (image->width + 7)/8; - u32 bit_mask, eorx; + u32 bit_mask, eorx, shift; const char *s = image->data, *src; u32 *dst; const u32 *tab; @@ -229,17 +229,23 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p, for (i = image->height; i--; ) { dst = dst1; + shift = 8; src = s; + /* + * Manually unroll the per-line copying loop for better + * performance. This works until we processed the last + * completely filled source byte (inclusive). + */ switch (ppw) { case 4: /* 8 bpp */ - for (j = k; j; j -= 2, ++src) { + for (j = k; j >= 2; j -= 2, ++src) { *dst++ = colortab[(*src >> 4) & bit_mask]; *dst++ = colortab[(*src >> 0) & bit_mask]; } break; case 2: /* 16 bpp */ - for (j = k; j; j -= 4, ++src) { + for (j = k; j >= 4; j -= 4, ++src) { *dst++ = colortab[(*src >> 6) & bit_mask]; *dst++ = colortab[(*src >> 4) & bit_mask]; *dst++ = colortab[(*src >> 2) & bit_mask]; @@ -247,7 +253,7 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p, } break; case 1: /* 32 bpp */ - for (j = k; j; j -= 8, ++src) { + for (j = k; j >= 8; j -= 8, ++src) { *dst++ = colortab[(*src >> 7) & bit_mask]; *dst++ = colortab[(*src >> 6) & bit_mask]; *dst++ = colortab[(*src >> 5) & bit_mask]; @@ -259,6 +265,21 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p, } break; } + + /* + * For image widths that are not a multiple of 8, there + * are trailing pixels left on the current line. Print + * them as well. + */ + for (; j--; ) { + shift -= ppw; + *dst++ = colortab[(*src >> shift) & bit_mask]; + if (!shift) { + shift = 8; + ++src; + } + } + dst1 += p->fix.line_length; s += spitch; } -- cgit From 7438f52b578afbac3807396531361aac85d43a06 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Sun, 13 Mar 2022 20:29:52 +0100 Subject: fbdev: Fix cfb_imageblit() for arbitrary image widths Commit 0d03011894d2 ("fbdev: Improve performance of cfb_imageblit()") broke cfb_imageblit() for image widths that are not aligned to 8-bit boundaries. Fix this by handling the trailing pixels on each line separately. The performance improvements in the original commit do not regress by this change. Signed-off-by: Thomas Zimmermann Fixes: 0d03011894d2 ("fbdev: Improve performance of cfb_imageblit()") Reviewed-by: Javier Martinez Canillas Acked-by: Daniel Vetter Tested-by: Marek Szyprowski Reported-by: Marek Szyprowski Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220313192952.12058-3-tzimmermann@suse.de --- drivers/video/fbdev/core/cfbimgblt.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c index 7361cfabdd85..9ebda4e0dc7a 100644 --- a/drivers/video/fbdev/core/cfbimgblt.c +++ b/drivers/video/fbdev/core/cfbimgblt.c @@ -218,7 +218,7 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * { u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel; u32 ppw = 32/bpp, spitch = (image->width + 7)/8; - u32 bit_mask, eorx; + u32 bit_mask, eorx, shift; const char *s = image->data, *src; u32 __iomem *dst; const u32 *tab = NULL; @@ -259,17 +259,23 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * for (i = image->height; i--; ) { dst = (u32 __iomem *)dst1; + shift = 8; src = s; + /* + * Manually unroll the per-line copying loop for better + * performance. This works until we processed the last + * completely filled source byte (inclusive). + */ switch (ppw) { case 4: /* 8 bpp */ - for (j = k; j; j -= 2, ++src) { + for (j = k; j >= 2; j -= 2, ++src) { FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++); FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++); } break; case 2: /* 16 bpp */ - for (j = k; j; j -= 4, ++src) { + for (j = k; j >= 4; j -= 4, ++src) { FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++); FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++); FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++); @@ -277,7 +283,7 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * } break; case 1: /* 32 bpp */ - for (j = k; j; j -= 8, ++src) { + for (j = k; j >= 8; j -= 8, ++src) { FB_WRITEL(colortab[(*src >> 7) & bit_mask], dst++); FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++); FB_WRITEL(colortab[(*src >> 5) & bit_mask], dst++); @@ -290,6 +296,20 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * break; } + /* + * For image widths that are not a multiple of 8, there + * are trailing pixels left on the current line. Print + * them as well. + */ + for (; j--; ) { + shift -= ppw; + FB_WRITEL(colortab[(*src >> shift) & bit_mask], dst++); + if (!shift) { + shift = 8; + ++src; + } + } + dst1 += p->fix.line_length; s += spitch; } -- cgit From daa83f744a31ca1c4f6cde7c4fcae0c89aa2dadc Mon Sep 17 00:00:00 2001 From: Christophe Branchereau Date: Fri, 11 Mar 2022 18:02:40 +0100 Subject: dt-bindings: display/panel: Add Leadtek ltk035c5444t Add binding for the leadtek ltk035c5444t, which is a 640x480 mipi-dbi over spi / 24-bit RGB panel based on the newvision NV03052C chipset. It is found in the Anbernic RG350M mips handheld. Signed-off-by: Christophe Branchereau Signed-off-by: Paul Cercueil Reviewed-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20220311170240.173846-5-cbranchereau@gmail.com --- .../display/panel/leadtek,ltk035c5444t.yaml | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/leadtek,ltk035c5444t.yaml diff --git a/Documentation/devicetree/bindings/display/panel/leadtek,ltk035c5444t.yaml b/Documentation/devicetree/bindings/display/panel/leadtek,ltk035c5444t.yaml new file mode 100644 index 000000000000..817a9bed7d5a --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/leadtek,ltk035c5444t.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/leadtek,ltk035c5444t.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Leadtek ltk035c5444t 3.5" (640x480 pixels) 24-bit IPS LCD panel + +maintainers: + - Paul Cercueil + - Christophe Branchereau + +allOf: + - $ref: panel-common.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: leadtek,ltk035c5444t + + backlight: true + port: true + power-supply: true + reg: true + reset-gpios: true + +required: + - compatible + - power-supply + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "leadtek,ltk035c5444t"; + reg = <0>; + + spi-3wire; + spi-max-frequency = <3125000>; + + reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&vcc>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; + }; -- cgit From 9f493fd71d4b132ed87e88b39cd46acf6b963062 Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Wed, 16 Mar 2022 23:05:49 +0530 Subject: drm/panel-edp: add LQ140M1JW46 edp panel entry Add panel identification entry for the sharp LQ140M1JW46 eDP panel with power sequencing delay information. Signed-off-by: Sankeerth Billakanti Reviewed-by: Stephen Boyd Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/1647452154-16361-5-git-send-email-quic_sbillaka@quicinc.com --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index f7bfcf63d48e..e15e62f235d8 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1859,6 +1859,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"), EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"), + EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &sharp_lq140m1jw46.delay, "LQ140M1JW46"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"), EDP_PANEL_ENTRY('S', 'T', 'A', 0x0100, &delay_100_500_e200, "2081116HHD028001-51D"), -- cgit From d14eb80e27795b7b20060f7b151cdfe39722a813 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 17 Mar 2022 23:55:37 +0100 Subject: drm/panel: ili9341: fix optional regulator handling If the optional regulator lookup fails, reset the pointer to NULL. Other functions such as mipi_dbi_poweron_reset_conditional() only do a NULL pointer check and will otherwise dereference the error pointer. Fixes: 5a04227326b04c15 ("drm/panel: Add ilitek ili9341 panel driver") Signed-off-by: Daniel Mack Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220317225537.826302-1-daniel@zonque.org --- drivers/gpu/drm/panel/panel-ilitek-ili9341.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c index 2c3378a259b1..e1542451ef9d 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c @@ -612,8 +612,10 @@ static int ili9341_dbi_probe(struct spi_device *spi, struct gpio_desc *dc, int ret; vcc = devm_regulator_get_optional(dev, "vcc"); - if (IS_ERR(vcc)) + if (IS_ERR(vcc)) { dev_err(dev, "get optional vcc failed\n"); + vcc = NULL; + } dbidev = devm_drm_dev_alloc(dev, &ili9341_dbi_driver, struct mipi_dbi_dev, drm); -- cgit From 856082f021a28221db2c32bd0531614a8382be67 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Fri, 18 Mar 2022 08:50:03 +0800 Subject: fbdev: defio: fix the pagelist corruption Easily hit the below list corruption: == list_add corruption. prev->next should be next (ffffffffc0ceb090), but was ffffec604507edc8. (prev=ffffec604507edc8). WARNING: CPU: 65 PID: 3959 at lib/list_debug.c:26 __list_add_valid+0x53/0x80 CPU: 65 PID: 3959 Comm: fbdev Tainted: G U RIP: 0010:__list_add_valid+0x53/0x80 Call Trace: fb_deferred_io_mkwrite+0xea/0x150 do_page_mkwrite+0x57/0xc0 do_wp_page+0x278/0x2f0 __handle_mm_fault+0xdc2/0x1590 handle_mm_fault+0xdd/0x2c0 do_user_addr_fault+0x1d3/0x650 exc_page_fault+0x77/0x180 ? asm_exc_page_fault+0x8/0x30 asm_exc_page_fault+0x1e/0x30 RIP: 0033:0x7fd98fc8fad1 == Figure out the race happens when one process is adding &page->lru into the pagelist tail in fb_deferred_io_mkwrite(), another process is re-initializing the same &page->lru in fb_deferred_io_fault(), which is not protected by the lock. This fix is to init all the page lists one time during initialization, it not only fixes the list corruption, but also avoids INIT_LIST_HEAD() redundantly. V2: change "int i" to "unsigned int i" (Geert Uytterhoeven) Signed-off-by: Chuansheng Liu Fixes: 105a940416fc ("fbdev/defio: Early-out if page is already enlisted") Cc: Thomas Zimmermann Cc: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220318005003.51810-1-chuansheng.liu@intel.com --- drivers/video/fbdev/core/fb_defio.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 98b0f23bf5e2..a1da54016c88 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -59,7 +59,6 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) printk(KERN_ERR "no mapping available\n"); BUG_ON(!page->mapping); - INIT_LIST_HEAD(&page->lru); page->index = vmf->pgoff; vmf->page = page; @@ -220,6 +219,8 @@ static void fb_deferred_io_work(struct work_struct *work) void fb_deferred_io_init(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; + struct page *page; + unsigned int i; BUG_ON(!fbdefio); mutex_init(&fbdefio->lock); @@ -227,6 +228,12 @@ void fb_deferred_io_init(struct fb_info *info) INIT_LIST_HEAD(&fbdefio->pagelist); if (fbdefio->delay == 0) /* set a default of 1 s */ fbdefio->delay = HZ; + + /* initialize all the page lists one time */ + for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) { + page = fb_deferred_io_page(info, i); + INIT_LIST_HEAD(&page->lru); + } } EXPORT_SYMBOL_GPL(fb_deferred_io_init); -- cgit From 10a26e0d5fc3574f63ce8a6cf28381b126317f40 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Fri, 18 Mar 2022 13:43:28 -0400 Subject: drm/vmwgfx: Fix an invalid read vmw_move assumed that buffers to be moved would always be vmw_buffer_object's but after introduction of new placement for mob pages that's no longer the case. The resulting invalid read didn't have any practical consequences because the memory isn't used unless the object actually is a vmw_buffer_object. Fix it by moving the cast to the spot where the results are used. Signed-off-by: Zack Rusin Fixes: f6be23264bba ("drm/vmwgfx: Introduce a new placement for MOB page tables") Reported-by: Chuck Lever III Reviewed-by: Martin Krastev Tested-by: Chuck Lever Link: https://patchwork.freedesktop.org/patch/msgid/20220318174332.440068-2-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 708899ba2102..6542f1498651 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -859,22 +859,21 @@ void vmw_query_move_notify(struct ttm_buffer_object *bo, struct ttm_device *bdev = bo->bdev; struct vmw_private *dev_priv; - dev_priv = container_of(bdev, struct vmw_private, bdev); mutex_lock(&dev_priv->binding_mutex); - dx_query_mob = container_of(bo, struct vmw_buffer_object, base); - if (!dx_query_mob || !dx_query_mob->dx_query_ctx) { - mutex_unlock(&dev_priv->binding_mutex); - return; - } - /* If BO is being moved from MOB to system memory */ if (new_mem->mem_type == TTM_PL_SYSTEM && old_mem->mem_type == VMW_PL_MOB) { struct vmw_fence_obj *fence; + dx_query_mob = container_of(bo, struct vmw_buffer_object, base); + if (!dx_query_mob || !dx_query_mob->dx_query_ctx) { + mutex_unlock(&dev_priv->binding_mutex); + return; + } + (void) vmw_query_readback_all(dx_query_mob); mutex_unlock(&dev_priv->binding_mutex); @@ -888,7 +887,6 @@ void vmw_query_move_notify(struct ttm_buffer_object *bo, (void) ttm_bo_wait(bo, false, false); } else mutex_unlock(&dev_priv->binding_mutex); - } /** -- cgit From ab709795e38637320fea9fdc8e06b64f670b58ee Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Fri, 18 Mar 2022 13:43:29 -0400 Subject: drm/vmwgfx: Fix mob cursor allocation race Writes to SVGA_REG_CURSOR_MOBID did not wait for the buffers to be fully populated. This sometimes results in the device not being aware of the buffer when the cursor mob register was written. Properly wait for the buffer to be fully populated before setting it as a cursor mob. Signed-off-by: Zack Rusin Fixes: 485d98d472d5 ("drm/vmwgfx: Add support for CursorMob and CursorBypass 4") Reviewed-by: Martin Krastev Link: https://patchwork.freedesktop.org/patch/msgid/20220318174332.440068-3-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index af252210ef84..7a23f252d212 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -602,6 +602,14 @@ vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, ret = ttm_bo_kmap(cm_bo, 0, PFN_UP(size), &vps->cm_map); + /* + * We just want to try to get mob bind to finish + * so that the first write to SVGA_REG_CURSOR_MOBID + * is done with a buffer that the device has already + * seen + */ + (void) ttm_bo_wait(cm_bo, false, false); + ttm_bo_unreserve(cm_bo); if (unlikely(ret != 0)) { -- cgit From 8bb75aeb58bd688d70827ae179bd3da57b6d975b Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Fri, 18 Mar 2022 13:43:30 -0400 Subject: drm/vmwgfx: validate the screen formats The kms code wasn't validating the modifiers and was letting through unsupported formats. rgb8 was never properly supported and has no matching svga screen target format so remove it. This fixes format/modifier failures in kms_addfb_basic from IGT. Signed-off-by: Zack Rusin Reviewed-by: Martin Krastev Link: https://patchwork.freedesktop.org/patch/msgid/20220318174332.440068-4-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 30 ++++++++++++++++-------------- drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 1 - 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 7a23f252d212..693028c31b6b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1171,6 +1171,15 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, * Sanity checks. */ + if (!drm_any_plane_has_format(&dev_priv->drm, + mode_cmd->pixel_format, + mode_cmd->modifier[0])) { + drm_dbg(&dev_priv->drm, + "unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); + return -EINVAL; + } + /* Surface must be marked as a scanout. */ if (unlikely(!surface->metadata.scanout)) return -EINVAL; @@ -1493,20 +1502,13 @@ static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, return -EINVAL; } - /* Limited framebuffer color depth support for screen objects */ - if (dev_priv->active_display_unit == vmw_du_screen_object) { - switch (mode_cmd->pixel_format) { - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_ARGB8888: - break; - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_RGB565: - break; - default: - DRM_ERROR("Invalid pixel format: %p4cc\n", - &mode_cmd->pixel_format); - return -EINVAL; - } + if (!drm_any_plane_has_format(&dev_priv->drm, + mode_cmd->pixel_format, + mode_cmd->modifier[0])) { + drm_dbg(&dev_priv->drm, + "unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); + return -EINVAL; } vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index c95be95deb8d..1d1c8b82c898 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -247,7 +247,6 @@ struct vmw_framebuffer_bo { static const uint32_t __maybe_unused vmw_primary_plane_formats[] = { DRM_FORMAT_XRGB1555, DRM_FORMAT_RGB565, - DRM_FORMAT_RGB888, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, }; -- cgit From 6f6f9788d821ee39c8a28e389a35027d6a5f143b Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Fri, 18 Mar 2022 13:43:31 -0400 Subject: drm/vmwgfx: Disable command buffers on svga3 without gbobjects With very limited vram on svga3 it's difficult to handle all the surface migrations. Without gbobjects, i.e. the ability to store surfaces in guest mobs, there's no reason to support intermediate svga2 features, especially because we can fall back to fb traces and svga3 will never support those in-between features. On svga3 we wither want to use fb traces or screen targets (i.e. gbobjects), nothing in between. This fixes presentation on a lot of fusion/esxi tech previews where the exposed svga3 caps haven't been finalized yet. Signed-off-by: Zack Rusin Fixes: 2cd80dbd3551 ("drm/vmwgfx: Add basic support for SVGA3") Cc: # v5.14+ Reviewed-by: Martin Krastev Link: https://patchwork.freedesktop.org/patch/msgid/20220318174332.440068-5-zack@kde.org --- drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c index bf1b394753da..162dfeb1cc5a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c @@ -675,11 +675,14 @@ int vmw_cmd_emit_dummy_query(struct vmw_private *dev_priv, */ bool vmw_cmd_supported(struct vmw_private *vmw) { - if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | - SVGA_CAP_CMD_BUFFERS_2)) != 0) - return true; + bool has_cmdbufs = + (vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | + SVGA_CAP_CMD_BUFFERS_2)) != 0; + if (vmw_is_svga_v3(vmw)) + return (has_cmdbufs && + (vmw->capabilities & SVGA_CAP_GBOBJECTS) != 0); /* * We have FIFO cmd's */ - return vmw->fifo_mem != NULL; + return has_cmdbufs || vmw->fifo_mem != NULL; } -- cgit From cc436b1c674e4247b4d4873f2236039f697f4e57 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Feb 2022 12:03:48 +0200 Subject: drm/gma500: Use drm_mode_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct drm_display_mode embeds a list head, so overwriting the full struct with another one will corrupt the list (if the destination mode is on a list). Use drm_mode_copy() instead which explicitly preserves the list head of the destination mode. Even if we know the destination mode is not on any list using drm_mode_copy() seems decent as it sets a good example. Bad examples of not using it might eventually get copied into code where preserving the list head actually matters. Obviously one case not covered here is when the mode itself is embedded in a larger structure and the whole structure is copied. But if we are careful when copying into modes embedded in structures I think we can be a little more reassured that bogus list heads haven't been propagated in. @is_mode_copy@ @@ drm_mode_copy(...) { ... } @depends on !is_mode_copy@ struct drm_display_mode *mode; expression E, S; @@ ( - *mode = E + drm_mode_copy(mode, &E) | - memcpy(mode, E, S) + drm_mode_copy(mode, E) ) @depends on !is_mode_copy@ struct drm_display_mode mode; expression E; @@ ( - mode = E + drm_mode_copy(&mode, &E) | - memcpy(&mode, E, S) + drm_mode_copy(&mode, E) ) @@ struct drm_display_mode *mode; @@ - &*mode + mode Cc: Patrik Jakobsson Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220218100403.7028-8-ville.syrjala@linux.intel.com Acked-by: Patrik Jakobsson --- drivers/gpu/drm/gma500/oaktrail_crtc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index 36c7c2686c90..79fc602b35bc 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -385,12 +385,8 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, if (!gma_power_begin(dev, true)) return 0; - memcpy(&gma_crtc->saved_mode, - mode, - sizeof(struct drm_display_mode)); - memcpy(&gma_crtc->saved_adjusted_mode, - adjusted_mode, - sizeof(struct drm_display_mode)); + drm_mode_copy(&gma_crtc->saved_mode, mode); + drm_mode_copy(&gma_crtc->saved_adjusted_mode, adjusted_mode); list_for_each_entry(connector, &mode_config->connector_list, head) { if (!connector->encoder || connector->encoder->crtc != crtc) -- cgit From 6185587b6675e1852c49235e00fb76f7d81ff0d9 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Feb 2022 12:03:57 +0200 Subject: drm/tilcdc: Use drm_mode_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct drm_display_mode embeds a list head, so overwriting the full struct with another one will corrupt the list (if the destination mode is on a list). Use drm_mode_copy() instead which explicitly preserves the list head of the destination mode. Even if we know the destination mode is not on any list using drm_mode_copy() seems decent as it sets a good example. Bad examples of not using it might eventually get copied into code where preserving the list head actually matters. Obviously one case not covered here is when the mode itself is embedded in a larger structure and the whole structure is copied. But if we are careful when copying into modes embedded in structures I think we can be a little more reassured that bogus list heads haven't been propagated in. @is_mode_copy@ @@ drm_mode_copy(...) { ... } @depends on !is_mode_copy@ struct drm_display_mode *mode; expression E, S; @@ ( - *mode = E + drm_mode_copy(mode, &E) | - memcpy(mode, E, S) + drm_mode_copy(mode, E) ) @depends on !is_mode_copy@ struct drm_display_mode mode; expression E; @@ ( - mode = E + drm_mode_copy(&mode, &E) | - memcpy(&mode, E, S) + drm_mode_copy(&mode, E) ) @@ struct drm_display_mode *mode; @@ - &*mode + mode Cc: Jyri Sarha Cc: Tomi Valkeinen Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220218100403.7028-17-ville.syrjala@linux.intel.com Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 29890d704cb4..853c6b443fff 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -433,7 +433,7 @@ static void tilcdc_crtc_set_mode(struct drm_crtc *crtc) set_scanout(crtc, fb); - crtc->hwmode = crtc->state->adjusted_mode; + drm_mode_copy(&crtc->hwmode, &crtc->state->adjusted_mode); tilcdc_crtc->hvtotal_us = tilcdc_mode_hvtotal(&crtc->hwmode); -- cgit From c5c948aa894a831f96fccd025e47186b1ee41615 Mon Sep 17 00:00:00 2001 From: Aurabindo Pillai Date: Tue, 15 Mar 2022 14:53:24 -0400 Subject: drm/amd: Add USBC connector ID [Why&How] Add a dedicated AMDGPU specific ID for use with newer ASICs that support USB-C output Signed-off-by: Aurabindo Pillai Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/ObjectID.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/ObjectID.h b/drivers/gpu/drm/amd/amdgpu/ObjectID.h index 5b393622f592..a0f0a17e224f 100644 --- a/drivers/gpu/drm/amd/amdgpu/ObjectID.h +++ b/drivers/gpu/drm/amd/amdgpu/ObjectID.h @@ -119,6 +119,7 @@ #define CONNECTOR_OBJECT_ID_eDP 0x14 #define CONNECTOR_OBJECT_ID_MXM 0x15 #define CONNECTOR_OBJECT_ID_LVDS_eDP 0x16 +#define CONNECTOR_OBJECT_ID_USBC 0x17 /* deleted */ -- cgit From c89717bd0a0f4fd35e86ea26072de608482a7710 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:36 +0100 Subject: drm/gma500: Use managed drmm_mode_config_init() Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-1-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/framebuffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 45df9de22007..2b99c996fdc2 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -514,7 +514,8 @@ void psb_modeset_init(struct drm_device *dev) struct pci_dev *pdev = to_pci_dev(dev->dev); int i; - drm_mode_config_init(dev); + if (drmm_mode_config_init(dev)) + return; dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; @@ -546,6 +547,5 @@ void psb_modeset_cleanup(struct drm_device *dev) if (dev_priv->modeset) { drm_kms_helper_poll_fini(dev); psb_fbdev_fini(dev); - drm_mode_config_cleanup(dev); } } -- cgit From 3e712b3cfb580752e5d0f848883bf2d074f09bdd Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:37 +0100 Subject: drm/gma500: Uninstall interrupts on driver removal Reloading the driver revealed that the interrupt handler never got uninstalled. Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-2-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/psb_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 82d51e9821ad..b231fddb8817 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -173,6 +173,8 @@ static void psb_driver_unload(struct drm_device *dev) gma_backlight_exit(dev); psb_modeset_cleanup(dev); + gma_irq_uninstall(dev); + if (dev_priv->ops->chip_teardown) dev_priv->ops->chip_teardown(dev); -- cgit From b1a7d0ddb169774c3db5afe9e64124daea7fdd9f Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:38 +0100 Subject: drm/gma500: Make use of the drm connector iterator This makes sure we're using proper locking when iterating the list of connectors. Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-3-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/cdv_device.c | 10 ++++++++-- drivers/gpu/drm/gma500/cdv_intel_display.c | 9 +++++++-- drivers/gpu/drm/gma500/framebuffer.c | 6 ++++-- drivers/gpu/drm/gma500/gma_display.c | 16 ++++++++++------ drivers/gpu/drm/gma500/oaktrail_crtc.c | 17 +++++++++++------ drivers/gpu/drm/gma500/oaktrail_lvds.c | 15 ++++++++------- drivers/gpu/drm/gma500/psb_device.c | 28 ++++++++++++++++++++-------- drivers/gpu/drm/gma500/psb_drv.c | 10 ++++++---- drivers/gpu/drm/gma500/psb_intel_display.c | 15 ++++++++++----- 9 files changed, 84 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index f854f58bcbb3..dd32b484dd82 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -262,6 +262,7 @@ static int cdv_save_display_registers(struct drm_device *dev) struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); struct psb_save_area *regs = &dev_priv->regs; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; dev_dbg(dev->dev, "Saving GPU registers.\n"); @@ -298,8 +299,10 @@ static int cdv_save_display_registers(struct drm_device *dev) regs->cdv.saveIER = REG_READ(PSB_INT_ENABLE_R); regs->cdv.saveIMR = REG_READ(PSB_INT_MASK_R); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF); + drm_connector_list_iter_end(&conn_iter); return 0; } @@ -317,6 +320,7 @@ static int cdv_restore_display_registers(struct drm_device *dev) struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); struct psb_save_area *regs = &dev_priv->regs; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; u32 temp; @@ -373,8 +377,10 @@ static int cdv_restore_display_registers(struct drm_device *dev) drm_mode_config_reset(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) connector->funcs->dpms(connector, DRM_MODE_DPMS_ON); + drm_connector_list_iter_end(&conn_iter); /* Resume the modeset for every activated CRTC */ drm_helper_resume_force_mode(dev); diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 94ebc48a4349..0c3ddcdc29dc 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -584,13 +584,14 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, bool ok; bool is_lvds = false; bool is_dp = false; - struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; const struct gma_limit_t *limit; u32 ddi_select = 0; bool is_edp = false; - list_for_each_entry(connector, &mode_config->connector_list, head) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { struct gma_encoder *gma_encoder = gma_attached_encoder(connector); @@ -613,10 +614,14 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, is_edp = true; break; default: + drm_connector_list_iter_end(&conn_iter); DRM_ERROR("invalid output type.\n"); return 0; } + + break; } + drm_connector_list_iter_end(&conn_iter); if (dev_priv->dplla_96mhz) /* low-end sku, 96/100 mhz */ diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 2b99c996fdc2..0ac6ea5fd3a1 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -451,6 +451,7 @@ static const struct drm_mode_config_funcs psb_mode_funcs = { static void psb_setup_outputs(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; drm_mode_create_scaling_mode_property(dev); @@ -461,8 +462,8 @@ static void psb_setup_outputs(struct drm_device *dev) "backlight", 0, 100); dev_priv->ops->output_init(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct drm_encoder *encoder = &gma_encoder->base; int crtc_mask = 0, clone_mask = 0; @@ -505,6 +506,7 @@ static void psb_setup_outputs(struct drm_device *dev) encoder->possible_clones = gma_connector_clones(dev, clone_mask); } + drm_connector_list_iter_end(&conn_iter); } void psb_modeset_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 1d7964c339f4..e8157464d9eb 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -27,17 +27,21 @@ bool gma_pipe_has_type(struct drm_crtc *crtc, int type) { struct drm_device *dev = crtc->dev; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_connector *l_entry; + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; - list_for_each_entry(l_entry, &mode_config->connector_list, head) { - if (l_entry->encoder && l_entry->encoder->crtc == crtc) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->encoder && connector->encoder->crtc == crtc) { struct gma_encoder *gma_encoder = - gma_attached_encoder(l_entry); - if (gma_encoder->type == type) + gma_attached_encoder(connector); + if (gma_encoder->type == type) { + drm_connector_list_iter_end(&conn_iter); return true; + } } } + drm_connector_list_iter_end(&conn_iter); return false; } diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index 79fc602b35bc..22398d34853a 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -372,9 +372,9 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, bool ok, is_sdvo = false; bool is_lvds = false; bool is_mipi = false; - struct drm_mode_config *mode_config = &dev->mode_config; struct gma_encoder *gma_encoder = NULL; uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; int i; int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0; @@ -388,7 +388,8 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, drm_mode_copy(&gma_crtc->saved_mode, mode); drm_mode_copy(&gma_crtc->saved_adjusted_mode, adjusted_mode); - list_for_each_entry(connector, &mode_config->connector_list, head) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (!connector->encoder || connector->encoder->crtc != crtc) continue; @@ -405,8 +406,16 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, is_mipi = true; break; } + + break; } + if (gma_encoder) + drm_object_property_get_value(&connector->base, + dev->mode_config.scaling_mode_property, &scalingType); + + drm_connector_list_iter_end(&conn_iter); + /* Disable the VGA plane that we never use */ for (i = 0; i <= need_aux; i++) REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i); @@ -420,10 +429,6 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, (mode->crtc_vdisplay - 1), i); } - if (gma_encoder) - drm_object_property_get_value(&connector->base, - dev->mode_config.scaling_mode_property, &scalingType); - if (scalingType == DRM_MODE_SCALE_NO_SCALE) { /* Moorestown doesn't have register support for centering so * we need to mess with the h/vblank and h/vsync start and diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 28b995ef2844..04852dbc7fb3 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -85,7 +85,7 @@ static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; - struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector = NULL; struct drm_crtc *crtc = encoder->crtc; u32 lvds_port; @@ -112,21 +112,22 @@ static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, REG_WRITE(LVDS, lvds_port); /* Find the connector we're trying to set up */ - list_for_each_entry(connector, &mode_config->connector_list, head) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->encoder && connector->encoder->crtc == crtc) break; } - if (list_entry_is_head(connector, &mode_config->connector_list, head)) { + if (!connector) { + drm_connector_list_iter_end(&conn_iter); DRM_ERROR("Couldn't find connector when setting mode"); gma_power_end(dev); return; } - drm_object_property_get_value( - &connector->base, - dev->mode_config.scaling_mode_property, - &v); + drm_object_property_get_value( &connector->base, + dev->mode_config.scaling_mode_property, &v); + drm_connector_list_iter_end(&conn_iter); if (v == DRM_MODE_SCALE_NO_SCALE) REG_WRITE(PFIT_CONTROL, 0); diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index 59f325165667..71534f4ca834 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -168,8 +168,10 @@ static void psb_init_pm(struct drm_device *dev) static int psb_save_display_registers(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct gma_connector *gma_connector; struct drm_crtc *crtc; - struct gma_connector *connector; + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; struct psb_state *regs = &dev_priv->regs.psb; /* Display arbitration control + watermarks */ @@ -189,9 +191,13 @@ static int psb_save_display_registers(struct drm_device *dev) dev_priv->ops->save_crtc(crtc); } - list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) - if (connector->save) - connector->save(&connector->base); + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + gma_connector = to_gma_connector(connector); + if (gma_connector->save) + gma_connector->save(connector); + } + drm_connector_list_iter_end(&conn_iter); drm_modeset_unlock_all(dev); return 0; @@ -206,8 +212,10 @@ static int psb_save_display_registers(struct drm_device *dev) static int psb_restore_display_registers(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct gma_connector *gma_connector; struct drm_crtc *crtc; - struct gma_connector *connector; + struct drm_connector_list_iter conn_iter; + struct drm_connector *connector; struct psb_state *regs = &dev_priv->regs.psb; /* Display arbitration + watermarks */ @@ -228,9 +236,13 @@ static int psb_restore_display_registers(struct drm_device *dev) if (drm_helper_crtc_in_use(crtc)) dev_priv->ops->restore_crtc(crtc); - list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) - if (connector->restore) - connector->restore(&connector->base); + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + gma_connector = to_gma_connector(connector); + if (gma_connector->restore) + gma_connector->restore(connector); + } + drm_connector_list_iter_end(&conn_iter); drm_modeset_unlock_all(dev); return 0; diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index b231fddb8817..bb0e3288e35b 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -236,10 +236,11 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) struct drm_psb_private *dev_priv = to_drm_psb_private(dev); unsigned long resource_start, resource_len; unsigned long irqflags; - int ret = -ENOMEM; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; struct gma_encoder *gma_encoder; struct psb_gtt *pg; + int ret = -ENOMEM; /* initializing driver private data */ @@ -390,9 +391,9 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) psb_fbdev_init(dev); drm_kms_helper_poll_init(dev); - /* Only add backlight support if we have LVDS output */ - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { + /* Only add backlight support if we have LVDS or MIPI output */ + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { gma_encoder = gma_attached_encoder(connector); switch (gma_encoder->type) { @@ -402,6 +403,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) break; } } + drm_connector_list_iter_end(&conn_iter); if (ret) return ret; diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index a99859b5b13a..fb8234f4d128 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -106,7 +106,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, u32 dpll = 0, fp = 0, dspcntr, pipeconf; bool ok, is_sdvo = false; bool is_lvds = false, is_tv = false; - struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; const struct gma_limit_t *limit; @@ -116,7 +116,8 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, return 0; } - list_for_each_entry(connector, &mode_config->connector_list, head) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { struct gma_encoder *gma_encoder = gma_attached_encoder(connector); if (!connector->encoder @@ -135,6 +136,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, break; } } + drm_connector_list_iter_end(&conn_iter); refclk = 96000; @@ -534,16 +536,19 @@ struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) int gma_connector_clones(struct drm_device *dev, int type_mask) { - int index_mask = 0; + struct drm_connector_list_iter conn_iter; struct drm_connector *connector; + int index_mask = 0; int entry = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { struct gma_encoder *gma_encoder = gma_attached_encoder(connector); if (type_mask & (1 << gma_encoder->type)) index_mask |= (1 << entry); entry++; } + drm_connector_list_iter_end(&conn_iter); + return index_mask; } -- cgit From 9f8308f4c94afc7486ea4ad41298031af0b91a92 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:39 +0100 Subject: drm/gma500: gma500 don't register non-hotpluggable connectors According to docs we should only register connectors that are hotpluggable. No connectors in gma500 are hotpluggable. Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-4-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/cdv_intel_crt.c | 3 --- drivers/gpu/drm/gma500/cdv_intel_dp.c | 3 --- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 2 -- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 2 -- drivers/gpu/drm/gma500/oaktrail_hdmi.c | 1 - drivers/gpu/drm/gma500/oaktrail_lvds.c | 1 - drivers/gpu/drm/gma500/psb_intel_lvds.c | 2 -- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 2 -- 8 files changed, 16 deletions(-) diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index 4a9bb4994a26..1ae0fbbda0eb 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -194,7 +194,6 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector) struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->ddc_bus); - drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -281,8 +280,6 @@ void cdv_intel_crt_init(struct drm_device *dev, drm_connector_helper_add(connector, &cdv_intel_crt_connector_helper_funcs); - drm_connector_register(connector); - return; failed_ddc: drm_encoder_cleanup(&gma_encoder->base); diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index f562e91337c7..3fba9d4e785d 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1866,7 +1866,6 @@ cdv_intel_dp_destroy(struct drm_connector *connector) intel_dp->panel_fixed_mode = NULL; } i2c_del_adapter(&intel_dp->adapter); - drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -1990,8 +1989,6 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_connector_register(connector); - /* Set up the DDC bus. */ switch (output_reg) { case DP_B: diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index e525689f84f0..e0d4c49b3c92 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -245,7 +245,6 @@ static void cdv_hdmi_destroy(struct drm_connector *connector) struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->i2c_bus); - drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -352,7 +351,6 @@ void cdv_hdmi_init(struct drm_device *dev, hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter); hdmi_priv->dev = dev; - drm_connector_register(connector); return; failed_ddc: diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 9e1cdb11023c..851a3cc4653e 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -329,7 +329,6 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector) struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->i2c_bus); - drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -647,7 +646,6 @@ void cdv_intel_lvds_init(struct drm_device *dev, out: mutex_unlock(&dev->mode_config.mutex); - drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index 6eef60a5ac27..b5946a1cdcd5 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -654,7 +654,6 @@ void oaktrail_hdmi_init(struct drm_device *dev, connector->display_info.subpixel_order = SubPixelHorizontalRGB; connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_connector_register(connector); dev_info(dev->dev, "HDMI initialised.\n"); return; diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 04852dbc7fb3..aed5de8f8245 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -401,7 +401,6 @@ void oaktrail_lvds_init(struct drm_device *dev, out: mutex_unlock(&dev->mode_config.mutex); - drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index ac97e0d3c7dd..ec8f0b504ccc 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -525,7 +525,6 @@ void psb_intel_lvds_destroy(struct drm_connector *connector) struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv; psb_intel_i2c_destroy(lvds_priv->ddc_bus); - drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -782,7 +781,6 @@ void psb_intel_lvds_init(struct drm_device *dev, */ out: mutex_unlock(&dev->mode_config.mutex); - drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 042c4392e676..5b72a759a182 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1542,7 +1542,6 @@ static int psb_intel_sdvo_get_modes(struct drm_connector *connector) static void psb_intel_sdvo_destroy(struct drm_connector *connector) { - drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -1932,7 +1931,6 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector, connector->base.restore = psb_intel_sdvo_restore; gma_connector_attach_encoder(&connector->base, &encoder->base); - drm_connector_register(&connector->base.base); } static void -- cgit From 12e67cca85a06b1227b0fd797d436ddffadd01a8 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:40 +0100 Subject: drm/gma500: Free the correct connector allocation The allocation is made for the gma_connector object so we must use the same address when free()ing the object. Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-5-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/cdv_intel_crt.c | 3 ++- drivers/gpu/drm/gma500/cdv_intel_dp.c | 3 ++- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 3 ++- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 3 ++- drivers/gpu/drm/gma500/psb_intel_lvds.c | 3 ++- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 4 +++- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index 1ae0fbbda0eb..6bcd18c63c31 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -191,11 +191,12 @@ static enum drm_connector_status cdv_intel_crt_detect( static void cdv_intel_crt_destroy(struct drm_connector *connector) { + struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->ddc_bus); drm_connector_cleanup(connector); - kfree(connector); + kfree(gma_connector); } static int cdv_intel_crt_get_modes(struct drm_connector *connector) diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 3fba9d4e785d..72b1b2fc3c27 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1857,6 +1857,7 @@ done: static void cdv_intel_dp_destroy(struct drm_connector *connector) { + struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct cdv_intel_dp *intel_dp = gma_encoder->dev_priv; @@ -1867,7 +1868,7 @@ cdv_intel_dp_destroy(struct drm_connector *connector) } i2c_del_adapter(&intel_dp->adapter); drm_connector_cleanup(connector); - kfree(connector); + kfree(gma_connector); } static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = { diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index e0d4c49b3c92..8987e555e113 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -242,11 +242,12 @@ static enum drm_mode_status cdv_hdmi_mode_valid(struct drm_connector *connector, static void cdv_hdmi_destroy(struct drm_connector *connector) { + struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->i2c_bus); drm_connector_cleanup(connector); - kfree(connector); + kfree(gma_connector); } static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = { diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 851a3cc4653e..98d9f5483a7c 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -326,11 +326,12 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) */ static void cdv_intel_lvds_destroy(struct drm_connector *connector) { + struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->i2c_bus); drm_connector_cleanup(connector); - kfree(connector); + kfree(gma_connector); } static int cdv_intel_lvds_set_property(struct drm_connector *connector, diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index ec8f0b504ccc..cad00380b386 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -521,12 +521,13 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector) */ void psb_intel_lvds_destroy(struct drm_connector *connector) { + struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv; psb_intel_i2c_destroy(lvds_priv->ddc_bus); drm_connector_cleanup(connector); - kfree(connector); + kfree(gma_connector); } int psb_intel_lvds_set_property(struct drm_connector *connector, diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 5b72a759a182..a85aace25548 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1542,8 +1542,10 @@ static int psb_intel_sdvo_get_modes(struct drm_connector *connector) static void psb_intel_sdvo_destroy(struct drm_connector *connector) { + struct gma_connector *gma_connector = to_gma_connector(connector); + drm_connector_cleanup(connector); - kfree(connector); + kfree(gma_connector); } static bool psb_intel_sdvo_detect_hdmi_audio(struct drm_connector *connector) -- cgit From d9f6e81272de653812e8b2aa77c93f16939e02e9 Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:41 +0100 Subject: drm/gma500: Declare a few functions static These functions are not used outside of their file scope so can be declared as static. Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-6-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/gma_display.c | 15 +++++++-------- drivers/gpu/drm/gma500/psb_drv.c | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index e8157464d9eb..369bc1f751cb 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -176,9 +176,9 @@ void gma_crtc_load_lut(struct drm_crtc *crtc) } } -int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - u32 size, - struct drm_modeset_acquire_ctx *ctx) +static int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, u32 size, + struct drm_modeset_acquire_ctx *ctx) { gma_crtc_load_lut(crtc); @@ -323,10 +323,9 @@ void gma_crtc_dpms(struct drm_crtc *crtc, int mode) REG_WRITE(DSPARB, 0x3F3E); } -int gma_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height) +static int gma_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, uint32_t handle, + uint32_t width, uint32_t height) { struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -439,7 +438,7 @@ unref_cursor: return ret; } -int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +static int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct drm_device *dev = crtc->dev; struct gma_crtc *gma_crtc = to_gma_crtc(crtc); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index bb0e3288e35b..2aff54d505e2 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -100,7 +100,7 @@ static const struct drm_ioctl_desc psb_ioctls[] = { * * Soft reset the graphics engine and then reload the necessary registers. */ -void psb_spank(struct drm_psb_private *dev_priv) +static void psb_spank(struct drm_psb_private *dev_priv) { PSB_WSGX32(_PSB_CS_RESET_BIF_RESET | _PSB_CS_RESET_DPM_RESET | _PSB_CS_RESET_TA_RESET | _PSB_CS_RESET_USE_RESET | -- cgit From 52e527f28776e4a8188fed0fe092d9f4d5e39f1c Mon Sep 17 00:00:00 2001 From: Patrik Jakobsson Date: Tue, 22 Mar 2022 14:17:42 +0100 Subject: drm/gma500: Add crtc prefix to vblank functions These functions operate on a crtc and should be prefixed properly. Suggested-by: Thomas Zimmermann Signed-off-by: Patrik Jakobsson Reviewed-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220322131742.11566-7-patrik.r.jakobsson@gmail.com --- drivers/gpu/drm/gma500/gma_display.c | 6 +++--- drivers/gpu/drm/gma500/psb_irq.c | 6 +++--- drivers/gpu/drm/gma500/psb_irq.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 369bc1f751cb..34ec3fca09ba 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -575,9 +575,9 @@ const struct drm_crtc_funcs gma_crtc_funcs = { .set_config = gma_crtc_set_config, .destroy = gma_crtc_destroy, .page_flip = gma_crtc_page_flip, - .enable_vblank = gma_enable_vblank, - .disable_vblank = gma_disable_vblank, - .get_vblank_counter = gma_get_vblank_counter, + .enable_vblank = gma_crtc_enable_vblank, + .disable_vblank = gma_crtc_disable_vblank, + .get_vblank_counter = gma_crtc_get_vblank_counter, }; /* diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c index 2e8ded532930..e6e6d61bbeab 100644 --- a/drivers/gpu/drm/gma500/psb_irq.c +++ b/drivers/gpu/drm/gma500/psb_irq.c @@ -371,7 +371,7 @@ void gma_irq_uninstall(struct drm_device *dev) free_irq(pdev->irq, dev); } -int gma_enable_vblank(struct drm_crtc *crtc) +int gma_crtc_enable_vblank(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; unsigned int pipe = crtc->index; @@ -404,7 +404,7 @@ int gma_enable_vblank(struct drm_crtc *crtc) return 0; } -void gma_disable_vblank(struct drm_crtc *crtc) +void gma_crtc_disable_vblank(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; unsigned int pipe = crtc->index; @@ -428,7 +428,7 @@ void gma_disable_vblank(struct drm_crtc *crtc) /* Called from drm generic code, passed a 'crtc', which * we use as a pipe index */ -u32 gma_get_vblank_counter(struct drm_crtc *crtc) +u32 gma_crtc_get_vblank_counter(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; unsigned int pipe = crtc->index; diff --git a/drivers/gpu/drm/gma500/psb_irq.h b/drivers/gpu/drm/gma500/psb_irq.h index c22878914f5b..b51e395194ff 100644 --- a/drivers/gpu/drm/gma500/psb_irq.h +++ b/drivers/gpu/drm/gma500/psb_irq.h @@ -20,9 +20,9 @@ void gma_irq_postinstall(struct drm_device *dev); int gma_irq_install(struct drm_device *dev, unsigned int irq); void gma_irq_uninstall(struct drm_device *dev); -int gma_enable_vblank(struct drm_crtc *crtc); -void gma_disable_vblank(struct drm_crtc *crtc); -u32 gma_get_vblank_counter(struct drm_crtc *crtc); +int gma_crtc_enable_vblank(struct drm_crtc *crtc); +void gma_crtc_disable_vblank(struct drm_crtc *crtc); +u32 gma_crtc_get_vblank_counter(struct drm_crtc *crtc); void gma_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); void gma_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); -- cgit From 0b9e66762aa0cda2a9c2d5542d64e04dac528fa6 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 7 Mar 2022 02:47:17 -0800 Subject: btrfs: zoned: traverse devices under chunk_mutex in btrfs_can_activate_zone btrfs_can_activate_zone() can be called with the device_list_mutex already held, which will lead to a deadlock: insert_dev_extents() // Takes device_list_mutex `-> insert_dev_extent() `-> btrfs_insert_empty_item() `-> btrfs_insert_empty_items() `-> btrfs_search_slot() `-> btrfs_cow_block() `-> __btrfs_cow_block() `-> btrfs_alloc_tree_block() `-> btrfs_reserve_extent() `-> find_free_extent() `-> find_free_extent_update_loop() `-> can_allocate_chunk() `-> btrfs_can_activate_zone() // Takes device_list_mutex again Instead of using the RCU on fs_devices->device_list we can use fs_devices->alloc_list, protected by the chunk_mutex to traverse the list of active devices. We are in the chunk allocation thread. The newer chunk allocation happens from the devices in the fs_device->alloc_list protected by the chunk_mutex. btrfs_create_chunk() lockdep_assert_held(&info->chunk_mutex); gather_device_info list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) Also, a device that reappears after the mount won't join the alloc_list yet and, it will be in the dev_list, which we don't want to consider in the context of the chunk alloc. [15.166572] WARNING: possible recursive locking detected [15.167117] 5.17.0-rc6-dennis #79 Not tainted [15.167487] -------------------------------------------- [15.167733] kworker/u8:3/146 is trying to acquire lock: [15.167733] ffff888102962ee0 (&fs_devs->device_list_mutex){+.+.}-{3:3}, at: find_free_extent+0x15a/0x14f0 [btrfs] [15.167733] [15.167733] but task is already holding lock: [15.167733] ffff888102962ee0 (&fs_devs->device_list_mutex){+.+.}-{3:3}, at: btrfs_create_pending_block_groups+0x20a/0x560 [btrfs] [15.167733] [15.167733] other info that might help us debug this: [15.167733] Possible unsafe locking scenario: [15.167733] [15.171834] CPU0 [15.171834] ---- [15.171834] lock(&fs_devs->device_list_mutex); [15.171834] lock(&fs_devs->device_list_mutex); [15.171834] [15.171834] *** DEADLOCK *** [15.171834] [15.171834] May be due to missing lock nesting notation [15.171834] [15.171834] 5 locks held by kworker/u8:3/146: [15.171834] #0: ffff888100050938 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work+0x1c3/0x5a0 [15.171834] #1: ffffc9000067be80 ((work_completion)(&fs_info->async_data_reclaim_work)){+.+.}-{0:0}, at: process_one_work+0x1c3/0x5a0 [15.176244] #2: ffff88810521e620 (sb_internal){.+.+}-{0:0}, at: flush_space+0x335/0x600 [btrfs] [15.176244] #3: ffff888102962ee0 (&fs_devs->device_list_mutex){+.+.}-{3:3}, at: btrfs_create_pending_block_groups+0x20a/0x560 [btrfs] [15.176244] #4: ffff8881152e4b78 (btrfs-dev-00){++++}-{3:3}, at: __btrfs_tree_lock+0x27/0x130 [btrfs] [15.179641] [15.179641] stack backtrace: [15.179641] CPU: 1 PID: 146 Comm: kworker/u8:3 Not tainted 5.17.0-rc6-dennis #79 [15.179641] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1.fc35 04/01/2014 [15.179641] Workqueue: events_unbound btrfs_async_reclaim_data_space [btrfs] [15.179641] Call Trace: [15.179641] [15.179641] dump_stack_lvl+0x45/0x59 [15.179641] __lock_acquire.cold+0x217/0x2b2 [15.179641] lock_acquire+0xbf/0x2b0 [15.183838] ? find_free_extent+0x15a/0x14f0 [btrfs] [15.183838] __mutex_lock+0x8e/0x970 [15.183838] ? find_free_extent+0x15a/0x14f0 [btrfs] [15.183838] ? find_free_extent+0x15a/0x14f0 [btrfs] [15.183838] ? lock_is_held_type+0xd7/0x130 [15.183838] ? find_free_extent+0x15a/0x14f0 [btrfs] [15.183838] find_free_extent+0x15a/0x14f0 [btrfs] [15.183838] ? _raw_spin_unlock+0x24/0x40 [15.183838] ? btrfs_get_alloc_profile+0x106/0x230 [btrfs] [15.187601] btrfs_reserve_extent+0x131/0x260 [btrfs] [15.187601] btrfs_alloc_tree_block+0xb5/0x3b0 [btrfs] [15.187601] __btrfs_cow_block+0x138/0x600 [btrfs] [15.187601] btrfs_cow_block+0x10f/0x230 [btrfs] [15.187601] btrfs_search_slot+0x55f/0xbc0 [btrfs] [15.187601] ? lock_is_held_type+0xd7/0x130 [15.187601] btrfs_insert_empty_items+0x2d/0x60 [btrfs] [15.187601] btrfs_create_pending_block_groups+0x2b3/0x560 [btrfs] [15.187601] __btrfs_end_transaction+0x36/0x2a0 [btrfs] [15.192037] flush_space+0x374/0x600 [btrfs] [15.192037] ? find_held_lock+0x2b/0x80 [15.192037] ? btrfs_async_reclaim_data_space+0x49/0x180 [btrfs] [15.192037] ? lock_release+0x131/0x2b0 [15.192037] btrfs_async_reclaim_data_space+0x70/0x180 [btrfs] [15.192037] process_one_work+0x24c/0x5a0 [15.192037] worker_thread+0x4a/0x3d0 Fixes: a85f05e59bc1 ("btrfs: zoned: avoid chunk allocation if active block group has enough space") CC: stable@vger.kernel.org # 5.16+ Reviewed-by: Anand Jain Signed-off-by: Johannes Thumshirn Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/zoned.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index b7b5fac1c779..61125aec8723 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -1976,18 +1976,19 @@ int btrfs_zone_finish(struct btrfs_block_group *block_group) bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags) { + struct btrfs_fs_info *fs_info = fs_devices->fs_info; struct btrfs_device *device; bool ret = false; - if (!btrfs_is_zoned(fs_devices->fs_info)) + if (!btrfs_is_zoned(fs_info)) return true; /* Non-single profiles are not supported yet */ ASSERT((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0); /* Check if there is a device with active zones left */ - mutex_lock(&fs_devices->device_list_mutex); - list_for_each_entry(device, &fs_devices->devices, dev_list) { + mutex_lock(&fs_info->chunk_mutex); + list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) { struct btrfs_zoned_device_info *zinfo = device->zone_info; if (!device->bdev) @@ -1999,7 +2000,7 @@ bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags) break; } } - mutex_unlock(&fs_devices->device_list_mutex); + mutex_unlock(&fs_info->chunk_mutex); return ret; } -- cgit From 62ed0bf7315b524973bb5fb9174b60e353289835 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 7 Mar 2022 02:47:18 -0800 Subject: btrfs: zoned: remove left over ASSERT checking for single profile With commit dcf5652291f6 ("btrfs: zoned: allow DUP on meta-data block groups") we started allowing DUP on metadata block groups, so the ASSERT()s in btrfs_can_activate_zone() and btrfs_zoned_get_device() are no longer valid and in fact even harmful. Fixes: dcf5652291f6 ("btrfs: zoned: allow DUP on meta-data block groups") CC: stable@vger.kernel.org # 5.17 Signed-off-by: Johannes Thumshirn Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/zoned.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 61125aec8723..1b1b310c3c51 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -1801,7 +1801,6 @@ struct btrfs_device *btrfs_zoned_get_device(struct btrfs_fs_info *fs_info, map = em->map_lookup; /* We only support single profile for now */ - ASSERT(map->num_stripes == 1); device = map->stripes[0].dev; free_extent_map(em); @@ -1983,9 +1982,6 @@ bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags) if (!btrfs_is_zoned(fs_info)) return true; - /* Non-single profiles are not supported yet */ - ASSERT((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0); - /* Check if there is a device with active zones left */ mutex_lock(&fs_info->chunk_mutex); list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) { -- cgit From b642b52d0b50f4d398cb4293f64992d0eed2e2ce Mon Sep 17 00:00:00 2001 From: Ethan Lien Date: Mon, 7 Mar 2022 18:00:04 +0800 Subject: btrfs: fix qgroup reserve overflow the qgroup limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use extent_changeset->bytes_changed in qgroup_reserve_data() to record how many bytes we set for EXTENT_QGROUP_RESERVED state. Currently the bytes_changed is set as "unsigned int", and it will overflow if we try to fallocate a range larger than 4GiB. The result is we reserve less bytes and eventually break the qgroup limit. Unlike regular buffered/direct write, which we use one changeset for each ordered extent, which can never be larger than 256M. For fallocate, we use one changeset for the whole range, thus it no longer respects the 256M per extent limit, and caused the problem. The following example test script reproduces the problem: $ cat qgroup-overflow.sh #!/bin/bash DEV=/dev/sdj MNT=/mnt/sdj mkfs.btrfs -f $DEV mount $DEV $MNT # Set qgroup limit to 2GiB. btrfs quota enable $MNT btrfs qgroup limit 2G $MNT # Try to fallocate a 3GiB file. This should fail. echo echo "Try to fallocate a 3GiB file..." fallocate -l 3G $MNT/3G.file # Try to fallocate a 5GiB file. echo echo "Try to fallocate a 5GiB file..." fallocate -l 5G $MNT/5G.file # See we break the qgroup limit. echo sync btrfs qgroup show -r $MNT umount $MNT When running the test: $ ./qgroup-overflow.sh (...) Try to fallocate a 3GiB file... fallocate: fallocate failed: Disk quota exceeded Try to fallocate a 5GiB file... qgroupid         rfer         excl     max_rfer --------         ----         ----     -------- 0/5           5.00GiB      5.00GiB      2.00GiB Since we have no control of how bytes_changed is used, it's better to set it to u64. CC: stable@vger.kernel.org # 4.14+ Reviewed-by: Qu Wenruo Signed-off-by: Ethan Lien Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent_io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 0399cf8e3c32..151e9da5da2d 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -118,7 +118,7 @@ struct btrfs_bio_ctrl { */ struct extent_changeset { /* How many bytes are set/cleared in this operation */ - unsigned int bytes_changed; + u64 bytes_changed; /* Changed ranges */ struct ulist range_changed; -- cgit From cc2f2df48a17980e815fa09a43b81861f7ebad85 Mon Sep 17 00:00:00 2001 From: Nicolas Belin Date: Wed, 16 Mar 2022 14:57:31 +0100 Subject: dt-bindings: display: bridge: it66121: Add audio support Update the ITE bridge HDMI it66121 bindings in order to support audio. Signed-off-by: Nicolas Belin Acked-by: Rob Herring Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20220316135733.173950-2-nbelin@baylibre.com --- Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml index 6ec1d5fbb8bc..c6e81f532215 100644 --- a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml @@ -38,6 +38,9 @@ properties: interrupts: maxItems: 1 + "#sound-dai-cells": + const: 0 + ports: $ref: /schemas/graph.yaml#/properties/ports -- cgit From 003a1bd6a2a55c16cb2451153533dbedb12bebec Mon Sep 17 00:00:00 2001 From: Nicolas Belin Date: Wed, 16 Mar 2022 14:57:32 +0100 Subject: drm: bridge: it66121: Fix the register page length Set the register page length or window length to 0x100 according to the documentation. Fixes: 988156dc2fc9 ("drm: bridge: add it66121 driver") Signed-off-by: Nicolas Belin Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20220316135733.173950-3-nbelin@baylibre.com --- drivers/gpu/drm/bridge/ite-it66121.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 69288cf894b9..e81c106e2c2b 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -227,7 +227,7 @@ static const struct regmap_range_cfg it66121_regmap_banks[] = { .selector_mask = 0x1, .selector_shift = 0, .window_start = 0x00, - .window_len = 0x130, + .window_len = 0x100, }, }; -- cgit From e0fd83dbe92426e4f09b01111d260d2a7dc72fdb Mon Sep 17 00:00:00 2001 From: Nicolas Belin Date: Wed, 16 Mar 2022 14:57:33 +0100 Subject: drm: bridge: it66121: Add audio support Adding the audio support on the HDMI bridge for I2S only. Signed-off-by: Nicolas Belin Signed-off-by: Andy.Hsieh Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20220316135733.173950-4-nbelin@baylibre.com --- drivers/gpu/drm/bridge/ite-it66121.c | 627 +++++++++++++++++++++++++++++++++++ 1 file changed, 627 insertions(+) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index e81c106e2c2b..448c58e60c11 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -27,6 +27,8 @@ #include #include +#include + #define IT66121_VENDOR_ID0_REG 0x00 #define IT66121_VENDOR_ID1_REG 0x01 #define IT66121_DEVICE_ID0_REG 0x02 @@ -155,6 +157,9 @@ #define IT66121_AV_MUTE_ON BIT(0) #define IT66121_AV_MUTE_BLUESCR BIT(1) +#define IT66121_PKT_CTS_CTRL_REG 0xC5 +#define IT66121_PKT_CTS_CTRL_SEL BIT(1) + #define IT66121_PKT_GEN_CTRL_REG 0xC6 #define IT66121_PKT_GEN_CTRL_ON BIT(0) #define IT66121_PKT_GEN_CTRL_RPT BIT(1) @@ -202,6 +207,89 @@ #define IT66121_EDID_SLEEP_US 20000 #define IT66121_EDID_TIMEOUT_US 200000 #define IT66121_EDID_FIFO_SIZE 32 + +#define IT66121_CLK_CTRL0_REG 0x58 +#define IT66121_CLK_CTRL0_AUTO_OVER_SAMPLING BIT(4) +#define IT66121_CLK_CTRL0_EXT_MCLK_MASK GENMASK(3, 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_128FS (0 << 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_256FS BIT(2) +#define IT66121_CLK_CTRL0_EXT_MCLK_512FS (2 << 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_1024FS (3 << 2) +#define IT66121_CLK_CTRL0_AUTO_IPCLK BIT(0) +#define IT66121_CLK_STATUS1_REG 0x5E +#define IT66121_CLK_STATUS2_REG 0x5F + +#define IT66121_AUD_CTRL0_REG 0xE0 +#define IT66121_AUD_SWL (3 << 6) +#define IT66121_AUD_16BIT (0 << 6) +#define IT66121_AUD_18BIT BIT(6) +#define IT66121_AUD_20BIT (2 << 6) +#define IT66121_AUD_24BIT (3 << 6) +#define IT66121_AUD_SPDIFTC BIT(5) +#define IT66121_AUD_SPDIF BIT(4) +#define IT66121_AUD_I2S (0 << 4) +#define IT66121_AUD_EN_I2S3 BIT(3) +#define IT66121_AUD_EN_I2S2 BIT(2) +#define IT66121_AUD_EN_I2S1 BIT(1) +#define IT66121_AUD_EN_I2S0 BIT(0) +#define IT66121_AUD_CTRL0_AUD_SEL BIT(4) + +#define IT66121_AUD_CTRL1_REG 0xE1 +#define IT66121_AUD_FIFOMAP_REG 0xE2 +#define IT66121_AUD_CTRL3_REG 0xE3 +#define IT66121_AUD_SRCVALID_FLAT_REG 0xE4 +#define IT66121_AUD_FLAT_SRC0 BIT(4) +#define IT66121_AUD_FLAT_SRC1 BIT(5) +#define IT66121_AUD_FLAT_SRC2 BIT(6) +#define IT66121_AUD_FLAT_SRC3 BIT(7) +#define IT66121_AUD_HDAUDIO_REG 0xE5 + +#define IT66121_AUD_PKT_CTS0_REG 0x130 +#define IT66121_AUD_PKT_CTS1_REG 0x131 +#define IT66121_AUD_PKT_CTS2_REG 0x132 +#define IT66121_AUD_PKT_N0_REG 0x133 +#define IT66121_AUD_PKT_N1_REG 0x134 +#define IT66121_AUD_PKT_N2_REG 0x135 + +#define IT66121_AUD_CHST_MODE_REG 0x191 +#define IT66121_AUD_CHST_CAT_REG 0x192 +#define IT66121_AUD_CHST_SRCNUM_REG 0x193 +#define IT66121_AUD_CHST_CHTNUM_REG 0x194 +#define IT66121_AUD_CHST_CA_FS_REG 0x198 +#define IT66121_AUD_CHST_OFS_WL_REG 0x199 + +#define IT66121_AUD_PKT_CTS_CNT0_REG 0x1A0 +#define IT66121_AUD_PKT_CTS_CNT1_REG 0x1A1 +#define IT66121_AUD_PKT_CTS_CNT2_REG 0x1A2 + +#define IT66121_AUD_FS_22P05K 0x4 +#define IT66121_AUD_FS_44P1K 0x0 +#define IT66121_AUD_FS_88P2K 0x8 +#define IT66121_AUD_FS_176P4K 0xC +#define IT66121_AUD_FS_24K 0x6 +#define IT66121_AUD_FS_48K 0x2 +#define IT66121_AUD_FS_96K 0xA +#define IT66121_AUD_FS_192K 0xE +#define IT66121_AUD_FS_768K 0x9 +#define IT66121_AUD_FS_32K 0x3 +#define IT66121_AUD_FS_OTHER 0x1 + +#define IT66121_AUD_SWL_21BIT 0xD +#define IT66121_AUD_SWL_24BIT 0xB +#define IT66121_AUD_SWL_23BIT 0x9 +#define IT66121_AUD_SWL_22BIT 0x5 +#define IT66121_AUD_SWL_20BIT 0x3 +#define IT66121_AUD_SWL_17BIT 0xC +#define IT66121_AUD_SWL_19BIT 0x8 +#define IT66121_AUD_SWL_18BIT 0x4 +#define IT66121_AUD_SWL_16BIT 0x2 +#define IT66121_AUD_SWL_NOT_INDICATED 0x0 + +#define IT66121_VENDOR_ID0 0x54 +#define IT66121_VENDOR_ID1 0x49 +#define IT66121_DEVICE_ID0 0x12 +#define IT66121_DEVICE_ID1 0x06 +#define IT66121_DEVICE_MASK 0x0F #define IT66121_AFE_CLK_HIGH 80000 /* Khz */ struct it66121_ctx { @@ -216,6 +304,13 @@ struct it66121_ctx { u32 bus_width; struct mutex lock; /* Protects fields below and device registers */ struct hdmi_avi_infoframe hdmi_avi_infoframe; + struct { + struct platform_device *pdev; + u8 ch_enable; + u8 fs; + u8 swl; + bool auto_cts; + } audio; }; static const struct regmap_range_cfg it66121_regmap_banks[] = { @@ -886,6 +981,536 @@ unlock: return IRQ_HANDLED; } +static int it661221_set_chstat(struct it66121_ctx *ctx, u8 iec60958_chstat[]) +{ + int ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_MODE_REG, iec60958_chstat[0] & 0x7C); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_CAT_REG, iec60958_chstat[1]); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_SRCNUM_REG, iec60958_chstat[2] & 0x0F); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_CHTNUM_REG, + (iec60958_chstat[2] >> 4) & 0x0F); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_CA_FS_REG, iec60958_chstat[3]); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_AUD_CHST_OFS_WL_REG, iec60958_chstat[4]); +} + +static int it661221_set_lpcm_audio(struct it66121_ctx *ctx, u8 audio_src_num, u8 audio_swl) +{ + int ret; + unsigned int audio_enable = 0; + unsigned int audio_format = 0; + + switch (audio_swl) { + case 16: + audio_enable |= IT66121_AUD_16BIT; + break; + case 18: + audio_enable |= IT66121_AUD_18BIT; + break; + case 20: + audio_enable |= IT66121_AUD_20BIT; + break; + case 24: + default: + audio_enable |= IT66121_AUD_24BIT; + break; + } + + audio_format |= 0x40; + switch (audio_src_num) { + case 4: + audio_enable |= IT66121_AUD_EN_I2S3 | IT66121_AUD_EN_I2S2 | + IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0; + break; + case 3: + audio_enable |= IT66121_AUD_EN_I2S2 | IT66121_AUD_EN_I2S1 | + IT66121_AUD_EN_I2S0; + break; + case 2: + audio_enable |= IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0; + break; + case 1: + default: + audio_format &= ~0x40; + audio_enable |= IT66121_AUD_EN_I2S0; + break; + } + + audio_format |= 0x01; + ctx->audio.ch_enable = audio_enable; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL0_REG, audio_enable & 0xF0); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL1_REG, audio_format); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_FIFOMAP_REG, 0xE4); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL3_REG, 0x00); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, 0x00); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_AUD_HDAUDIO_REG, 0x00); +} + +static int it661221_set_ncts(struct it66121_ctx *ctx, u8 fs) +{ + int ret; + unsigned int n; + + switch (fs) { + case IT66121_AUD_FS_32K: + n = 4096; + break; + case IT66121_AUD_FS_44P1K: + n = 6272; + break; + case IT66121_AUD_FS_48K: + n = 6144; + break; + case IT66121_AUD_FS_88P2K: + n = 12544; + break; + case IT66121_AUD_FS_96K: + n = 12288; + break; + case IT66121_AUD_FS_176P4K: + n = 25088; + break; + case IT66121_AUD_FS_192K: + n = 24576; + break; + case IT66121_AUD_FS_768K: + n = 24576; + break; + default: + n = 6144; + break; + } + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N0_REG, (u8)((n) & 0xFF)); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N1_REG, (u8)((n >> 8) & 0xFF)); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N2_REG, (u8)((n >> 16) & 0xF)); + if (ret) + return ret; + + if (ctx->audio.auto_cts) { + u8 loop_cnt = 255; + u8 cts_stable_cnt = 0; + unsigned int sum_cts = 0; + unsigned int cts = 0; + unsigned int last_cts = 0; + unsigned int diff; + unsigned int val; + + while (loop_cnt--) { + msleep(30); + regmap_read(ctx->regmap, IT66121_AUD_PKT_CTS_CNT2_REG, &val); + cts = val << 12; + regmap_read(ctx->regmap, IT66121_AUD_PKT_CTS_CNT1_REG, &val); + cts |= val << 4; + regmap_read(ctx->regmap, IT66121_AUD_PKT_CTS_CNT0_REG, &val); + cts |= val >> 4; + if (cts == 0) { + continue; + } else { + if (last_cts > cts) + diff = last_cts - cts; + else + diff = cts - last_cts; + last_cts = cts; + if (diff < 5) { + cts_stable_cnt++; + sum_cts += cts; + } else { + cts_stable_cnt = 0; + sum_cts = 0; + continue; + } + + if (cts_stable_cnt >= 32) { + last_cts = (sum_cts >> 5); + break; + } + } + } + + regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS0_REG, (u8)((last_cts) & 0xFF)); + regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS1_REG, (u8)((last_cts >> 8) & 0xFF)); + regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS2_REG, (u8)((last_cts >> 16) & 0x0F)); + } + + ret = regmap_write(ctx->regmap, 0xF8, 0xC3); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, 0xF8, 0xA5); + if (ret) + return ret; + + if (ctx->audio.auto_cts) { + ret = regmap_write_bits(ctx->regmap, IT66121_PKT_CTS_CTRL_REG, + IT66121_PKT_CTS_CTRL_SEL, + 1); + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_PKT_CTS_CTRL_REG, + IT66121_PKT_CTS_CTRL_SEL, + 0); + } + + if (ret) + return ret; + + return regmap_write(ctx->regmap, 0xF8, 0xFF); +} + +static int it661221_audio_output_enable(struct it66121_ctx *ctx, bool enable) +{ + int ret; + + if (enable) { + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_EN_I2S3 | IT66121_AUD_EN_I2S2 | + IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0, + ctx->audio.ch_enable); + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_EN_I2S3 | IT66121_AUD_EN_I2S2 | + IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0, + ctx->audio.ch_enable & 0xF0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF); + } + + return ret; +} + +static int it661221_audio_ch_enable(struct it66121_ctx *ctx, bool enable) +{ + int ret; + + if (enable) { + ret = regmap_write(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, 0); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL0_REG, ctx->audio.ch_enable); + } else { + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL0_REG, ctx->audio.ch_enable & 0xF0); + } + + return ret; +} + +static int it66121_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + u8 fs; + u8 swl; + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + static u8 iec60958_chstat[5]; + unsigned int channels = params->channels; + unsigned int sample_rate = params->sample_rate; + unsigned int sample_width = params->sample_width; + + mutex_lock(&ctx->lock); + dev_dbg(dev, "%s: %u, %u, %u, %u\n", __func__, + daifmt->fmt, sample_rate, sample_width, channels); + + switch (daifmt->fmt) { + case HDMI_I2S: + dev_dbg(dev, "Using HDMI I2S\n"); + break; + default: + dev_err(dev, "Invalid or unsupported DAI format %d\n", daifmt->fmt); + ret = -EINVAL; + goto out; + } + + // Set audio clock recovery (N/CTS) + ret = regmap_write(ctx->regmap, IT66121_CLK_CTRL0_REG, + IT66121_CLK_CTRL0_AUTO_OVER_SAMPLING | + IT66121_CLK_CTRL0_EXT_MCLK_256FS | + IT66121_CLK_CTRL0_AUTO_IPCLK); + if (ret) + goto out; + + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_CTRL0_AUD_SEL, 0); // remove spdif selection + if (ret) + goto out; + + switch (sample_rate) { + case 44100L: + fs = IT66121_AUD_FS_44P1K; + break; + case 88200L: + fs = IT66121_AUD_FS_88P2K; + break; + case 176400L: + fs = IT66121_AUD_FS_176P4K; + break; + case 32000L: + fs = IT66121_AUD_FS_32K; + break; + case 48000L: + fs = IT66121_AUD_FS_48K; + break; + case 96000L: + fs = IT66121_AUD_FS_96K; + break; + case 192000L: + fs = IT66121_AUD_FS_192K; + break; + case 768000L: + fs = IT66121_AUD_FS_768K; + break; + default: + fs = IT66121_AUD_FS_48K; + break; + } + + ctx->audio.fs = fs; + ret = it661221_set_ncts(ctx, fs); + if (ret) { + dev_err(dev, "Failed to set N/CTS: %d\n", ret); + goto out; + } + + // Set audio format register (except audio channel enable) + ret = it661221_set_lpcm_audio(ctx, (channels + 1) / 2, sample_width); + if (ret) { + dev_err(dev, "Failed to set LPCM audio: %d\n", ret); + goto out; + } + + // Set audio channel status + iec60958_chstat[0] = 0; + if ((channels + 1) / 2 == 1) + iec60958_chstat[0] |= 0x1; + iec60958_chstat[0] &= ~(1 << 1); + iec60958_chstat[1] = 0; + iec60958_chstat[2] = (channels + 1) / 2; + iec60958_chstat[2] |= (channels << 4) & 0xF0; + iec60958_chstat[3] = fs; + + switch (sample_width) { + case 21L: + swl = IT66121_AUD_SWL_21BIT; + break; + case 24L: + swl = IT66121_AUD_SWL_24BIT; + break; + case 23L: + swl = IT66121_AUD_SWL_23BIT; + break; + case 22L: + swl = IT66121_AUD_SWL_22BIT; + break; + case 20L: + swl = IT66121_AUD_SWL_20BIT; + break; + case 17L: + swl = IT66121_AUD_SWL_17BIT; + break; + case 19L: + swl = IT66121_AUD_SWL_19BIT; + break; + case 18L: + swl = IT66121_AUD_SWL_18BIT; + break; + case 16L: + swl = IT66121_AUD_SWL_16BIT; + break; + default: + swl = IT66121_AUD_SWL_NOT_INDICATED; + break; + } + + iec60958_chstat[4] = (((~fs) << 4) & 0xF0) | swl; + ret = it661221_set_chstat(ctx, iec60958_chstat); + if (ret) { + dev_err(dev, "Failed to set channel status: %d\n", ret); + goto out; + } + + // Enable audio channel enable while input clock stable (if SPDIF). + ret = it661221_audio_ch_enable(ctx, true); + if (ret) { + dev_err(dev, "Failed to enable audio channel: %d\n", ret); + goto out; + } + + ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, + IT66121_INT_MASK1_AUD_OVF, + 0); + if (ret) + goto out; + + dev_dbg(dev, "HDMI audio enabled.\n"); +out: + mutex_unlock(&ctx->lock); + + return ret; +} + +static int it66121_audio_startup(struct device *dev, void *data) +{ + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + mutex_lock(&ctx->lock); + ret = it661221_audio_output_enable(ctx, true); + if (ret) + dev_err(dev, "Failed to enable audio output: %d\n", ret); + + mutex_unlock(&ctx->lock); + + return ret; +} + +static void it66121_audio_shutdown(struct device *dev, void *data) +{ + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + mutex_lock(&ctx->lock); + ret = it661221_audio_output_enable(ctx, false); + if (ret) + dev_err(dev, "Failed to disable audio output: %d\n", ret); + + mutex_unlock(&ctx->lock); +} + +static int it66121_audio_mute(struct device *dev, void *data, + bool enable, int direction) +{ + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "%s: enable=%s, direction=%d\n", + __func__, enable ? "true" : "false", direction); + + mutex_lock(&ctx->lock); + + if (enable) { + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, + IT66121_AUD_FLAT_SRC0 | IT66121_AUD_FLAT_SRC1 | + IT66121_AUD_FLAT_SRC2 | IT66121_AUD_FLAT_SRC3, + IT66121_AUD_FLAT_SRC0 | IT66121_AUD_FLAT_SRC1 | + IT66121_AUD_FLAT_SRC2 | IT66121_AUD_FLAT_SRC3); + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, + IT66121_AUD_FLAT_SRC0 | IT66121_AUD_FLAT_SRC1 | + IT66121_AUD_FLAT_SRC2 | IT66121_AUD_FLAT_SRC3, + 0); + } + + mutex_unlock(&ctx->lock); + + return ret; +} + +static int it66121_audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + mutex_lock(&ctx->lock); + + memcpy(buf, ctx->connector->eld, + min(sizeof(ctx->connector->eld), len)); + + mutex_unlock(&ctx->lock); + + return 0; +} + +static const struct hdmi_codec_ops it66121_audio_codec_ops = { + .hw_params = it66121_audio_hw_params, + .audio_startup = it66121_audio_startup, + .audio_shutdown = it66121_audio_shutdown, + .mute_stream = it66121_audio_mute, + .get_eld = it66121_audio_get_eld, + .no_capture_mute = 1, +}; + +static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &it66121_audio_codec_ops, + .i2s = 1, /* Only i2s support for now */ + .spdif = 0, + .max_i2s_channels = 8, + }; + + dev_dbg(dev, "%s\n", __func__); + + if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) { + dev_info(dev, "No \"#sound-dai-cells\", no audio\n"); + return 0; + } + + ctx->audio.pdev = platform_device_register_data(dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + + if (IS_ERR(ctx->audio.pdev)) { + dev_err(dev, "Failed to initialize HDMI audio codec: %d\n", + PTR_ERR_OR_ZERO(ctx->audio.pdev)); + } + + return PTR_ERR_OR_ZERO(ctx->audio.pdev); +} + static int it66121_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -988,6 +1613,8 @@ static int it66121_probe(struct i2c_client *client, return ret; } + it66121_audio_codec_init(ctx, dev); + drm_bridge_add(&ctx->bridge); dev_info(ctx->dev, "IT66121 revision %d probed\n", revision_id); -- cgit From 0941a4e3c629c902e7d229218dbc36a146ca699c Mon Sep 17 00:00:00 2001 From: Christian König Date: Wed, 3 Nov 2021 09:34:29 +0100 Subject: drm/etnaviv: stop using dma_resv_excl_fence v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can get the excl fence together with the shared ones as well. v2: rename the member to fences as well Signed-off-by: Christian König Reviewed-by: Daniel Vetter Reviewed-by: Lucas Stach Cc: Lucas Stach Cc: Russell King Cc: Christian Gmeiner Cc: etnaviv@lists.freedesktop.org Link: https://patchwork.freedesktop.org/patch/msgid/20220321135856.1331-5-christian.koenig@amd.com --- drivers/gpu/drm/etnaviv/etnaviv_gem.h | 5 ++--- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 14 +++++--------- drivers/gpu/drm/etnaviv/etnaviv_sched.c | 24 +++++++----------------- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index 98e60df882b6..8983a0ef383e 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -80,9 +80,8 @@ struct etnaviv_gem_submit_bo { u64 va; struct etnaviv_gem_object *obj; struct etnaviv_vram_mapping *mapping; - struct dma_fence *excl; - unsigned int nr_shared; - struct dma_fence **shared; + unsigned int nr_fences; + struct dma_fence **fences; }; /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 4eb00a0cb650..592cbb38609a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -188,15 +188,11 @@ static int submit_fence_sync(struct etnaviv_gem_submit *submit) if (submit->flags & ETNA_SUBMIT_NO_IMPLICIT) continue; - if (bo->flags & ETNA_SUBMIT_BO_WRITE) { - ret = dma_resv_get_fences(robj, true, &bo->nr_shared, - &bo->shared); - if (ret) - return ret; - } else { - bo->excl = dma_fence_get(dma_resv_excl_fence(robj)); - } - + ret = dma_resv_get_fences(robj, + bo->flags & ETNA_SUBMIT_BO_WRITE, + &bo->nr_fences, &bo->fences); + if (ret) + return ret; } return ret; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 35e5ef7dbdcc..a8452ce10e3a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -39,31 +39,21 @@ etnaviv_sched_dependency(struct drm_sched_job *sched_job, struct etnaviv_gem_submit_bo *bo = &submit->bos[i]; int j; - if (bo->excl) { - fence = bo->excl; - bo->excl = NULL; - - if (!dma_fence_is_signaled(fence)) - return fence; - - dma_fence_put(fence); - } - - for (j = 0; j < bo->nr_shared; j++) { - if (!bo->shared[j]) + for (j = 0; j < bo->nr_fences; j++) { + if (!bo->fences[j]) continue; - fence = bo->shared[j]; - bo->shared[j] = NULL; + fence = bo->fences[j]; + bo->fences[j] = NULL; if (!dma_fence_is_signaled(fence)) return fence; dma_fence_put(fence); } - kfree(bo->shared); - bo->nr_shared = 0; - bo->shared = NULL; + kfree(bo->fences); + bo->nr_fences = 0; + bo->fences = NULL; } return NULL; -- cgit From f30bceab16d1d6a97cbe262623e25ed2873896f0 Mon Sep 17 00:00:00 2001 From: Christian König Date: Wed, 22 Sep 2021 13:01:04 +0200 Subject: RDMA: use dma_resv_wait() instead of extracting the fence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use dma_resv_wait() instead of extracting the exclusive fence and waiting on it manually. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Acked-by: Jason Gunthorpe Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Maor Gottlieb Cc: Gal Pressman Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Link: https://patchwork.freedesktop.org/patch/msgid/20220321135856.1331-4-christian.koenig@amd.com --- drivers/infiniband/core/umem_dmabuf.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index f0760741f281..d32cd7538835 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -16,7 +16,6 @@ int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf) { struct sg_table *sgt; struct scatterlist *sg; - struct dma_fence *fence; unsigned long start, end, cur = 0; unsigned int nmap = 0; int i; @@ -68,11 +67,8 @@ wait_fence: * may be not up-to-date. Wait for the exporter to finish * the migration. */ - fence = dma_resv_excl_fence(umem_dmabuf->attach->dmabuf->resv); - if (fence) - return dma_fence_wait(fence, false); - - return 0; + return dma_resv_wait_timeout(umem_dmabuf->attach->dmabuf->resv, false, + false, MAX_SCHEDULE_TIMEOUT); } EXPORT_SYMBOL(ib_umem_dmabuf_map_pages); -- cgit From 548e7432dc2da475a18077b612e8d55b8ff51891 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 24 Sep 2021 10:55:45 +0200 Subject: dma-buf: add dma_resv_replace_fences v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function allows to replace fences from the shared fence list when we can gurantee that the operation represented by the original fence has finished or no accesses to the resources protected by the dma_resv object any more when the new fence finishes. Then use this function in the amdkfd code when BOs are unmapped from the process. v2: add an example when this is usefull. Signed-off-by: Christian König Reviewed-by: Felix Kuehling Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220321135856.1331-1-christian.koenig@amd.com --- drivers/dma-buf/dma-resv.c | 45 ++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 49 ++++-------------------- include/linux/dma-resv.h | 2 + 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index b51416405e86..509060861cf3 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -289,6 +289,51 @@ replace: } EXPORT_SYMBOL(dma_resv_add_shared_fence); +/** + * dma_resv_replace_fences - replace fences in the dma_resv obj + * @obj: the reservation object + * @context: the context of the fences to replace + * @replacement: the new fence to use instead + * + * Replace fences with a specified context with a new fence. Only valid if the + * operation represented by the original fence has no longer access to the + * resources represented by the dma_resv object when the new fence completes. + * + * And example for using this is replacing a preemption fence with a page table + * update fence which makes the resource inaccessible. + */ +void dma_resv_replace_fences(struct dma_resv *obj, uint64_t context, + struct dma_fence *replacement) +{ + struct dma_resv_list *list; + struct dma_fence *old; + unsigned int i; + + dma_resv_assert_held(obj); + + write_seqcount_begin(&obj->seq); + + old = dma_resv_excl_fence(obj); + if (old->context == context) { + RCU_INIT_POINTER(obj->fence_excl, dma_fence_get(replacement)); + dma_fence_put(old); + } + + list = dma_resv_shared_list(obj); + for (i = 0; list && i < list->shared_count; ++i) { + old = rcu_dereference_protected(list->shared[i], + dma_resv_held(obj)); + if (old->context != context) + continue; + + rcu_assign_pointer(list->shared[i], dma_fence_get(replacement)); + dma_fence_put(old); + } + + write_seqcount_end(&obj->seq); +} +EXPORT_SYMBOL(dma_resv_replace_fences); + /** * dma_resv_add_excl_fence - Add an exclusive fence. * @obj: the reservation object diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index f9bab963a948..b6f266f612ea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -253,53 +253,18 @@ void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo) static int amdgpu_amdkfd_remove_eviction_fence(struct amdgpu_bo *bo, struct amdgpu_amdkfd_fence *ef) { - struct dma_resv *resv = bo->tbo.base.resv; - struct dma_resv_list *old, *new; - unsigned int i, j, k; + struct dma_fence *replacement; if (!ef) return -EINVAL; - old = dma_resv_shared_list(resv); - if (!old) - return 0; - - new = kmalloc(struct_size(new, shared, old->shared_max), GFP_KERNEL); - if (!new) - return -ENOMEM; - - /* Go through all the shared fences in the resevation object and sort - * the interesting ones to the end of the list. + /* TODO: Instead of block before we should use the fence of the page + * table update and TLB flush here directly. */ - for (i = 0, j = old->shared_count, k = 0; i < old->shared_count; ++i) { - struct dma_fence *f; - - f = rcu_dereference_protected(old->shared[i], - dma_resv_held(resv)); - - if (f->context == ef->base.context) - RCU_INIT_POINTER(new->shared[--j], f); - else - RCU_INIT_POINTER(new->shared[k++], f); - } - new->shared_max = old->shared_max; - new->shared_count = k; - - /* Install the new fence list, seqcount provides the barriers */ - write_seqcount_begin(&resv->seq); - RCU_INIT_POINTER(resv->fence, new); - write_seqcount_end(&resv->seq); - - /* Drop the references to the removed fences or move them to ef_list */ - for (i = j; i < old->shared_count; ++i) { - struct dma_fence *f; - - f = rcu_dereference_protected(new->shared[i], - dma_resv_held(resv)); - dma_fence_put(f); - } - kfree_rcu(old, rcu); - + replacement = dma_fence_get_stub(); + dma_resv_replace_fences(bo->tbo.base.resv, ef->base.context, + replacement); + dma_fence_put(replacement); return 0; } diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h index afdfdfac729f..3f53177bdb46 100644 --- a/include/linux/dma-resv.h +++ b/include/linux/dma-resv.h @@ -468,6 +468,8 @@ void dma_resv_init(struct dma_resv *obj); void dma_resv_fini(struct dma_resv *obj); int dma_resv_reserve_shared(struct dma_resv *obj, unsigned int num_fences); void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence); +void dma_resv_replace_fences(struct dma_resv *obj, uint64_t context, + struct dma_fence *fence); void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence); int dma_resv_get_fences(struct dma_resv *obj, bool write, unsigned int *num_fences, struct dma_fence ***fences); -- cgit From 8938d48451f5d7cb565dfa68aa0bd0e81985da09 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 24 Sep 2021 14:19:22 +0200 Subject: dma-buf: finally make the dma_resv_list private v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers should never touch this directly. v2: drop kerneldoc for now internal handling Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220321135856.1331-2-christian.koenig@amd.com --- drivers/dma-buf/dma-resv.c | 11 +++++++++++ include/linux/dma-resv.h | 26 +------------------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index 509060861cf3..5001e9b4420a 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -56,6 +56,12 @@ DEFINE_WD_CLASS(reservation_ww_class); EXPORT_SYMBOL(reservation_ww_class); +struct dma_resv_list { + struct rcu_head rcu; + u32 shared_count, shared_max; + struct dma_fence __rcu *shared[]; +}; + /** * dma_resv_list_alloc - allocate fence list * @shared_max: number of fences we need space for @@ -133,6 +139,11 @@ void dma_resv_fini(struct dma_resv *obj) } EXPORT_SYMBOL(dma_resv_fini); +static inline struct dma_resv_list *dma_resv_shared_list(struct dma_resv *obj) +{ + return rcu_dereference_check(obj->fence, dma_resv_held(obj)); +} + /** * dma_resv_reserve_shared - Reserve space to add shared fences to * a dma_resv. diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h index 3f53177bdb46..202cc65d0621 100644 --- a/include/linux/dma-resv.h +++ b/include/linux/dma-resv.h @@ -47,18 +47,7 @@ extern struct ww_class reservation_ww_class; -/** - * struct dma_resv_list - a list of shared fences - * @rcu: for internal use - * @shared_count: table of shared fences - * @shared_max: for growing shared fence table - * @shared: shared fence table - */ -struct dma_resv_list { - struct rcu_head rcu; - u32 shared_count, shared_max; - struct dma_fence __rcu *shared[]; -}; +struct dma_resv_list; /** * struct dma_resv - a reservation object manages fences for a buffer @@ -451,19 +440,6 @@ dma_resv_excl_fence(struct dma_resv *obj) return rcu_dereference_check(obj->fence_excl, dma_resv_held(obj)); } -/** - * dma_resv_shared_list - get the reservation object's shared fence list - * @obj: the reservation object - * - * Returns the shared fence list. Caller must either hold the objects - * through dma_resv_lock() or the RCU read side lock through rcu_read_lock(), - * or one of the variants of each - */ -static inline struct dma_resv_list *dma_resv_shared_list(struct dma_resv *obj) -{ - return rcu_dereference_check(obj->fence, dma_resv_held(obj)); -} - void dma_resv_init(struct dma_resv *obj); void dma_resv_fini(struct dma_resv *obj); int dma_resv_reserve_shared(struct dma_resv *obj, unsigned int num_fences); -- cgit From 3dd4834a6efe4eb3c086526e1870bb768776d86a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 17 Mar 2022 17:47:24 +0800 Subject: drm/bridge: it6505: Fix build error If DRM_ITE_IT6505 is y but DRM_DP_HELPER is m, building failed: drivers/gpu/drm/bridge/ite-it6505.o: In function `it6505_i2c_remove': ite-it6505.c:(.text+0x35c): undefined reference to `drm_dp_aux_unregister' drivers/gpu/drm/bridge/ite-it6505.o: In function `it6505_dpcd_read': ite-it6505.c:(.text+0x420): undefined reference to `drm_dp_dpcd_read' drivers/gpu/drm/bridge/ite-it6505.o: In function `it6505_get_dpcd': ite-it6505.c:(.text+0x4a4): undefined reference to `drm_dp_dpcd_read' drivers/gpu/drm/bridge/ite-it6505.o: In function `it6505_dpcd_write': ite-it6505.c:(.text+0x52c): undefined reference to `drm_dp_dpcd_write' Select DRM_DP_HELPER for DRM_ITE_IT6505 to fix this. Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver") Signed-off-by: YueHaibing Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220317094724.25972-1-yuehaibing@huawei.com --- drivers/gpu/drm/bridge/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index c86f5be4dfe0..1b8721a7a6df 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -78,6 +78,7 @@ config DRM_DISPLAY_CONNECTOR config DRM_ITE_IT6505 tristate "ITE IT6505 DisplayPort bridge" depends on OF + select DRM_DP_HELPER select DRM_KMS_HELPER select EXTCON help -- cgit From 6135ee07702781cd8206bcb1c202c4630d6e3c9c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:36 +0100 Subject: drm/vc4: hdmi: Rename pixel_rate variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pixel_rate field in the vc4_hdmi_connector_state struct actually stores the TMDS character rate, let's rename it for consistency. Suggested-by: Ville Syrjälä Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-2-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 40 +++++++++++++++++++------------------- drivers/gpu/drm/vc4/vc4_hdmi.h | 2 +- drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 335385f5b3fb..c70e5059b077 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -343,7 +343,7 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) if (!new_state) return NULL; - new_state->pixel_rate = vc4_state->pixel_rate; + new_state->tmds_char_rate = vc4_state->tmds_char_rate; __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); return &new_state->base; @@ -1028,7 +1028,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, struct vc4_hdmi_connector_state *vc4_conn_state = conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; - unsigned long pixel_rate = vc4_conn_state->pixel_rate; + unsigned long tmds_char_rate = vc4_conn_state->tmds_char_rate; unsigned long bvb_rate, hsm_rate; unsigned long flags; int ret; @@ -1051,7 +1051,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, * Additionally, the AXI clock needs to be at least 25% of * pixel clock, but HSM ends up being the limiting factor. */ - hsm_rate = max_t(unsigned long, 120000000, (pixel_rate / 100) * 101); + hsm_rate = max_t(unsigned long, 120000000, (tmds_char_rate / 100) * 101); ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate); if (ret) { DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); @@ -1064,7 +1064,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, goto out; } - ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate); + ret = clk_set_rate(vc4_hdmi->pixel_clock, tmds_char_rate); if (ret) { DRM_ERROR("Failed to set pixel clock rate: %d\n", ret); goto err_put_runtime_pm; @@ -1079,9 +1079,9 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, vc4_hdmi_cec_update_clk_div(vc4_hdmi); - if (pixel_rate > 297000000) + if (tmds_char_rate > 297000000) bvb_rate = 300000000; - else if (pixel_rate > 148500000) + else if (tmds_char_rate > 148500000) bvb_rate = 150000000; else bvb_rate = 75000000; @@ -1255,8 +1255,8 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - unsigned long long pixel_rate = mode->clock * 1000; - unsigned long long tmds_rate; + unsigned long long tmds_char_rate = mode->clock * 1000; + unsigned long long tmds_bit_rate; if (vc4_hdmi->variant->unsupported_odd_h_timings && ((mode->hdisplay % 2) || (mode->hsync_start % 2) || @@ -1269,32 +1269,32 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, * bandwidth). Slightly lower the frequency to bring it out of * the WiFi range. */ - tmds_rate = pixel_rate * 10; + tmds_bit_rate = tmds_char_rate * 10; if (vc4_hdmi->disable_wifi_frequencies && - (tmds_rate >= WIFI_2_4GHz_CH1_MIN_FREQ && - tmds_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) { + (tmds_bit_rate >= WIFI_2_4GHz_CH1_MIN_FREQ && + tmds_bit_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) { mode->clock = 238560; - pixel_rate = mode->clock * 1000; + tmds_char_rate = mode->clock * 1000; } if (conn_state->max_bpc == 12) { - pixel_rate = pixel_rate * 150; - do_div(pixel_rate, 100); + tmds_char_rate = tmds_char_rate * 150; + do_div(tmds_char_rate, 100); } else if (conn_state->max_bpc == 10) { - pixel_rate = pixel_rate * 125; - do_div(pixel_rate, 100); + tmds_char_rate = tmds_char_rate * 125; + do_div(tmds_char_rate, 100); } if (mode->flags & DRM_MODE_FLAG_DBLCLK) - pixel_rate = pixel_rate * 2; + tmds_char_rate = tmds_char_rate * 2; - if (pixel_rate > vc4_hdmi->variant->max_pixel_clock) + if (tmds_char_rate > vc4_hdmi->variant->max_pixel_clock) return -EINVAL; - if (vc4_hdmi->disable_4kp60 && (pixel_rate > HDMI_14_MAX_TMDS_CLK)) + if (vc4_hdmi->disable_4kp60 && (tmds_char_rate > HDMI_14_MAX_TMDS_CLK)) return -EINVAL; - vc4_state->pixel_rate = pixel_rate; + vc4_state->tmds_char_rate = tmds_char_rate; return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 2b6aaafc020a..37a87d7e167a 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -234,7 +234,7 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder) struct vc4_hdmi_connector_state { struct drm_connector_state base; - unsigned long long pixel_rate; + unsigned long long tmds_char_rate; }; static inline struct vc4_hdmi_connector_state * diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c index 62148f0dc284..ec24999bf96d 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c @@ -365,7 +365,7 @@ void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, { const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings; const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; - unsigned long long pixel_freq = conn_state->pixel_rate; + unsigned long long pixel_freq = conn_state->tmds_char_rate; unsigned long long vco_freq; unsigned char word_sel; unsigned long flags; -- cgit From ccbf364f329cd43c9b148a2613881a8890f97237 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:37 +0100 Subject: drm/vc4: hdmi: Move clock validation to its own function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our code is doing the same clock rate validation in multiple instances. Let's create a helper to share the rate validation. Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-3-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index c70e5059b077..d4cebb524578 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1245,6 +1245,19 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, mutex_unlock(&vc4_hdmi->mutex); } +static enum drm_mode_status +vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, + unsigned long long clock) +{ + if (clock > vc4_hdmi->variant->max_pixel_clock) + return MODE_CLOCK_HIGH; + + if (vc4_hdmi->disable_4kp60 && clock > HDMI_14_MAX_TMDS_CLK) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL @@ -1288,10 +1301,7 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, if (mode->flags & DRM_MODE_FLAG_DBLCLK) tmds_char_rate = tmds_char_rate * 2; - if (tmds_char_rate > vc4_hdmi->variant->max_pixel_clock) - return -EINVAL; - - if (vc4_hdmi->disable_4kp60 && (tmds_char_rate > HDMI_14_MAX_TMDS_CLK)) + if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, tmds_char_rate) != MODE_OK) return -EINVAL; vc4_state->tmds_char_rate = tmds_char_rate; @@ -1310,13 +1320,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, (mode->hsync_end % 2) || (mode->htotal % 2))) return MODE_H_ILLEGAL; - if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock) - return MODE_CLOCK_HIGH; - - if (vc4_hdmi->disable_4kp60 && vc4_hdmi_mode_needs_scrambling(mode)) - return MODE_CLOCK_HIGH; - - return MODE_OK; + return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode->clock * 1000); } static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { -- cgit From 595dcf4eb859d043de9919f50d44272304e75422 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:38 +0100 Subject: drm/vc4: hdmi: Move clock calculation into its own function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code to compute our clock rate for a given setup will be called in multiple places in the next patches, so let's create a separate function for it. Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-4-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 52 ++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index d4cebb524578..e5d9d54ce20a 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1258,6 +1258,38 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, return MODE_OK; } +static unsigned long long +vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc) +{ + unsigned long long clock = mode->clock * 1000; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + clock = clock * 2; + + clock = clock * bpc; + do_div(clock, 8); + + return clock; +} + +static int +vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, + struct vc4_hdmi_connector_state *vc4_state, + const struct drm_display_mode *mode, + unsigned int bpc) +{ + unsigned long long clock; + + clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc); + if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK) + return -EINVAL; + + vc4_state->tmds_char_rate = clock; + + return 0; +} + #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL @@ -1270,6 +1302,7 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); unsigned long long tmds_char_rate = mode->clock * 1000; unsigned long long tmds_bit_rate; + int ret; if (vc4_hdmi->variant->unsupported_odd_h_timings && ((mode->hdisplay % 2) || (mode->hsync_start % 2) || @@ -1290,21 +1323,10 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, tmds_char_rate = mode->clock * 1000; } - if (conn_state->max_bpc == 12) { - tmds_char_rate = tmds_char_rate * 150; - do_div(tmds_char_rate, 100); - } else if (conn_state->max_bpc == 10) { - tmds_char_rate = tmds_char_rate * 125; - do_div(tmds_char_rate, 100); - } - - if (mode->flags & DRM_MODE_FLAG_DBLCLK) - tmds_char_rate = tmds_char_rate * 2; - - if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, tmds_char_rate) != MODE_OK) - return -EINVAL; - - vc4_state->tmds_char_rate = tmds_char_rate; + ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, mode, + conn_state->max_bpc); + if (ret) + return ret; return 0; } -- cgit From 15a809211a6971cac5262c309aeadddc158c64d9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:39 +0100 Subject: drm/vc4: hdmi: Take the sink maximum TMDS clock into account MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the function that validates that the clock isn't too high, we've only taken our controller limitations into account so far. However, the sink can have a limit on the maximum TMDS clock it can deal with too which is exposed through the EDID and the drm_display_info. Make sure we check it. Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-5-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e5d9d54ce20a..e8e70727b5f3 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1249,12 +1249,18 @@ static enum drm_mode_status vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, unsigned long long clock) { + const struct drm_connector *connector = &vc4_hdmi->connector; + const struct drm_display_info *info = &connector->display_info; + if (clock > vc4_hdmi->variant->max_pixel_clock) return MODE_CLOCK_HIGH; if (vc4_hdmi->disable_4kp60 && clock > HDMI_14_MAX_TMDS_CLK) return MODE_CLOCK_HIGH; + if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000)) + return MODE_CLOCK_HIGH; + return MODE_OK; } -- cgit From f785dcfc596c378a19af01a6a337b8b67e5e4534 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:40 +0100 Subject: drm/vc4: hdmi: Take bpp into account for the scrambler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code only base its decision for whether the scrambler must be enabled or not on the pixel clock of the mode, but doesn't take the bits per color into account. Let's leverage the new function to compute the clock rate in the scrambler setup code. Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-6-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 16 ++++++++++++---- drivers/gpu/drm/vc4/vc4_hdmi.h | 5 +++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e8e70727b5f3..e58488416883 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -99,9 +99,16 @@ #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) -static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode) +static unsigned long long +vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc); + +static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, + unsigned int bpc) { - return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK; + unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc); + + return clock > HDMI_14_MAX_TMDS_CLK; } static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi, @@ -272,7 +279,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) struct drm_display_mode *mode; list_for_each_entry(mode, &connector->probed_modes, head) { - if (vc4_hdmi_mode_needs_scrambling(mode)) { + if (vc4_hdmi_mode_needs_scrambling(mode, 8)) { drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz."); drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60."); } @@ -613,7 +620,7 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) if (!vc4_hdmi_supports_scrambling(encoder, mode)) return; - if (!vc4_hdmi_mode_needs_scrambling(mode)) + if (!vc4_hdmi_mode_needs_scrambling(mode, vc4_hdmi->output_bpc)) return; drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true); @@ -1242,6 +1249,7 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, mutex_lock(&vc4_hdmi->mutex); drm_mode_copy(&vc4_hdmi->saved_adjusted_mode, &crtc_state->adjusted_mode); + vc4_hdmi->output_bpc = conn_state->max_bpc; mutex_unlock(&vc4_hdmi->mutex); } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 37a87d7e167a..cb744bc5489b 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -216,6 +216,11 @@ struct vc4_hdmi { * the scrambler on? Protected by @mutex. */ bool scdc_enabled; + + /** + * @output_bpc: BPC currently being used. Protected by @mutex. + */ + unsigned int output_bpc; }; static inline struct vc4_hdmi * -- cgit From dd39d024a8471e88951f8494f19e693c9e3185a9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:41 +0100 Subject: drm/vc4: hdmi: Always try to have the highest bpc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we take the max_bpc property as the bpc value and do not try anything else. However, what the other drivers seem to be doing is that they would try with the highest bpc allowed by the max_bpc property and the hardware capabilities, test if it results in an acceptable configuration, and if not decrease the bpc and try again. Let's use the same logic. Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-7-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 44 ++++++++++++++++++++++++++++++++++++++---- drivers/gpu/drm/vc4/vc4_hdmi.h | 4 +++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e58488416883..da36f529e7c2 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -351,6 +351,7 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) return NULL; new_state->tmds_char_rate = vc4_state->tmds_char_rate; + new_state->output_bpc = vc4_state->output_bpc; __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); return &new_state->base; @@ -905,6 +906,8 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, struct drm_display_mode *mode) { + const struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(state); bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; @@ -952,7 +955,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_VERTB0, vertb_even); HDMI_WRITE(HDMI_VERTB1, vertb); - switch (state->max_bpc) { + switch (vc4_state->output_bpc) { case 12: gcp = 6; gcp_en = true; @@ -1245,11 +1248,13 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, struct drm_connector_state *conn_state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(conn_state); mutex_lock(&vc4_hdmi->mutex); drm_mode_copy(&vc4_hdmi->saved_adjusted_mode, &crtc_state->adjusted_mode); - vc4_hdmi->output_bpc = conn_state->max_bpc; + vc4_hdmi->output_bpc = vc4_state->output_bpc; mutex_unlock(&vc4_hdmi->mutex); } @@ -1304,6 +1309,38 @@ vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, return 0; } +static int +vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi, + struct vc4_hdmi_connector_state *vc4_state, + const struct drm_display_mode *mode) +{ + struct drm_connector_state *conn_state = &vc4_state->base; + unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12); + unsigned int bpc; + int ret; + + for (bpc = max_bpc; bpc >= 8; bpc -= 2) { + drm_dbg(dev, "Trying with a %d bpc output\n", bpc); + + ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, + mode, bpc); + if (ret) + continue; + + vc4_state->output_bpc = bpc; + + drm_dbg(dev, + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, clock: %llu\n", + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), + vc4_state->output_bpc, + vc4_state->tmds_char_rate); + + break; + } + + return ret; +} + #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL @@ -1337,8 +1374,7 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, tmds_char_rate = mode->clock * 1000; } - ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, mode, - conn_state->max_bpc); + ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode); if (ret) return ret; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index cb744bc5489b..79a1a10c40a8 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -218,7 +218,8 @@ struct vc4_hdmi { bool scdc_enabled; /** - * @output_bpc: BPC currently being used. Protected by @mutex. + * @output_bpc: Copy of @vc4_connector_state.output_bpc for use + * outside of KMS hooks. Protected by @mutex. */ unsigned int output_bpc; }; @@ -240,6 +241,7 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder) struct vc4_hdmi_connector_state { struct drm_connector_state base; unsigned long long tmds_char_rate; + unsigned int output_bpc; }; static inline struct vc4_hdmi_connector_state * -- cgit From e769a3504e273309c9fe4a417d427c8dbff4cf1c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 22 Feb 2022 17:40:42 +0100 Subject: drm/vc4: hdmi: Support HDMI YUV output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition to the RGB444 output, the BCM2711 HDMI controller supports the YUV444 and YUV422 output formats. Let's add support for them in the driver, but still use RGB as the preferred format. Signed-off-by: Maxime Ripard Acked-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220222164042.403112-8-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 291 ++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/vc4/vc4_hdmi.h | 14 ++ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 6 + drivers/gpu/drm/vc4/vc4_regs.h | 16 ++ 4 files changed, 311 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index da36f529e7c2..cf4ba83420d5 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -99,14 +99,30 @@ #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) +static const char * const output_format_str[] = { + [VC4_HDMI_OUTPUT_RGB] = "RGB", + [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0", + [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2", + [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4", +}; + +static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt) +{ + if (fmt >= ARRAY_SIZE(output_format_str)) + return "invalid"; + + return output_format_str[fmt]; +} + static unsigned long long vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, - unsigned int bpc); + unsigned int bpc, enum vc4_hdmi_output_format fmt); static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, - unsigned int bpc) + unsigned int bpc, + enum vc4_hdmi_output_format fmt) { - unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc); + unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); return clock > HDMI_14_MAX_TMDS_CLK; } @@ -279,7 +295,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) struct drm_display_mode *mode; list_for_each_entry(mode, &connector->probed_modes, head) { - if (vc4_hdmi_mode_needs_scrambling(mode, 8)) { + if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) { drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz."); drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60."); } @@ -336,6 +352,7 @@ static void vc4_hdmi_connector_reset(struct drm_connector *connector) new_state->base.max_bpc = 8; new_state->base.max_requested_bpc = 8; + new_state->output_format = VC4_HDMI_OUTPUT_RGB; drm_atomic_helper_connector_tv_reset(connector); } @@ -352,6 +369,7 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) new_state->tmds_char_rate = vc4_state->tmds_char_rate; new_state->output_bpc = vc4_state->output_bpc; + new_state->output_format = vc4_state->output_format; __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); return &new_state->base; @@ -495,11 +513,38 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); } +static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, + enum vc4_hdmi_output_format fmt) +{ + switch (fmt) { + case VC4_HDMI_OUTPUT_RGB: + frame->colorspace = HDMI_COLORSPACE_RGB; + break; + + case VC4_HDMI_OUTPUT_YUV420: + frame->colorspace = HDMI_COLORSPACE_YUV420; + break; + + case VC4_HDMI_OUTPUT_YUV422: + frame->colorspace = HDMI_COLORSPACE_YUV422; + break; + + case VC4_HDMI_OUTPUT_YUV444: + frame->colorspace = HDMI_COLORSPACE_YUV444; + break; + + default: + break; + } +} + static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector_state *cstate = connector->state; + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(cstate); const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; union hdmi_infoframe frame; int ret; @@ -519,6 +564,7 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) HDMI_QUANTIZATION_RANGE_FULL : HDMI_QUANTIZATION_RANGE_LIMITED); drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate); + vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format); drm_hdmi_avi_infoframe_bars(&frame.avi, cstate); vc4_hdmi_write_infoframe(encoder, &frame); @@ -621,7 +667,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) if (!vc4_hdmi_supports_scrambling(encoder, mode)) return; - if (!vc4_hdmi_mode_needs_scrambling(mode, vc4_hdmi->output_bpc)) + if (!vc4_hdmi_mode_needs_scrambling(mode, + vc4_hdmi->output_bpc, + vc4_hdmi->output_format)) return; drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true); @@ -816,6 +864,39 @@ static const u16 vc5_hdmi_csc_full_rgb_to_limited_rgb[3][4] = { { 0x0000, 0x0000, 0x1b80, 0x0400 }, }; +/* + * Conversion between Full Range RGB and Full Range YUV422 using the + * BT.709 Colorspace + * + * + * [ 0.181906 0.611804 0.061758 16 ] + * [ -0.100268 -0.337232 0.437500 128 ] + * [ 0.437500 -0.397386 -0.040114 128 ] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_limited_yuv422_bt709[3][4] = { + { 0x05d2, 0x1394, 0x01fa, 0x0400 }, + { 0xfccc, 0xf536, 0x0e00, 0x2000 }, + { 0x0e00, 0xf34a, 0xfeb8, 0x2000 }, +}; + +/* + * Conversion between Full Range RGB and Full Range YUV444 using the + * BT.709 Colorspace + * + * [ -0.100268 -0.337232 0.437500 128 ] + * [ 0.437500 -0.397386 -0.040114 128 ] + * [ 0.181906 0.611804 0.061758 16 ] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_limited_yuv444_bt709[3][4] = { + { 0xfccc, 0xf536, 0x0e00, 0x2000 }, + { 0x0e00, 0xf34a, 0xfeb8, 0x2000 }, + { 0x05d2, 0x1394, 0x01fa, 0x0400 }, +}; + static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi, const u16 coeffs[3][4]) { @@ -833,19 +914,53 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, const struct drm_display_mode *mode) { + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(state); unsigned long flags; + u32 if_cfg = 0; + u32 if_xbar = 0x543210; + u32 csc_chan_ctl = 0; u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, VC5_MT_CP_CSC_CTL_MODE); spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); - HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021); + switch (vc4_state->output_format) { + case VC4_HDMI_OUTPUT_YUV444: + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_yuv444_bt709); + break; - if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) - vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb); - else - vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity); + case VC4_HDMI_OUTPUT_YUV422: + csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD, + VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) | + VC5_MT_CP_CSC_CTL_USE_444_TO_422 | + VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION; + + csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE, + VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP); + + if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY, + VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422); + + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_yuv422_bt709); + break; + + case VC4_HDMI_OUTPUT_RGB: + if_xbar = 0x354021; + if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb); + else + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity); + break; + + default: + break; + } + + HDMI_WRITE(HDMI_VEC_INTERFACE_CFG, if_cfg); + HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, if_xbar); + HDMI_WRITE(HDMI_CSC_CHANNEL_CTL, csc_chan_ctl); HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); @@ -971,6 +1086,15 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, break; } + /* + * YCC422 is always 36-bit and not considered deep colour so + * doesn't signal in GCP. + */ + if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) { + gcp = 4; + gcp_en = false; + } + reg = HDMI_READ(HDMI_DEEP_COLOR_CONFIG_1); reg &= ~(VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK | VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK); @@ -1255,9 +1379,94 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, drm_mode_copy(&vc4_hdmi->saved_adjusted_mode, &crtc_state->adjusted_mode); vc4_hdmi->output_bpc = vc4_state->output_bpc; + vc4_hdmi->output_format = vc4_state->output_format; mutex_unlock(&vc4_hdmi->mutex); } +static bool +vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi, + const struct drm_display_info *info, + const struct drm_display_mode *mode, + unsigned int format, unsigned int bpc) +{ + struct drm_device *dev = vc4_hdmi->connector.dev; + u8 vic = drm_match_cea_mode(mode); + + if (vic == 1 && bpc != 8) { + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc); + return false; + } + + if (!info->is_hdmi && + (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) { + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n"); + return false; + } + + switch (format) { + case VC4_HDMI_OUTPUT_RGB: + drm_dbg(dev, "RGB Format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444)) + return false; + + if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) { + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) { + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + drm_dbg(dev, "RGB format supported in that configuration.\n"); + + return true; + + case VC4_HDMI_OUTPUT_YUV422: + drm_dbg(dev, "YUV422 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) { + drm_dbg(dev, "Sink doesn't support YUV422.\n"); + return false; + } + + if (bpc != 12) { + drm_dbg(dev, "YUV422 only supports 12 bpc.\n"); + return false; + } + + drm_dbg(dev, "YUV422 format supported in that configuration.\n"); + + return true; + + case VC4_HDMI_OUTPUT_YUV444: + drm_dbg(dev, "YUV444 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) { + drm_dbg(dev, "Sink doesn't support YUV444.\n"); + return false; + } + + if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) { + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) { + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + drm_dbg(dev, "YUV444 format supported in that configuration.\n"); + + return true; + } + + return false; +} + static enum drm_mode_status vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, unsigned long long clock) @@ -1279,13 +1488,17 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, static unsigned long long vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, - unsigned int bpc) + unsigned int bpc, + enum vc4_hdmi_output_format fmt) { unsigned long long clock = mode->clock * 1000; if (mode->flags & DRM_MODE_FLAG_DBLCLK) clock = clock * 2; + if (fmt == VC4_HDMI_OUTPUT_YUV422) + bpc = 8; + clock = clock * bpc; do_div(clock, 8); @@ -1296,11 +1509,11 @@ static int vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, struct vc4_hdmi_connector_state *vc4_state, const struct drm_display_mode *mode, - unsigned int bpc) + unsigned int bpc, unsigned int fmt) { unsigned long long clock; - clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc); + clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK) return -EINVAL; @@ -1309,11 +1522,56 @@ vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, return 0; } +static int +vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi, + struct vc4_hdmi_connector_state *vc4_state, + const struct drm_display_mode *mode, + unsigned int bpc) +{ + struct drm_device *dev = vc4_hdmi->connector.dev; + const struct drm_connector *connector = &vc4_hdmi->connector; + const struct drm_display_info *info = &connector->display_info; + unsigned int format; + + drm_dbg(dev, "Trying with an RGB output\n"); + + format = VC4_HDMI_OUTPUT_RGB; + if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { + int ret; + + ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, + mode, bpc, format); + if (!ret) { + vc4_state->output_format = format; + return 0; + } + } + + drm_dbg(dev, "Failed, Trying with an YUV422 output\n"); + + format = VC4_HDMI_OUTPUT_YUV422; + if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { + int ret; + + ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, + mode, bpc, format); + if (!ret) { + vc4_state->output_format = format; + return 0; + } + } + + drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n"); + + return -EINVAL; +} + static int vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi, struct vc4_hdmi_connector_state *vc4_state, const struct drm_display_mode *mode) { + struct drm_device *dev = vc4_hdmi->connector.dev; struct drm_connector_state *conn_state = &vc4_state->base; unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12); unsigned int bpc; @@ -1322,17 +1580,18 @@ vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi, for (bpc = max_bpc; bpc >= 8; bpc -= 2) { drm_dbg(dev, "Trying with a %d bpc output\n", bpc); - ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, - mode, bpc); + ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state, + mode, bpc); if (ret) continue; vc4_state->output_bpc = bpc; drm_dbg(dev, - "Mode %ux%u @ %uHz: Found configuration: bpc: %u, clock: %llu\n", + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), vc4_state->output_bpc, + vc4_hdmi_output_fmt_str(vc4_state->output_format), vc4_state->tmds_char_rate); break; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 79a1a10c40a8..b642971a811e 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -117,6 +117,13 @@ struct vc4_hdmi_audio { bool streaming; }; +enum vc4_hdmi_output_format { + VC4_HDMI_OUTPUT_RGB, + VC4_HDMI_OUTPUT_YUV422, + VC4_HDMI_OUTPUT_YUV444, + VC4_HDMI_OUTPUT_YUV420, +}; + /* General HDMI hardware state. */ struct vc4_hdmi { struct vc4_hdmi_audio audio; @@ -222,6 +229,12 @@ struct vc4_hdmi { * outside of KMS hooks. Protected by @mutex. */ unsigned int output_bpc; + + /** + * @output_format: Copy of @vc4_connector_state.output_format + * for use outside of KMS hooks. Protected by @mutex. + */ + enum vc4_hdmi_output_format output_format; }; static inline struct vc4_hdmi * @@ -242,6 +255,7 @@ struct vc4_hdmi_connector_state { struct drm_connector_state base; unsigned long long tmds_char_rate; unsigned int output_bpc; + enum vc4_hdmi_output_format output_format; }; static inline struct vc4_hdmi_connector_state * diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index fc971506bd4f..a040356b6bdc 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -54,6 +54,7 @@ enum vc4_hdmi_field { HDMI_CSC_24_23, HDMI_CSC_32_31, HDMI_CSC_34_33, + HDMI_CSC_CHANNEL_CTL, HDMI_CSC_CTL, /* @@ -119,6 +120,7 @@ enum vc4_hdmi_field { HDMI_TX_PHY_POWERDOWN_CTL, HDMI_TX_PHY_RESET_CTL, HDMI_TX_PHY_TMDS_CLK_WORD_SEL, + HDMI_VEC_INTERFACE_CFG, HDMI_VEC_INTERFACE_XBAR, HDMI_VERTA0, HDMI_VERTA1, @@ -244,6 +246,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = { VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), + VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), @@ -289,6 +292,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = { VC5_CSC_REG(HDMI_CSC_24_23, 0x010), VC5_CSC_REG(HDMI_CSC_32_31, 0x014), VC5_CSC_REG(HDMI_CSC_34_33, 0x018), + VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c), }; static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { @@ -324,6 +328,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), + VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), @@ -369,6 +374,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { VC5_CSC_REG(HDMI_CSC_24_23, 0x010), VC5_CSC_REG(HDMI_CSC_32_31, 0x014), VC5_CSC_REG(HDMI_CSC_34_33, 0x018), + VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c), }; static inline diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 33410718089e..c8210247cf24 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -774,11 +774,27 @@ enum { # define VC4_HD_CSC_CTL_RGB2YCC BIT(1) # define VC4_HD_CSC_CTL_ENABLE BIT(0) +# define VC5_MT_CP_CSC_CTL_USE_444_TO_422 BIT(6) +# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_MASK \ + VC4_MASK(5, 4) +# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD \ + 3 +# define VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION BIT(3) # define VC5_MT_CP_CSC_CTL_ENABLE BIT(2) # define VC5_MT_CP_CSC_CTL_MODE_MASK VC4_MASK(1, 0) +# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_MASK \ + VC4_MASK(7, 6) +# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE \ + 2 + # define VC4_DVP_HT_CLOCK_STOP_PIXEL BIT(1) +# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_MASK \ + VC4_MASK(3, 2) +# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY \ + 2 + /* HVS display list information. */ #define HVS_BOOTLOADER_DLIST_END 32 -- cgit From bbac58698a55cc0a6f0c0d69a6dcd3f9f3134c11 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 8 Mar 2022 13:36:38 +0800 Subject: btrfs: remove device item and update super block in the same transaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [BUG] There is a report that a btrfs has a bad super block num devices. This makes btrfs to reject the fs completely. BTRFS error (device sdd3): super_num_devices 3 mismatch with num_devices 2 found here BTRFS error (device sdd3): failed to read chunk tree: -22 BTRFS error (device sdd3): open_ctree failed [CAUSE] During btrfs device removal, chunk tree and super block num devs are updated in two different transactions: btrfs_rm_device() |- btrfs_rm_dev_item(device) | |- trans = btrfs_start_transaction() | | Now we got transaction X | | | |- btrfs_del_item() | | Now device item is removed from chunk tree | | | |- btrfs_commit_transaction() | Transaction X got committed, super num devs untouched, | but device item removed from chunk tree. | (AKA, super num devs is already incorrect) | |- cur_devices->num_devices--; |- cur_devices->total_devices--; |- btrfs_set_super_num_devices() All those operations are not in transaction X, thus it will only be written back to disk in next transaction. So after the transaction X in btrfs_rm_dev_item() committed, but before transaction X+1 (which can be minutes away), a power loss happen, then we got the super num mismatch. [FIX] Instead of starting and committing a transaction inside btrfs_rm_dev_item(), start a transaction in side btrfs_rm_device() and pass it to btrfs_rm_dev_item(). And only commit the transaction after everything is done. Reported-by: Luca Béla Palkovics Link: https://lore.kernel.org/linux-btrfs/CA+8xDSpvdm_U0QLBAnrH=zqDq_cWCOH5TiV46CKmp3igr44okQ@mail.gmail.com/ CC: stable@vger.kernel.org # 4.14+ Reviewed-by: Anand Jain Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 65 +++++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 1be7cb2f955f..2cfbc74a3b4e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1896,23 +1896,18 @@ static void update_dev_time(const char *device_path) path_put(&path); } -static int btrfs_rm_dev_item(struct btrfs_device *device) +static int btrfs_rm_dev_item(struct btrfs_trans_handle *trans, + struct btrfs_device *device) { struct btrfs_root *root = device->fs_info->chunk_root; int ret; struct btrfs_path *path; struct btrfs_key key; - struct btrfs_trans_handle *trans; path = btrfs_alloc_path(); if (!path) return -ENOMEM; - trans = btrfs_start_transaction(root, 0); - if (IS_ERR(trans)) { - btrfs_free_path(path); - return PTR_ERR(trans); - } key.objectid = BTRFS_DEV_ITEMS_OBJECTID; key.type = BTRFS_DEV_ITEM_KEY; key.offset = device->devid; @@ -1923,21 +1918,12 @@ static int btrfs_rm_dev_item(struct btrfs_device *device) if (ret) { if (ret > 0) ret = -ENOENT; - btrfs_abort_transaction(trans, ret); - btrfs_end_transaction(trans); goto out; } ret = btrfs_del_item(trans, root, path); - if (ret) { - btrfs_abort_transaction(trans, ret); - btrfs_end_transaction(trans); - } - out: btrfs_free_path(path); - if (!ret) - ret = btrfs_commit_transaction(trans); return ret; } @@ -2078,6 +2064,7 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, struct btrfs_dev_lookup_args *args, struct block_device **bdev, fmode_t *mode) { + struct btrfs_trans_handle *trans; struct btrfs_device *device; struct btrfs_fs_devices *cur_devices; struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; @@ -2098,7 +2085,7 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, ret = btrfs_check_raid_min_devices(fs_info, num_devices - 1); if (ret) - goto out; + return ret; device = btrfs_find_device(fs_info->fs_devices, args); if (!device) { @@ -2106,27 +2093,22 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND; else ret = -ENOENT; - goto out; + return ret; } if (btrfs_pinned_by_swapfile(fs_info, device)) { btrfs_warn_in_rcu(fs_info, "cannot remove device %s (devid %llu) due to active swapfile", rcu_str_deref(device->name), device->devid); - ret = -ETXTBSY; - goto out; + return -ETXTBSY; } - if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { - ret = BTRFS_ERROR_DEV_TGT_REPLACE; - goto out; - } + if (test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) + return BTRFS_ERROR_DEV_TGT_REPLACE; if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) && - fs_info->fs_devices->rw_devices == 1) { - ret = BTRFS_ERROR_DEV_ONLY_WRITABLE; - goto out; - } + fs_info->fs_devices->rw_devices == 1) + return BTRFS_ERROR_DEV_ONLY_WRITABLE; if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { mutex_lock(&fs_info->chunk_mutex); @@ -2139,14 +2121,22 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, if (ret) goto error_undo; - /* - * TODO: the superblock still includes this device in its num_devices - * counter although write_all_supers() is not locked out. This - * could give a filesystem state which requires a degraded mount. - */ - ret = btrfs_rm_dev_item(device); - if (ret) + trans = btrfs_start_transaction(fs_info->chunk_root, 0); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); goto error_undo; + } + + ret = btrfs_rm_dev_item(trans, device); + if (ret) { + /* Any error in dev item removal is critical */ + btrfs_crit(fs_info, + "failed to remove device item for devid %llu: %d", + device->devid, ret); + btrfs_abort_transaction(trans, ret); + btrfs_end_transaction(trans); + return ret; + } clear_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); btrfs_scrub_cancel_dev(device); @@ -2229,7 +2219,8 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, free_fs_devices(cur_devices); } -out: + ret = btrfs_commit_transaction(trans); + return ret; error_undo: @@ -2240,7 +2231,7 @@ error_undo: device->fs_devices->rw_devices++; mutex_unlock(&fs_info->chunk_mutex); } - goto out; + return ret; } void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_device *srcdev) -- cgit From 05fd9564e9faf0f23b4676385e27d9405cef6637 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 14 Mar 2022 10:55:32 -0700 Subject: btrfs: fix fallocate to use file_modified to update permissions consistently Since the initial introduction of (posix) fallocate back at the turn of the century, it has been possible to use this syscall to change the user-visible contents of files. This can happen by extending the file size during a preallocation, or through any of the newer modes (punch, zero range). Because the call can be used to change file contents, we should treat it like we do any other modification to a file -- update the mtime, and drop set[ug]id privileges/capabilities. The VFS function file_modified() does all this for us if pass it a locked inode, so let's make fallocate drop permissions correctly. Reviewed-by: Filipe Manana Signed-off-by: Darrick J. Wong Signed-off-by: David Sterba --- fs/btrfs/file.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 9f455c96c974..380054c94e4b 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2957,8 +2957,9 @@ out: return ret; } -static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len) +static int btrfs_punch_hole(struct file *file, loff_t offset, loff_t len) { + struct inode *inode = file_inode(file); struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_state *cached_state = NULL; @@ -2990,6 +2991,10 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len) goto out_only_mutex; } + ret = file_modified(file); + if (ret) + goto out_only_mutex; + lockstart = round_up(offset, btrfs_inode_sectorsize(BTRFS_I(inode))); lockend = round_down(offset + len, btrfs_inode_sectorsize(BTRFS_I(inode))) - 1; @@ -3430,7 +3435,7 @@ static long btrfs_fallocate(struct file *file, int mode, return -EOPNOTSUPP; if (mode & FALLOC_FL_PUNCH_HOLE) - return btrfs_punch_hole(inode, offset, len); + return btrfs_punch_hole(file, offset, len); /* * Only trigger disk allocation, don't trigger qgroup reserve @@ -3452,6 +3457,10 @@ static long btrfs_fallocate(struct file *file, int mode, goto out; } + ret = file_modified(file); + if (ret) + goto out; + /* * TODO: Move these two operations after we have checked * accurate reserved space, or fallocate can still fail but -- cgit From 75a36a7d3ea904cef2e5b56af0c58cc60dcf947a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 15 Mar 2022 19:28:05 +0800 Subject: btrfs: avoid defragging extents whose next extents are not targets [BUG] There is a report that autodefrag is defragging single sector, which is completely waste of IO, and no help for defragging: btrfs-cleaner-808 defrag_one_locked_range: root=256 ino=651122 start=0 len=4096 [CAUSE] In defrag_collect_targets(), we check if the current range (A) can be merged with next one (B). If mergeable, we will add range A into target for defrag. However there is a catch for autodefrag, when checking mergeability against range B, we intentionally pass 0 as @newer_than, hoping to get a higher chance to merge with the next extent. But in the next iteration, range B will looked up by defrag_lookup_extent(), with non-zero @newer_than. And if range B is not really newer, it will rejected directly, causing only range A being defragged, while we expect to defrag both range A and B. [FIX] Since the root cause is the difference in check condition of defrag_check_next_extent() and defrag_collect_targets(), we fix it by: 1. Pass @newer_than to defrag_check_next_extent() 2. Pass @extent_thresh to defrag_check_next_extent() This makes the check between defrag_collect_targets() and defrag_check_next_extent() more consistent. While there is still some minor difference, the remaining checks are focus on runtime flags like writeback/delalloc, which are mostly transient and safe to be checked only in defrag_collect_targets(). Link: https://github.com/btrfs/linux/issues/423#issuecomment-1066981856 CC: stable@vger.kernel.org # 5.16+ Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 238cee5b5254..f46e71061942 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1239,7 +1239,7 @@ static u32 get_extent_max_capacity(const struct extent_map *em) } static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em, - bool locked) + u32 extent_thresh, u64 newer_than, bool locked) { struct extent_map *next; bool ret = false; @@ -1249,11 +1249,12 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em, return false; /* - * We want to check if the next extent can be merged with the current - * one, which can be an extent created in a past generation, so we pass - * a minimum generation of 0 to defrag_lookup_extent(). + * Here we need to pass @newer_then when checking the next extent, or + * we will hit a case we mark current extent for defrag, but the next + * one will not be a target. + * This will just cause extra IO without really reducing the fragments. */ - next = defrag_lookup_extent(inode, em->start + em->len, 0, locked); + next = defrag_lookup_extent(inode, em->start + em->len, newer_than, locked); /* No more em or hole */ if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE) goto out; @@ -1265,6 +1266,13 @@ static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em, */ if (next->len >= get_extent_max_capacity(em)) goto out; + /* Skip older extent */ + if (next->generation < newer_than) + goto out; + /* Also check extent size */ + if (next->len >= extent_thresh) + goto out; + ret = true; out: free_extent_map(next); @@ -1470,7 +1478,7 @@ static int defrag_collect_targets(struct btrfs_inode *inode, goto next; next_mergeable = defrag_check_next_extent(&inode->vfs_inode, em, - locked); + extent_thresh, newer_than, locked); if (!next_mergeable) { struct defrag_target_range *last; -- cgit From a7d16d9a07bbcb7dcd5214a1bea75c808830bc0d Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 23 Mar 2022 11:30:36 -0400 Subject: btrfs: do not warn for free space inode in cow_file_range This is a long time leftover from when I originally added the free space inode, the point was to catch cases where we weren't honoring the NOCOW flag. However there exists a race with relocation, if we allocate our free space inode in a block group that is about to be relocated, we could trigger the COW path before the relocation has the opportunity to find the extents and delete the free space cache. In production where we have auto-relocation enabled we're seeing this WARN_ON_ONCE() around 5k times in a 2 week period, so not super common but enough that it's at the top of our metrics. We're properly handling the error here, and with us phasing out v1 space cache anyway just drop the WARN_ON_ONCE. Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2e7143ff5523..b976f757571f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1128,7 +1128,6 @@ static noinline int cow_file_range(struct btrfs_inode *inode, int ret = 0; if (btrfs_is_free_space_inode(inode)) { - WARN_ON_ONCE(1); ret = -EINVAL; goto out_unlock; } -- cgit From 60021bd754c6ca0addc6817994f20290a321d8d6 Mon Sep 17 00:00:00 2001 From: Kaiwen Hu Date: Wed, 23 Mar 2022 15:10:32 +0800 Subject: btrfs: prevent subvol with swapfile from being deleted A subvolume with an active swapfile must not be deleted otherwise it would not be possible to deactivate it. After the subvolume is deleted, we cannot swapoff the swapfile in this deleted subvolume because the path is unreachable. The swapfile is still active and holding references, the filesystem cannot be unmounted. The test looks like this: mkfs.btrfs -f $dev > /dev/null mount $dev $mnt btrfs sub create $mnt/subvol touch $mnt/subvol/swapfile chmod 600 $mnt/subvol/swapfile chattr +C $mnt/subvol/swapfile dd if=/dev/zero of=$mnt/subvol/swapfile bs=1K count=4096 mkswap $mnt/subvol/swapfile swapon $mnt/subvol/swapfile btrfs sub delete $mnt/subvol swapoff $mnt/subvol/swapfile # failed: No such file or directory swapoff --all unmount $mnt # target is busy. To prevent above issue, we simply check that whether the subvolume contains any active swapfile, and stop the deleting process. This behavior is like snapshot ioctl dealing with a swapfile. CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Robbie Ko Reviewed-by: Qu Wenruo Reviewed-by: Filipe Manana Signed-off-by: Kaiwen Hu Signed-off-by: David Sterba --- fs/btrfs/inode.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b976f757571f..5aab6af88349 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4487,6 +4487,13 @@ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) dest->root_key.objectid); return -EPERM; } + if (atomic_read(&dest->nr_swapfiles)) { + spin_unlock(&dest->root_item_lock); + btrfs_warn(fs_info, + "attempt to delete subvolume %llu with active swapfile", + root->root_key.objectid); + return -EPERM; + } root_flags = btrfs_root_flags(&dest->root_item); btrfs_set_root_flags(&dest->root_item, root_flags | BTRFS_ROOT_SUBVOL_DEAD); @@ -11110,8 +11117,23 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, * set. We use this counter to prevent snapshots. We must increment it * before walking the extents because we don't want a concurrent * snapshot to run after we've already checked the extents. + * + * It is possible that subvolume is marked for deletion but still not + * removed yet. To prevent this race, we check the root status before + * activating the swapfile. */ + spin_lock(&root->root_item_lock); + if (btrfs_root_dead(root)) { + spin_unlock(&root->root_item_lock); + + btrfs_exclop_finish(fs_info); + btrfs_warn(fs_info, + "cannot activate swapfile because subvolume %llu is being deleted", + root->root_key.objectid); + return -EPERM; + } atomic_inc(&root->nr_swapfiles); + spin_unlock(&root->root_item_lock); isize = ALIGN_DOWN(inode->i_size, fs_info->sectorsize); -- cgit From 194d250cdc4a40ccbd179afd522a9e9846957402 Mon Sep 17 00:00:00 2001 From: Liu Zixian Date: Tue, 22 Mar 2022 17:17:30 +0800 Subject: drm/virtio: fix NULL pointer dereference in virtio_gpu_conn_get_modes drm_cvt_mode may return NULL and we should check it. This bug is found by syzkaller: FAULT_INJECTION stacktrace: [ 168.567394] FAULT_INJECTION: forcing a failure. name failslab, interval 1, probability 0, space 0, times 1 [ 168.567403] CPU: 1 PID: 6425 Comm: syz Kdump: loaded Not tainted 4.19.90-vhulk2201.1.0.h1035.kasan.eulerosv2r10.aarch64 #1 [ 168.567406] Hardware name: QEMU KVM Virtual Machine, BIOS 0.0.0 02/06/2015 [ 168.567408] Call trace: [ 168.567414] dump_backtrace+0x0/0x310 [ 168.567418] show_stack+0x28/0x38 [ 168.567423] dump_stack+0xec/0x15c [ 168.567427] should_fail+0x3ac/0x3d0 [ 168.567437] __should_failslab+0xb8/0x120 [ 168.567441] should_failslab+0x28/0xc0 [ 168.567445] kmem_cache_alloc_trace+0x50/0x640 [ 168.567454] drm_mode_create+0x40/0x90 [ 168.567458] drm_cvt_mode+0x48/0xc78 [ 168.567477] virtio_gpu_conn_get_modes+0xa8/0x140 [virtio_gpu] [ 168.567485] drm_helper_probe_single_connector_modes+0x3a4/0xd80 [ 168.567492] drm_mode_getconnector+0x2e0/0xa70 [ 168.567496] drm_ioctl_kernel+0x11c/0x1d8 [ 168.567514] drm_ioctl+0x558/0x6d0 [ 168.567522] do_vfs_ioctl+0x160/0xf30 [ 168.567525] ksys_ioctl+0x98/0xd8 [ 168.567530] __arm64_sys_ioctl+0x50/0xc8 [ 168.567536] el0_svc_common+0xc8/0x320 [ 168.567540] el0_svc_handler+0xf8/0x160 [ 168.567544] el0_svc+0x10/0x218 KASAN stacktrace: [ 168.567561] BUG: KASAN: null-ptr-deref in virtio_gpu_conn_get_modes+0xb4/0x140 [virtio_gpu] [ 168.567565] Read of size 4 at addr 0000000000000054 by task syz/6425 [ 168.567566] [ 168.567571] CPU: 1 PID: 6425 Comm: syz Kdump: loaded Not tainted 4.19.90-vhulk2201.1.0.h1035.kasan.eulerosv2r10.aarch64 #1 [ 168.567573] Hardware name: QEMU KVM Virtual Machine, BIOS 0.0.0 02/06/2015 [ 168.567575] Call trace: [ 168.567578] dump_backtrace+0x0/0x310 [ 168.567582] show_stack+0x28/0x38 [ 168.567586] dump_stack+0xec/0x15c [ 168.567591] kasan_report+0x244/0x2f0 [ 168.567594] __asan_load4+0x58/0xb0 [ 168.567607] virtio_gpu_conn_get_modes+0xb4/0x140 [virtio_gpu] [ 168.567612] drm_helper_probe_single_connector_modes+0x3a4/0xd80 [ 168.567617] drm_mode_getconnector+0x2e0/0xa70 [ 168.567621] drm_ioctl_kernel+0x11c/0x1d8 [ 168.567624] drm_ioctl+0x558/0x6d0 [ 168.567628] do_vfs_ioctl+0x160/0xf30 [ 168.567632] ksys_ioctl+0x98/0xd8 [ 168.567636] __arm64_sys_ioctl+0x50/0xc8 [ 168.567641] el0_svc_common+0xc8/0x320 [ 168.567645] el0_svc_handler+0xf8/0x160 [ 168.567649] el0_svc+0x10/0x218 Signed-off-by: Liu Zixian Link: http://patchwork.freedesktop.org/patch/msgid/20220322091730.1653-1-liuzixian4@huawei.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_display.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 5b00310ac4cd..f73352e7b832 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -179,6 +179,8 @@ static int virtio_gpu_conn_get_modes(struct drm_connector *connector) DRM_DEBUG("add mode: %dx%d\n", width, height); mode = drm_cvt_mode(connector->dev, width, height, 60, false, false, false); + if (!mode) + return count; mode->type |= DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); count++; -- cgit From ec3cbb30893be248d180dd748ee9148d408a99d1 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 19 Feb 2022 09:03:01 -0800 Subject: drm/virtio: Remove restriction of non-zero blob_flags With native userspace drivers in guest, a lot of GEM objects need to be neither shared nor mappable. And in fact making everything mappable and/or sharable results in unreasonably high fd usage in host VMM. Signed-off-by: Rob Clark Reviewed-by: Chia-I Wu Link: http://patchwork.freedesktop.org/patch/msgid/20220219170301.545432-1-robdclark@gmail.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_ioctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index c708bab555c6..77743fd2c61a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -609,8 +609,7 @@ static int verify_blob(struct virtio_gpu_device *vgdev, if (!vgdev->has_resource_blob) return -EINVAL; - if ((rc_blob->blob_flags & ~VIRTGPU_BLOB_FLAG_USE_MASK) || - !rc_blob->blob_flags) + if (rc_blob->blob_flags & ~VIRTGPU_BLOB_FLAG_USE_MASK) return -EINVAL; if (rc_blob->blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) { -- cgit From 59ab4ee012f0166873ae15d235b2450f21dcb5e7 Mon Sep 17 00:00:00 2001 From: Cong Liu Date: Thu, 24 Mar 2022 18:49:28 +0800 Subject: drm/qxl: fix qxl can't use in arm64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qxl use ioremap to map ram_header and rom, in the arm64 implementation, the device is mapped as DEVICE_nGnRE, it can not support unaligned access. and qxl is a virtual device, it can be treated more like RAM than actual MMIO registers. use ioremap_wc() replace it. Signed-off-by: Cong Liu Acked-by: Christian König Link: http://patchwork.freedesktop.org/patch/msgid/20220324104928.2959545-1-liucong2@kylinos.cn Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_kms.c | 4 ++-- drivers/gpu/drm/qxl/qxl_ttm.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 4dc5ad13f12c..a054e4a00fe8 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -165,7 +165,7 @@ int qxl_device_init(struct qxl_device *qdev, (int)qdev->surfaceram_size / 1024, (sb == 4) ? "64bit" : "32bit"); - qdev->rom = ioremap(qdev->rom_base, qdev->rom_size); + qdev->rom = ioremap_wc(qdev->rom_base, qdev->rom_size); if (!qdev->rom) { pr_err("Unable to ioremap ROM\n"); r = -ENOMEM; @@ -183,7 +183,7 @@ int qxl_device_init(struct qxl_device *qdev, goto rom_unmap; } - qdev->ram_header = ioremap(qdev->vram_base + + qdev->ram_header = ioremap_wc(qdev->vram_base + qdev->rom->ram_header_offset, sizeof(*qdev->ram_header)); if (!qdev->ram_header) { diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index b2e33d5ba5d0..95df5750f47f 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -82,13 +82,13 @@ int qxl_ttm_io_mem_reserve(struct ttm_device *bdev, case TTM_PL_VRAM: mem->bus.is_iomem = true; mem->bus.offset = (mem->start << PAGE_SHIFT) + qdev->vram_base; - mem->bus.caching = ttm_cached; + mem->bus.caching = ttm_write_combined; break; case TTM_PL_PRIV: mem->bus.is_iomem = true; mem->bus.offset = (mem->start << PAGE_SHIFT) + qdev->surfaceram_base; - mem->bus.caching = ttm_cached; + mem->bus.caching = ttm_write_combined; break; default: return -EINVAL; -- cgit From caaf2ae712b7cc3c7717898fe267dbf882a502ef Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 24 Jan 2022 14:03:24 +0100 Subject: dma-buf: Add dma_fence_array_for_each (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper to iterate over all fences in a dma_fence_array object. v2 (Jason Ekstrand) - Return NULL from dma_fence_array_first if head == NULL. This matches the iterator behavior of dma_fence_chain_for_each in that it iterates zero times if head == NULL. - Return NULL from dma_fence_array_next if index > array->num_fences. Signed-off-by: Jason Ekstrand Reviewed-by: Jason Ekstrand Reviewed-by: Christian König Cc: Daniel Vetter Cc: Maarten Lankhorst Link: https://patchwork.freedesktop.org/patch/msgid/20210610210925.642582-2-jason@jlekstrand.net Signed-off-by: Christian König --- drivers/dma-buf/dma-fence-array.c | 27 +++++++++++++++++++++++++++ include/linux/dma-fence-array.h | 17 +++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/dma-buf/dma-fence-array.c b/drivers/dma-buf/dma-fence-array.c index cb1bacb5a42b..52b85d292383 100644 --- a/drivers/dma-buf/dma-fence-array.c +++ b/drivers/dma-buf/dma-fence-array.c @@ -219,3 +219,30 @@ bool dma_fence_match_context(struct dma_fence *fence, u64 context) return true; } EXPORT_SYMBOL(dma_fence_match_context); + +struct dma_fence *dma_fence_array_first(struct dma_fence *head) +{ + struct dma_fence_array *array; + + if (!head) + return NULL; + + array = to_dma_fence_array(head); + if (!array) + return head; + + return array->fences[0]; +} +EXPORT_SYMBOL(dma_fence_array_first); + +struct dma_fence *dma_fence_array_next(struct dma_fence *head, + unsigned int index) +{ + struct dma_fence_array *array = to_dma_fence_array(head); + + if (!array || index >= array->num_fences) + return NULL; + + return array->fences[index]; +} +EXPORT_SYMBOL(dma_fence_array_next); diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h index fec374f69e12..e34dcb0bb462 100644 --- a/include/linux/dma-fence-array.h +++ b/include/linux/dma-fence-array.h @@ -61,6 +61,19 @@ to_dma_fence_array(struct dma_fence *fence) return container_of(fence, struct dma_fence_array, base); } +/** + * dma_fence_array_for_each - iterate over all fences in array + * @fence: current fence + * @index: index into the array + * @head: potential dma_fence_array object + * + * Test if @array is a dma_fence_array object and if yes iterate over all fences + * in the array. If not just iterate over the fence in @array itself. + */ +#define dma_fence_array_for_each(fence, index, head) \ + for (index = 0, fence = dma_fence_array_first(head); fence; \ + ++(index), fence = dma_fence_array_next(head, index)) + struct dma_fence_array *dma_fence_array_create(int num_fences, struct dma_fence **fences, u64 context, unsigned seqno, @@ -68,4 +81,8 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, bool dma_fence_match_context(struct dma_fence *fence, u64 context); +struct dma_fence *dma_fence_array_first(struct dma_fence *head); +struct dma_fence *dma_fence_array_next(struct dma_fence *head, + unsigned int index); + #endif /* __LINUX_DMA_FENCE_ARRAY_H */ -- cgit From 64a8f92fd783e750cdb81af75942dcd53bbf61bd Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 11 Mar 2022 10:27:53 +0100 Subject: dma-buf: add dma_fence_unwrap v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a general purpose helper to deep dive into dma_fence_chain/dma_fence_array structures and iterate over all the fences in them. This is useful when we need to flatten out all fences in those structures. v2: some selftests cleanup, improved function naming and documentation Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220311110244.1245-1-christian.koenig@amd.com --- Documentation/driver-api/dma-buf.rst | 6 + drivers/dma-buf/Makefile | 1 + drivers/dma-buf/selftests.h | 1 + drivers/dma-buf/st-dma-fence-unwrap.c | 261 ++++++++++++++++++++++++++++++++++ include/linux/dma-fence-array.h | 2 + include/linux/dma-fence-chain.h | 2 + include/linux/dma-fence-unwrap.h | 95 +++++++++++++ 7 files changed, 368 insertions(+) create mode 100644 drivers/dma-buf/st-dma-fence-unwrap.c create mode 100644 include/linux/dma-fence-unwrap.h diff --git a/Documentation/driver-api/dma-buf.rst b/Documentation/driver-api/dma-buf.rst index 55006678394a..36a76cbe9095 100644 --- a/Documentation/driver-api/dma-buf.rst +++ b/Documentation/driver-api/dma-buf.rst @@ -185,6 +185,12 @@ DMA Fence Chain .. kernel-doc:: include/linux/dma-fence-chain.h :internal: +DMA Fence unwrap +~~~~~~~~~~~~~~~~ + +.. kernel-doc:: include/linux/dma-fence-unwrap.h + :internal: + DMA Fence uABI/Sync File ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 511805dbeb75..4c9eb53ba3f8 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -12,6 +12,7 @@ dmabuf_selftests-y := \ selftest.o \ st-dma-fence.o \ st-dma-fence-chain.o \ + st-dma-fence-unwrap.o \ st-dma-resv.o obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h index 97d73aaa31da..851965867d9c 100644 --- a/drivers/dma-buf/selftests.h +++ b/drivers/dma-buf/selftests.h @@ -12,4 +12,5 @@ selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ selftest(dma_fence, dma_fence) selftest(dma_fence_chain, dma_fence_chain) +selftest(dma_fence_unwrap, dma_fence_unwrap) selftest(dma_resv, dma_resv) diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c new file mode 100644 index 000000000000..039f016b57be --- /dev/null +++ b/drivers/dma-buf/st-dma-fence-unwrap.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: MIT + +/* + * Copyright (C) 2022 Advanced Micro Devices, Inc. + */ + +#include +#if 0 +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "selftest.h" + +#define CHAIN_SZ (4 << 10) + +static inline struct mock_fence { + struct dma_fence base; + spinlock_t lock; +} *to_mock_fence(struct dma_fence *f) { + return container_of(f, struct mock_fence, base); +} + +static const char *mock_name(struct dma_fence *f) +{ + return "mock"; +} + +static const struct dma_fence_ops mock_ops = { + .get_driver_name = mock_name, + .get_timeline_name = mock_name, +}; + +static struct dma_fence *mock_fence(void) +{ + struct mock_fence *f; + + f = kmalloc(sizeof(*f), GFP_KERNEL); + if (!f) + return NULL; + + spin_lock_init(&f->lock); + dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0); + + return &f->base; +} + +static struct dma_fence *mock_array(unsigned int num_fences, ...) +{ + struct dma_fence_array *array; + struct dma_fence **fences; + va_list valist; + int i; + + fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL); + if (!fences) + return NULL; + + va_start(valist, num_fences); + for (i = 0; i < num_fences; ++i) + fences[i] = va_arg(valist, typeof(*fences)); + va_end(valist); + + array = dma_fence_array_create(num_fences, fences, + dma_fence_context_alloc(1), + 1, false); + if (!array) + goto cleanup; + return &array->base; + +cleanup: + for (i = 0; i < num_fences; ++i) + dma_fence_put(fences[i]); + kfree(fences); + return NULL; +} + +static struct dma_fence *mock_chain(struct dma_fence *prev, + struct dma_fence *fence) +{ + struct dma_fence_chain *f; + + f = dma_fence_chain_alloc(); + if (!f) { + dma_fence_put(prev); + dma_fence_put(fence); + return NULL; + } + + dma_fence_chain_init(f, prev, fence, 1); + return &f->base; +} + +static int sanitycheck(void *arg) +{ + struct dma_fence *f, *chain, *array; + int err = 0; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + array = mock_array(1, f); + if (!array) + return -ENOMEM; + + chain = mock_chain(NULL, array); + if (!chain) + return -ENOMEM; + + dma_fence_signal(f); + dma_fence_put(chain); + return err; +} + +static int unwrap_array(void *arg) +{ + struct dma_fence *fence, *f1, *f2, *array; + struct dma_fence_unwrap iter; + int err = 0; + + f1 = mock_fence(); + if (!f1) + return -ENOMEM; + + f2 = mock_fence(); + if (!f2) { + dma_fence_put(f1); + return -ENOMEM; + } + + array = mock_array(2, f1, f2); + if (!array) + return -ENOMEM; + + dma_fence_unwrap_for_each(fence, &iter, array) { + if (fence == f1) { + f1 = NULL; + } else if (fence == f2) { + f2 = NULL; + } else { + pr_err("Unexpected fence!\n"); + err = -EINVAL; + } + } + + if (f1 || f2) { + pr_err("Not all fences seen!\n"); + err = -EINVAL; + } + + dma_fence_signal(f1); + dma_fence_signal(f2); + dma_fence_put(array); + return 0; +} + +static int unwrap_chain(void *arg) +{ + struct dma_fence *fence, *f1, *f2, *chain; + struct dma_fence_unwrap iter; + int err = 0; + + f1 = mock_fence(); + if (!f1) + return -ENOMEM; + + f2 = mock_fence(); + if (!f2) { + dma_fence_put(f1); + return -ENOMEM; + } + + chain = mock_chain(f1, f2); + if (!chain) + return -ENOMEM; + + dma_fence_unwrap_for_each(fence, &iter, chain) { + if (fence == f1) { + f1 = NULL; + } else if (fence == f2) { + f2 = NULL; + } else { + pr_err("Unexpected fence!\n"); + err = -EINVAL; + } + } + + if (f1 || f2) { + pr_err("Not all fences seen!\n"); + err = -EINVAL; + } + + dma_fence_signal(f1); + dma_fence_signal(f2); + dma_fence_put(chain); + return 0; +} + +static int unwrap_chain_array(void *arg) +{ + struct dma_fence *fence, *f1, *f2, *array, *chain; + struct dma_fence_unwrap iter; + int err = 0; + + f1 = mock_fence(); + if (!f1) + return -ENOMEM; + + f2 = mock_fence(); + if (!f2) { + dma_fence_put(f1); + return -ENOMEM; + } + + array = mock_array(2, f1, f2); + if (!array) + return -ENOMEM; + + chain = mock_chain(NULL, array); + if (!chain) + return -ENOMEM; + + dma_fence_unwrap_for_each(fence, &iter, chain) { + if (fence == f1) { + f1 = NULL; + } else if (fence == f2) { + f2 = NULL; + } else { + pr_err("Unexpected fence!\n"); + err = -EINVAL; + } + } + + if (f1 || f2) { + pr_err("Not all fences seen!\n"); + err = -EINVAL; + } + + dma_fence_signal(f1); + dma_fence_signal(f2); + dma_fence_put(chain); + return 0; +} + +int dma_fence_unwrap(void) +{ + static const struct subtest tests[] = { + SUBTEST(sanitycheck), + SUBTEST(unwrap_array), + SUBTEST(unwrap_chain), + SUBTEST(unwrap_chain_array), + }; + + return subtests(tests, NULL); +} diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h index e34dcb0bb462..ec7f25def392 100644 --- a/include/linux/dma-fence-array.h +++ b/include/linux/dma-fence-array.h @@ -69,6 +69,8 @@ to_dma_fence_array(struct dma_fence *fence) * * Test if @array is a dma_fence_array object and if yes iterate over all fences * in the array. If not just iterate over the fence in @array itself. + * + * For a deep dive iterator see dma_fence_unwrap_for_each(). */ #define dma_fence_array_for_each(fence, index, head) \ for (index = 0, fence = dma_fence_array_first(head); fence; \ diff --git a/include/linux/dma-fence-chain.h b/include/linux/dma-fence-chain.h index 10d51bcdf7b7..4bdf0b96da28 100644 --- a/include/linux/dma-fence-chain.h +++ b/include/linux/dma-fence-chain.h @@ -112,6 +112,8 @@ static inline void dma_fence_chain_free(struct dma_fence_chain *chain) * * Iterate over all fences in the chain. We keep a reference to the current * fence while inside the loop which must be dropped when breaking out. + * + * For a deep dive iterator see dma_fence_unwrap_for_each(). */ #define dma_fence_chain_for_each(iter, head) \ for (iter = dma_fence_get(head); iter; \ diff --git a/include/linux/dma-fence-unwrap.h b/include/linux/dma-fence-unwrap.h new file mode 100644 index 000000000000..77e335a1bcac --- /dev/null +++ b/include/linux/dma-fence-unwrap.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * fence-chain: chain fences together in a timeline + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * Authors: + * Christian König + */ + +#ifndef __LINUX_DMA_FENCE_UNWRAP_H +#define __LINUX_DMA_FENCE_UNWRAP_H + +#include +#include + +/** + * struct dma_fence_unwrap - cursor into the container structure + * + * Should be used with dma_fence_unwrap_for_each() iterator macro. + */ +struct dma_fence_unwrap { + /** + * @chain: potential dma_fence_chain, but can be other fence as well + */ + struct dma_fence *chain; + /** + * @array: potential dma_fence_array, but can be other fence as well + */ + struct dma_fence *array; + /** + * @index: last returned index if @array is really a dma_fence_array + */ + unsigned int index; +}; + +/* Internal helper to start new array iteration, don't use directly */ +static inline struct dma_fence * +__dma_fence_unwrap_array(struct dma_fence_unwrap * cursor) +{ + cursor->array = dma_fence_chain_contained(cursor->chain); + cursor->index = 0; + return dma_fence_array_first(cursor->array); +} + +/** + * dma_fence_unwrap_first - return the first fence from fence containers + * @head: the entrypoint into the containers + * @cursor: current position inside the containers + * + * Unwraps potential dma_fence_chain/dma_fence_array containers and return the + * first fence. + */ +static inline struct dma_fence * +dma_fence_unwrap_first(struct dma_fence *head, struct dma_fence_unwrap *cursor) +{ + cursor->chain = dma_fence_get(head); + return __dma_fence_unwrap_array(cursor); +} + +/** + * dma_fence_unwrap_next - return the next fence from a fence containers + * @cursor: current position inside the containers + * + * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return + * the next fence from them. + */ +static inline struct dma_fence * +dma_fence_unwrap_next(struct dma_fence_unwrap *cursor) +{ + struct dma_fence *tmp; + + ++cursor->index; + tmp = dma_fence_array_next(cursor->array, cursor->index); + if (tmp) + return tmp; + + cursor->chain = dma_fence_chain_walk(cursor->chain); + return __dma_fence_unwrap_array(cursor); +} + +/** + * dma_fence_unwrap_for_each - iterate over all fences in containers + * @fence: current fence + * @cursor: current position inside the containers + * @head: starting point for the iterator + * + * Unwrap dma_fence_chain and dma_fence_array containers and deep dive into all + * potential fences in them. If @head is just a normal fence only that one is + * returned. + */ +#define dma_fence_unwrap_for_each(fence, cursor, head) \ + for (fence = dma_fence_unwrap_first(head, cursor); fence; \ + fence = dma_fence_unwrap_next(cursor)) + +#endif -- cgit From 519f490db07e1a539490612f376487f61e48e39c Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 11 Mar 2022 10:32:26 +0100 Subject: dma-buf/sync-file: fix warning about fence containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dma_fence_chain containers can show up in sync_files as well resulting in warnings that those can't be added to dma_fence_array containers when merging multiple sync_files together. Solve this by using the dma_fence_unwrap iterator to deep dive into the contained fences and then add those flatten out into a dma_fence_array. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220311110244.1245-2-christian.koenig@amd.com --- drivers/dma-buf/sync_file.c | 141 +++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 394e6e1e9686..b8dea4ec123b 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -5,6 +5,7 @@ * Copyright (C) 2012 Google, Inc. */ +#include #include #include #include @@ -172,20 +173,6 @@ static int sync_file_set_fence(struct sync_file *sync_file, return 0; } -static struct dma_fence **get_fences(struct sync_file *sync_file, - int *num_fences) -{ - if (dma_fence_is_array(sync_file->fence)) { - struct dma_fence_array *array = to_dma_fence_array(sync_file->fence); - - *num_fences = array->num_fences; - return array->fences; - } - - *num_fences = 1; - return &sync_file->fence; -} - static void add_fence(struct dma_fence **fences, int *i, struct dma_fence *fence) { @@ -210,86 +197,97 @@ static void add_fence(struct dma_fence **fences, static struct sync_file *sync_file_merge(const char *name, struct sync_file *a, struct sync_file *b) { + struct dma_fence *a_fence, *b_fence, **fences; + struct dma_fence_unwrap a_iter, b_iter; + unsigned int index, num_fences; struct sync_file *sync_file; - struct dma_fence **fences = NULL, **nfences, **a_fences, **b_fences; - int i = 0, i_a, i_b, num_fences, a_num_fences, b_num_fences; sync_file = sync_file_alloc(); if (!sync_file) return NULL; - a_fences = get_fences(a, &a_num_fences); - b_fences = get_fences(b, &b_num_fences); - if (a_num_fences > INT_MAX - b_num_fences) - goto err; + num_fences = 0; + dma_fence_unwrap_for_each(a_fence, &a_iter, a->fence) + ++num_fences; + dma_fence_unwrap_for_each(b_fence, &b_iter, b->fence) + ++num_fences; - num_fences = a_num_fences + b_num_fences; + if (num_fences > INT_MAX) + goto err_free_sync_file; fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL); if (!fences) - goto err; + goto err_free_sync_file; /* - * Assume sync_file a and b are both ordered and have no - * duplicates with the same context. + * We can't guarantee that fences in both a and b are ordered, but it is + * still quite likely. * - * If a sync_file can only be created with sync_file_merge - * and sync_file_create, this is a reasonable assumption. + * So attempt to order the fences as we pass over them and merge fences + * with the same context. */ - for (i_a = i_b = 0; i_a < a_num_fences && i_b < b_num_fences; ) { - struct dma_fence *pt_a = a_fences[i_a]; - struct dma_fence *pt_b = b_fences[i_b]; - if (pt_a->context < pt_b->context) { - add_fence(fences, &i, pt_a); + index = 0; + for (a_fence = dma_fence_unwrap_first(a->fence, &a_iter), + b_fence = dma_fence_unwrap_first(b->fence, &b_iter); + a_fence || b_fence; ) { + + if (!b_fence) { + add_fence(fences, &index, a_fence); + a_fence = dma_fence_unwrap_next(&a_iter); + + } else if (!a_fence) { + add_fence(fences, &index, b_fence); + b_fence = dma_fence_unwrap_next(&b_iter); + + } else if (a_fence->context < b_fence->context) { + add_fence(fences, &index, a_fence); + a_fence = dma_fence_unwrap_next(&a_iter); - i_a++; - } else if (pt_a->context > pt_b->context) { - add_fence(fences, &i, pt_b); + } else if (b_fence->context < a_fence->context) { + add_fence(fences, &index, b_fence); + b_fence = dma_fence_unwrap_next(&b_iter); + + } else if (__dma_fence_is_later(a_fence->seqno, b_fence->seqno, + a_fence->ops)) { + add_fence(fences, &index, a_fence); + a_fence = dma_fence_unwrap_next(&a_iter); + b_fence = dma_fence_unwrap_next(&b_iter); - i_b++; } else { - if (__dma_fence_is_later(pt_a->seqno, pt_b->seqno, - pt_a->ops)) - add_fence(fences, &i, pt_a); - else - add_fence(fences, &i, pt_b); - - i_a++; - i_b++; + add_fence(fences, &index, b_fence); + a_fence = dma_fence_unwrap_next(&a_iter); + b_fence = dma_fence_unwrap_next(&b_iter); } } - for (; i_a < a_num_fences; i_a++) - add_fence(fences, &i, a_fences[i_a]); - - for (; i_b < b_num_fences; i_b++) - add_fence(fences, &i, b_fences[i_b]); - - if (i == 0) - fences[i++] = dma_fence_get(a_fences[0]); + if (index == 0) + add_fence(fences, &index, dma_fence_get_stub()); - if (num_fences > i) { - nfences = krealloc_array(fences, i, sizeof(*fences), GFP_KERNEL); - if (!nfences) - goto err; + if (num_fences > index) { + struct dma_fence **tmp; - fences = nfences; + /* Keep going even when reducing the size failed */ + tmp = krealloc_array(fences, index, sizeof(*fences), + GFP_KERNEL); + if (tmp) + fences = tmp; } - if (sync_file_set_fence(sync_file, fences, i) < 0) - goto err; + if (sync_file_set_fence(sync_file, fences, index) < 0) + goto err_put_fences; strlcpy(sync_file->user_name, name, sizeof(sync_file->user_name)); return sync_file; -err: - while (i) - dma_fence_put(fences[--i]); +err_put_fences: + while (index) + dma_fence_put(fences[--index]); kfree(fences); + +err_free_sync_file: fput(sync_file->file); return NULL; - } static int sync_file_release(struct inode *inode, struct file *file) @@ -398,11 +396,13 @@ static int sync_fill_fence_info(struct dma_fence *fence, static long sync_file_ioctl_fence_info(struct sync_file *sync_file, unsigned long arg) { - struct sync_file_info info; struct sync_fence_info *fence_info = NULL; - struct dma_fence **fences; + struct dma_fence_unwrap iter; + struct sync_file_info info; + unsigned int num_fences; + struct dma_fence *fence; + int ret; __u32 size; - int num_fences, ret, i; if (copy_from_user(&info, (void __user *)arg, sizeof(info))) return -EFAULT; @@ -410,7 +410,9 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file, if (info.flags || info.pad) return -EINVAL; - fences = get_fences(sync_file, &num_fences); + num_fences = 0; + dma_fence_unwrap_for_each(fence, &iter, sync_file->fence) + ++num_fences; /* * Passing num_fences = 0 means that userspace doesn't want to @@ -433,8 +435,11 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file, if (!fence_info) return -ENOMEM; - for (i = 0; i < num_fences; i++) { - int status = sync_fill_fence_info(fences[i], &fence_info[i]); + num_fences = 0; + dma_fence_unwrap_for_each(fence, &iter, sync_file->fence) { + int status; + + status = sync_fill_fence_info(fence, &fence_info[num_fences++]); info.status = info.status <= 0 ? info.status : status; } -- cgit From 32f90e65251981f061eec883b0fe9e75d74e9665 Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Tue, 15 Mar 2022 14:46:28 +0800 Subject: drm/amdgpu: prevent memory wipe in suspend/shutdown stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On GPUs with RAS enabled, below call trace is observed when suspending or shutting down device. The cause is we have enabled memory wipe flag for BOs on such GPUs by default, and such BOs will go to memory wipe by amdgpu_fill_buffer, however, because ring is off already, it fails to clean up the memory and throw this error message. So add a suspend/shutdown check before wipping memory. [drm:amdgpu_fill_buffer [amdgpu]] *ERROR* Trying to clear memory with ring turned off. v2: fix coding style issue Fixes: fc6ea4bee13071 ("drm/amdgpu: Wipe all VRAM on free when RAS is enabled") Signed-off-by: Guchun Chen Reviewed-by: Christian König Acked-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 25731719c627..940752488330 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1284,6 +1284,7 @@ void amdgpu_bo_get_memory(struct amdgpu_bo *bo, uint64_t *vram_mem, */ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo) { + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev); struct dma_fence *fence = NULL; struct amdgpu_bo *abo; int r; @@ -1303,7 +1304,8 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo) amdgpu_amdkfd_remove_fence_on_pt_pd_bos(abo); if (bo->resource->mem_type != TTM_PL_VRAM || - !(abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE)) + !(abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE) || + adev->in_suspend || adev->shutdown) return; if (WARN_ON_ONCE(!dma_resv_trylock(bo->base.resv))) -- cgit From 2d505453f38e18d42ba7d5428aaa17aaa7752c65 Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Tue, 15 Mar 2022 14:59:28 +0800 Subject: drm/amdgpu: conduct a proper cleanup of PDB bo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use amdgpu_bo_free_kernel instead of amdgpu_bo_unref to perform a proper cleanup of PDB bo. v2: update subject to be more accurate Signed-off-by: Guchun Chen Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 431742eb7811..08ceabd6c853 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1721,7 +1721,7 @@ static int gmc_v9_0_sw_fini(void *handle) amdgpu_gem_force_release(adev); amdgpu_vm_manager_fini(adev); amdgpu_gart_table_vram_free(adev); - amdgpu_bo_unref(&adev->gmc.pdb0_bo); + amdgpu_bo_free_kernel(&adev->gmc.pdb0_bo, NULL, &adev->gmc.ptr_pdb0); amdgpu_bo_fini(adev); return 0; -- cgit From 60d61f4ed6ead43ad2de31ebb8d1d27c57290529 Mon Sep 17 00:00:00 2001 From: Yifan Zhang Date: Tue, 15 Mar 2022 18:19:06 +0800 Subject: drm/amdgpu/pm: fix the Stable pstate Test in amdgpu_test If GFX DPM is disbaled, Stable pstate Test in amdgpu_test fails. Check GFX DPM statue before change clock level Log: [ 46.595274] [drm] Initialized amdgpu 3.46.0 20150101 for 0000:02:00.0 on minor 0 [ 46.599929] fbcon: amdgpudrmfb (fb0) is primary device [ 46.785753] Console: switching to colour frame buffer device 240x67 [ 46.811765] amdgpu 0000:02:00.0: [drm] fb0: amdgpudrmfb frame buffer device [ 131.398407] amdgpu 0000:02:00.0: amdgpu: Failed to set performance level! Signed-off-by: Yifan Zhang Acked-by: Huang Rui Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c index 7bfac029e513..b81711c4ff33 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c @@ -991,7 +991,7 @@ static int smu_v13_0_5_set_performance_level(struct smu_context *smu, return -EINVAL; } - if (sclk_min && sclk_max) { + if (sclk_min && sclk_max && smu_v13_0_5_clk_dpm_is_enabled(smu, SMU_SCLK)) { ret = smu_v13_0_5_set_soft_freq_limited_range(smu, SMU_SCLK, sclk_min, -- cgit From 1647b54ed55d4d48c7199d439f8834626576cbe9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 16 Mar 2022 11:41:48 +0300 Subject: drm/amdgpu: fix off by one in amdgpu_gfx_kiq_acquire() This post-op should be a pre-op so that we do not pass -1 as the bit number to test_bit(). The current code will loop downwards from 63 to -1. After changing to a pre-op, it loops from 63 to 0. Fixes: 71c37505e7ea ("drm/amdgpu/gfx: move more common KIQ code to amdgpu_gfx.c") Signed-off-by: Dan Carpenter Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 8fe939976224..28a736c507bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -266,7 +266,7 @@ static int amdgpu_gfx_kiq_acquire(struct amdgpu_device *adev, * adev->gfx.mec.num_pipe_per_mec * adev->gfx.mec.num_queue_per_pipe; - while (queue_bit-- >= 0) { + while (--queue_bit >= 0) { if (test_bit(queue_bit, adev->gfx.mec.queue_bitmap)) continue; -- cgit From b818a5d374542ccec73dcfe578a081574029820e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 9 Mar 2022 18:02:11 -0500 Subject: drm/amdgpu/gmc: use PCI BARs for APUs in passthrough MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the GPU is passed through to a guest VM, use the PCI BAR for CPU FB access rather than the physical address of carve out. The physical address is not valid in a guest. v2: Fix HDP handing as suggested by Michel Reviewed-by: Christian König Reviewed-by: Michel Dänzer Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 ++-- drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c | 5 +++-- drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 3987ecb24ef4..49f734137f15 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -5733,7 +5733,7 @@ void amdgpu_device_flush_hdp(struct amdgpu_device *adev, struct amdgpu_ring *ring) { #ifdef CONFIG_X86_64 - if (adev->flags & AMD_IS_APU) + if ((adev->flags & AMD_IS_APU) && !amdgpu_passthrough(adev)) return; #endif if (adev->gmc.xgmi.connected_to_cpu) @@ -5749,7 +5749,7 @@ void amdgpu_device_invalidate_hdp(struct amdgpu_device *adev, struct amdgpu_ring *ring) { #ifdef CONFIG_X86_64 - if (adev->flags & AMD_IS_APU) + if ((adev->flags & AMD_IS_APU) && !amdgpu_passthrough(adev)) return; #endif if (adev->gmc.xgmi.connected_to_cpu) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index 3c1d440824a7..5228421b0f72 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -814,7 +814,7 @@ static int gmc_v10_0_mc_init(struct amdgpu_device *adev) adev->gmc.aper_size = pci_resource_len(adev->pdev, 0); #ifdef CONFIG_X86_64 - if (adev->flags & AMD_IS_APU) { + if ((adev->flags & AMD_IS_APU) && !amdgpu_passthrough(adev)) { adev->gmc.aper_base = adev->gfxhub.funcs->get_mc_fb_offset(adev); adev->gmc.aper_size = adev->gmc.real_vram_size; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 344d819b4c1b..979da6f510e8 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -381,8 +381,9 @@ static int gmc_v7_0_mc_init(struct amdgpu_device *adev) adev->gmc.aper_size = pci_resource_len(adev->pdev, 0); #ifdef CONFIG_X86_64 - if (adev->flags & AMD_IS_APU && - adev->gmc.real_vram_size > adev->gmc.aper_size) { + if ((adev->flags & AMD_IS_APU) && + adev->gmc.real_vram_size > adev->gmc.aper_size && + !amdgpu_passthrough(adev)) { adev->gmc.aper_base = ((u64)RREG32(mmMC_VM_FB_OFFSET)) << 22; adev->gmc.aper_size = adev->gmc.real_vram_size; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index ca9841d5669f..1932a3e4af7e 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -581,7 +581,7 @@ static int gmc_v8_0_mc_init(struct amdgpu_device *adev) adev->gmc.aper_size = pci_resource_len(adev->pdev, 0); #ifdef CONFIG_X86_64 - if (adev->flags & AMD_IS_APU) { + if ((adev->flags & AMD_IS_APU) && !amdgpu_passthrough(adev)) { adev->gmc.aper_base = ((u64)RREG32(mmMC_VM_FB_OFFSET)) << 22; adev->gmc.aper_size = adev->gmc.real_vram_size; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 08ceabd6c853..6009fbfdcc19 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1456,7 +1456,7 @@ static int gmc_v9_0_mc_init(struct amdgpu_device *adev) */ /* check whether both host-gpu and gpu-gpu xgmi links exist */ - if ((adev->flags & AMD_IS_APU) || + if (((adev->flags & AMD_IS_APU) && !amdgpu_passthrough(adev)) || (adev->gmc.xgmi.supported && adev->gmc.xgmi.connected_to_cpu)) { adev->gmc.aper_base = -- cgit From 0d8e4eb337644cab528ff3844675d58496ec22db Mon Sep 17 00:00:00 2001 From: Lang Yu Date: Tue, 8 Mar 2022 11:26:41 +0800 Subject: drm/amdgpu: add workarounds for VCN TMZ issue on CHIP_RAVEN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is a hardware issue that VCN can't handle a GTT backing stored TMZ buffer on CHIP_RAVEN series ASIC. Move such a TMZ buffer to VRAM domain before command submission as a workaround. v2: - Use patch_cs_in_place callback. v3: - Bail out early if unsecure IBs. Suggested-by: Christian König Signed-off-by: Lang Yu Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c index dff54190b96c..f0fbcda76f5e 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c @@ -24,6 +24,7 @@ #include #include "amdgpu.h" +#include "amdgpu_cs.h" #include "amdgpu_vcn.h" #include "amdgpu_pm.h" #include "soc15.h" @@ -1900,6 +1901,75 @@ static const struct amd_ip_funcs vcn_v1_0_ip_funcs = { .set_powergating_state = vcn_v1_0_set_powergating_state, }; +/* + * It is a hardware issue that VCN can't handle a GTT TMZ buffer on + * CHIP_RAVEN series ASIC. Move such a GTT TMZ buffer to VRAM domain + * before command submission as a workaround. + */ +static int vcn_v1_0_validate_bo(struct amdgpu_cs_parser *parser, + struct amdgpu_job *job, + uint64_t addr) +{ + struct ttm_operation_ctx ctx = { false, false }; + struct amdgpu_fpriv *fpriv = parser->filp->driver_priv; + struct amdgpu_vm *vm = &fpriv->vm; + struct amdgpu_bo_va_mapping *mapping; + struct amdgpu_bo *bo; + int r; + + addr &= AMDGPU_GMC_HOLE_MASK; + if (addr & 0x7) { + DRM_ERROR("VCN messages must be 8 byte aligned!\n"); + return -EINVAL; + } + + mapping = amdgpu_vm_bo_lookup_mapping(vm, addr/AMDGPU_GPU_PAGE_SIZE); + if (!mapping || !mapping->bo_va || !mapping->bo_va->base.bo) + return -EINVAL; + + bo = mapping->bo_va->base.bo; + if (!(bo->flags & AMDGPU_GEM_CREATE_ENCRYPTED)) + return 0; + + amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM); + r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); + if (r) { + DRM_ERROR("Failed to validate the VCN message BO (%d)!\n", r); + return r; + } + + return r; +} + +static int vcn_v1_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, + struct amdgpu_job *job, + struct amdgpu_ib *ib) +{ + uint32_t msg_lo = 0, msg_hi = 0; + int i, r; + + if (!(ib->flags & AMDGPU_IB_FLAGS_SECURE)) + return 0; + + for (i = 0; i < ib->length_dw; i += 2) { + uint32_t reg = amdgpu_ib_get_value(ib, i); + uint32_t val = amdgpu_ib_get_value(ib, i + 1); + + if (reg == PACKET0(p->adev->vcn.internal.data0, 0)) { + msg_lo = val; + } else if (reg == PACKET0(p->adev->vcn.internal.data1, 0)) { + msg_hi = val; + } else if (reg == PACKET0(p->adev->vcn.internal.cmd, 0)) { + r = vcn_v1_0_validate_bo(p, job, + ((u64)msg_hi) << 32 | msg_lo); + if (r) + return r; + } + } + + return 0; +} + static const struct amdgpu_ring_funcs vcn_v1_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_DEC, .align_mask = 0xf, @@ -1910,6 +1980,7 @@ static const struct amdgpu_ring_funcs vcn_v1_0_dec_ring_vm_funcs = { .get_rptr = vcn_v1_0_dec_ring_get_rptr, .get_wptr = vcn_v1_0_dec_ring_get_wptr, .set_wptr = vcn_v1_0_dec_ring_set_wptr, + .patch_cs_in_place = vcn_v1_0_ring_patch_cs_in_place, .emit_frame_size = 6 + 6 + /* hdp invalidate / flush */ SOC15_FLUSH_GPU_TLB_NUM_WREG * 6 + -- cgit From 93dde6ccd66d29502506e95f568fd0f49d575d27 Mon Sep 17 00:00:00 2001 From: "Stanley.Yang" Date: Mon, 21 Mar 2022 12:50:36 +0800 Subject: drm/amdgpu/pm: add asic smu support check It must check asic whether support smu before call smu powerplay function, otherwise it may cause null point on no support smu asic. Signed-off-by: Stanley.Yang Reviewed-by: Evan Quan Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 89fbee568be4..c73fb73e9628 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -500,6 +500,9 @@ int amdgpu_dpm_send_hbm_bad_pages_num(struct amdgpu_device *adev, uint32_t size) struct smu_context *smu = adev->powerplay.pp_handle; int ret = 0; + if (!is_support_sw_smu(adev)) + return -EOPNOTSUPP; + mutex_lock(&adev->pm.mutex); ret = smu_send_hbm_bad_pages_num(smu, size); mutex_unlock(&adev->pm.mutex); @@ -512,6 +515,9 @@ int amdgpu_dpm_send_hbm_bad_channel_flag(struct amdgpu_device *adev, uint32_t si struct smu_context *smu = adev->powerplay.pp_handle; int ret = 0; + if (!is_support_sw_smu(adev)) + return -EOPNOTSUPP; + mutex_lock(&adev->pm.mutex); ret = smu_send_hbm_bad_channel_flag(smu, size); mutex_unlock(&adev->pm.mutex); -- cgit From 3107e1a7ae088ee94323fe9ab05dbefd65b3077f Mon Sep 17 00:00:00 2001 From: Nicholas Kazlauskas Date: Fri, 18 Mar 2022 11:10:34 -0400 Subject: drm/amd/display: Fix p-state allow debug index on dcn31 [Why] It changed since dcn30 but the hubbub31 constructor hasn't been modified to reflect this. [How] Update the value in the constructor to 0x6 so we're checking the right bits for p-state allow. It worked before by accident, but can falsely assert 0 depending on HW state transitions. The most frequent of which appears to be when all pipes turn off during IGT tests. Cc: Harry Wentland Fixes: e7031d8258f1b4 ("drm/amd/display: Add pstate verification and recovery for DCN31") Signed-off-by: Nicholas Kazlauskas Reviewed-by: Eric Yang Acked-by: Harry Wentland Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.c index 3e6d6ebd199e..51c5f3685470 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.c @@ -1042,5 +1042,7 @@ void hubbub31_construct(struct dcn20_hubbub *hubbub31, hubbub31->detile_buf_size = det_size_kb * 1024; hubbub31->pixel_chunk_size = pixel_chunk_size_kb * 1024; hubbub31->crb_size_segs = config_return_buffer_size_kb / DCN31_CRB_SEGMENT_SIZE_KB; + + hubbub31->debug_test_index_pstate = 0x6; } -- cgit From 5e8a71cf13bc9184fee915b2220be71b4c6cac74 Mon Sep 17 00:00:00 2001 From: Charlene Liu Date: Mon, 7 Mar 2022 18:31:29 -0500 Subject: drm/amd/display: fix audio format not updated after edid updated [why] for the case edid change only changed audio format. driver still need to update stream. Reviewed-by: Alvin Lee Reviewed-by: Aric Cyr Acked-by: Alex Hung Signed-off-by: Charlene Liu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7af153434e9e..d251c3f3a714 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1685,8 +1685,8 @@ bool dc_is_stream_unchanged( if (old_stream->ignore_msa_timing_param != stream->ignore_msa_timing_param) return false; - // Only Have Audio left to check whether it is same or not. This is a corner case for Tiled sinks - if (old_stream->audio_info.mode_count != stream->audio_info.mode_count) + /*compare audio info*/ + if (memcmp(&old_stream->audio_info, &stream->audio_info, sizeof(stream->audio_info)) != 0) return false; return true; -- cgit From 6bf528ec91fb96e186461215c8f76265c5a35250 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Mon, 28 Feb 2022 19:01:59 -0500 Subject: drm/amd/display: Reduce stack size Linux kernel enabled more compilation restrictions related to the stack size, which caused compilation failures in our code. This commit reduces the allocation size by allocating the required memory dynamically. Reviewed-by: Harry Wentland Reviewed-by: Aric Cyr Acked-by: Alex Hung Signed-off-by: Rodrigo Siqueira Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index c3e141c19a77..ad757b59e00e 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -2056,7 +2056,7 @@ static int dcn10_align_pixel_clocks(struct dc *dc, int group_size, { struct dc_context *dc_ctx = dc->ctx; int i, master = -1, embedded = -1; - struct dc_crtc_timing hw_crtc_timing[MAX_PIPES] = {0}; + struct dc_crtc_timing *hw_crtc_timing; uint64_t phase[MAX_PIPES]; uint64_t modulo[MAX_PIPES]; unsigned int pclk; @@ -2067,6 +2067,10 @@ static int dcn10_align_pixel_clocks(struct dc *dc, int group_size, uint32_t dp_ref_clk_100hz = dc->res_pool->dp_clock_source->ctx->dc->clk_mgr->dprefclk_khz*10; + hw_crtc_timing = kcalloc(MAX_PIPES, sizeof(*hw_crtc_timing), GFP_KERNEL); + if (!hw_crtc_timing) + return master; + if (dc->config.vblank_alignment_dto_params && dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk) { embedded_h_total = @@ -2130,6 +2134,8 @@ static int dcn10_align_pixel_clocks(struct dc *dc, int group_size, } } + + kfree(hw_crtc_timing); return master; } -- cgit From 7d56a154e22ffb3613fdebf83ec34d5225a22993 Mon Sep 17 00:00:00 2001 From: Chiawen Huang Date: Thu, 10 Mar 2022 00:07:59 +0800 Subject: drm/amd/display: FEC check in timing validation [Why] disable/enable leads FEC mismatch between hw/sw FEC state. [How] check FEC status to fastboot on/off. Reviewed-by: Anthony Koo Acked-by: Alex Hung Signed-off-by: Chiawen Huang Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index f6e19efea756..75f9c97bebb0 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1496,6 +1496,10 @@ bool dc_validate_boot_timing(const struct dc *dc, if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) return false; + /* Check for FEC status*/ + if (link->link_enc->funcs->fec_is_active(link->link_enc)) + return false; + enc_inst = link->link_enc->funcs->get_dig_frontend(link->link_enc); if (enc_inst == ENGINE_ID_UNKNOWN) -- cgit From bd219322dbb41cc7c753da3a6936ce09d502f113 Mon Sep 17 00:00:00 2001 From: Oliver Logush Date: Tue, 8 Mar 2022 10:34:04 -0500 Subject: drm/amd/display: Add fSMC_MSG_SetDtbClk support [why] Needed to support dcn315 Reviewed-by: Charlene Liu Acked-by: Alex Hung Signed-off-by: Oliver Logush Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c | 19 +++++++++++++++---- .../drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.h | 4 +++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c index 880ffea2afc6..2600313fea57 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c @@ -80,8 +80,8 @@ static const struct IP_BASE NBIO_BASE = { { { { 0x00000000, 0x00000014, 0x00000D #define VBIOSSMC_MSG_SetDppclkFreq 0x06 ///< Set DPP clock frequency in MHZ #define VBIOSSMC_MSG_SetHardMinDcfclkByFreq 0x07 ///< Set DCF clock frequency hard min in MHZ #define VBIOSSMC_MSG_SetMinDeepSleepDcfclk 0x08 ///< Set DCF clock minimum frequency in deep sleep in MHZ -#define VBIOSSMC_MSG_SetPhyclkVoltageByFreq 0x09 ///< Set display phy clock frequency in MHZ in case VMIN does not support phy frequency -#define VBIOSSMC_MSG_GetFclkFrequency 0x0A ///< Get FCLK frequency, return frequemcy in MHZ +#define VBIOSSMC_MSG_GetDtbclkFreq 0x09 ///< Get display dtb clock frequency in MHZ in case VMIN does not support phy frequency +#define VBIOSSMC_MSG_SetDtbClk 0x0A ///< Set dtb clock frequency, return frequemcy in MHZ #define VBIOSSMC_MSG_SetDisplayCount 0x0B ///< Inform PMFW of number of display connected #define VBIOSSMC_MSG_EnableTmdp48MHzRefclkPwrDown 0x0C ///< To ask PMFW turn off TMDP 48MHz refclk during display off to save power #define VBIOSSMC_MSG_UpdatePmeRestore 0x0D ///< To ask PMFW to write into Azalia for PME wake up event @@ -324,15 +324,26 @@ int dcn315_smu_get_dpref_clk(struct clk_mgr_internal *clk_mgr) return (dprefclk_get_mhz * 1000); } -int dcn315_smu_get_smu_fclk(struct clk_mgr_internal *clk_mgr) +int dcn315_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr) { int fclk_get_mhz = -1; if (clk_mgr->smu_present) { fclk_get_mhz = dcn315_smu_send_msg_with_param( clk_mgr, - VBIOSSMC_MSG_GetFclkFrequency, + VBIOSSMC_MSG_GetDtbclkFreq, 0); } return (fclk_get_mhz * 1000); } + +void dcn315_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) +{ + if (!clk_mgr->smu_present) + return; + + dcn315_smu_send_msg_with_param( + clk_mgr, + VBIOSSMC_MSG_SetDtbClk, + enable); +} diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.h index 66fa42f8dd18..5aa3275ac7d8 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.h @@ -37,6 +37,7 @@ #define NUM_SOC_VOLTAGE_LEVELS 4 #define NUM_DF_PSTATE_LEVELS 4 + typedef struct { uint16_t MinClock; // This is either DCFCLK or SOCCLK (in MHz) uint16_t MaxClock; // This is either DCFCLK or SOCCLK (in MHz) @@ -124,5 +125,6 @@ void dcn315_smu_transfer_wm_table_dram_2_smu(struct clk_mgr_internal *clk_mgr); void dcn315_smu_request_voltage_via_phyclk(struct clk_mgr_internal *clk_mgr, int requested_phyclk_khz); void dcn315_smu_enable_pme_wa(struct clk_mgr_internal *clk_mgr); int dcn315_smu_get_dpref_clk(struct clk_mgr_internal *clk_mgr); -int dcn315_smu_get_smu_fclk(struct clk_mgr_internal *clk_mgr); +int dcn315_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr); +void dcn315_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable); #endif /* DAL_DC_315_SMU_H_ */ -- cgit From c9fbf6435162ed5fb7201d1d4adf6585c6a8c327 Mon Sep 17 00:00:00 2001 From: "Leo (Hanghong) Ma" Date: Fri, 11 Mar 2022 11:35:29 -0500 Subject: drm/amd/display: Update VTEM Infopacket definition [Why & How] The latest HDMI SPEC has updated the VTEM packet structure, so change the VTEM Infopacket defined in the driver side to align with the SPEC. Reviewed-by: Chris Park Acked-by: Alex Hung Signed-off-by: Leo (Hanghong) Ma Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index b691aa45e84f..79bc207415bc 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -100,7 +100,8 @@ enum vsc_packet_revision { //PB7 = MD0 #define MASK_VTEM_MD0__VRR_EN 0x01 #define MASK_VTEM_MD0__M_CONST 0x02 -#define MASK_VTEM_MD0__RESERVED2 0x0C +#define MASK_VTEM_MD0__QMS_EN 0x04 +#define MASK_VTEM_MD0__RESERVED2 0x08 #define MASK_VTEM_MD0__FVA_FACTOR_M1 0xF0 //MD1 @@ -109,7 +110,7 @@ enum vsc_packet_revision { //MD2 #define MASK_VTEM_MD2__BASE_REFRESH_RATE_98 0x03 #define MASK_VTEM_MD2__RB 0x04 -#define MASK_VTEM_MD2__RESERVED3 0xF8 +#define MASK_VTEM_MD2__NEXT_TFR 0xF8 //MD3 #define MASK_VTEM_MD3__BASE_REFRESH_RATE_07 0xFF -- cgit From 02fc996d5098f4c3f65bdf6cdb6b28e3f29ba789 Mon Sep 17 00:00:00 2001 From: Emily Deng Date: Mon, 21 Mar 2022 16:25:24 +0800 Subject: drm/amdgpu/vcn: Fix the register setting for vcn1 Correct the code error for setting register UVD_GFX10_ADDR_CONFIG. Need to use inst_idx, or it only will set VCN0. Signed-off-by: Emily Deng Reviewed-by: James Zhu Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index c87263ed20ec..b16c56aa2d22 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -575,8 +575,8 @@ static void vcn_v3_0_mc_resume_dpg_mode(struct amdgpu_device *adev, int inst_idx AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_fw_shared)), 0, indirect); /* VCN global tiling registers */ - WREG32_SOC15_DPG_MODE(0, SOC15_DPG_MODE_OFFSET( - UVD, 0, mmUVD_GFX10_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect); + WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET( + UVD, inst_idx, mmUVD_GFX10_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect); } static void vcn_v3_0_disable_static_power_gating(struct amdgpu_device *adev, int inst) -- cgit From e5fc78252ccd8dfc260f87d83905e9dffff6d975 Mon Sep 17 00:00:00 2001 From: Gabe Teeger Date: Mon, 14 Mar 2022 12:07:14 -0400 Subject: drm/amd/display: Add support for zstate during extended vblank [why] When we enter FREESYNC_STATE_VIDEO, we want to use the extra vblank portion to enter zstate if possible. [how] When we enter freesync, a full update is triggered and the new vtotal with extra lines is passed to dml in a stream update. The time gained from extra vblank lines is calculated in microseconds. We allow zstate entry if the time gained is greater than 5 ms, which is the current policy. Furthermore, an optimized value for min_dst_y_next_start is calculated and written to its register. When exiting freesync, another full update is triggered and default values are restored. Reviewed-by: Nicholas Kazlauskas Acked-by: Alex Hung Signed-off-by: Gabe Teeger Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc.c | 19 +++++++++++++++++++ drivers/gpu/drm/amd/display/dc/dc.h | 6 +++++- drivers/gpu/drm/amd/display/dc/dc_stream.h | 2 ++ drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c | 12 ++++++++++++ drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.c | 8 ++++++++ drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c | 1 + drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c | 18 +++++++++++++++--- .../amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c | 13 +++++++++++++ .../gpu/drm/amd/display/dc/dml/display_mode_structs.h | 2 ++ drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h | 3 +++ 10 files changed, 80 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 75f9c97bebb0..f2ad8f58e69c 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2393,6 +2393,8 @@ static enum surface_update_type check_update_surfaces_for_stream( if (stream_update->mst_bw_update) su_flags->bits.mst_bw = 1; + if (stream_update->crtc_timing_adjust && dc_extended_blank_supported(dc)) + su_flags->bits.crtc_timing_adjust = 1; if (su_flags->raw != 0) overall_type = UPDATE_TYPE_FULL; @@ -2654,6 +2656,9 @@ static void copy_stream_update_to_stream(struct dc *dc, if (update->vrr_infopacket) stream->vrr_infopacket = *update->vrr_infopacket; + if (update->crtc_timing_adjust) + stream->adjust = *update->crtc_timing_adjust; + if (update->dpms_off) stream->dpms_off = *update->dpms_off; @@ -4055,3 +4060,17 @@ void dc_notify_vsync_int_state(struct dc *dc, struct dc_stream_state *stream, bo if (pipe->stream_res.abm && pipe->stream_res.abm->funcs->set_abm_pause) pipe->stream_res.abm->funcs->set_abm_pause(pipe->stream_res.abm, !enable, i, pipe->stream_res.tg->inst); } +/* + * dc_extended_blank_supported: Decide whether extended blank is supported + * + * Extended blank is a freesync optimization feature to be enabled in the future. + * During the extra vblank period gained from freesync, we have the ability to enter z9/z10. + * + * @param [in] dc: Current DC state + * @return: Indicate whether extended blank is supported (true or false) + */ +bool dc_extended_blank_supported(struct dc *dc) +{ + return dc->debug.extended_blank_optimization && !dc->debug.disable_z10 + && dc->caps.zstate_support && dc->caps.is_apu; +} diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 4ffab7bb1098..9f4d926d54e7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -188,6 +188,7 @@ struct dc_caps { bool psp_setup_panel_mode; bool extended_aux_timeout_support; bool dmcub_support; + bool zstate_support; uint32_t num_of_internal_disp; enum dp_protocol_version max_dp_protocol_version; unsigned int mall_size_per_mem_channel; @@ -703,13 +704,14 @@ struct dc_debug_options { bool enable_driver_sequence_debug; enum det_size crb_alloc_policy; int crb_alloc_policy_min_disp_count; -#if defined(CONFIG_DRM_AMD_DC_DCN) bool disable_z10; +#if defined(CONFIG_DRM_AMD_DC_DCN) bool enable_z9_disable_interface; bool enable_sw_cntl_psr; union dpia_debug_options dpia_debug; #endif bool apply_vendor_specific_lttpr_wa; + bool extended_blank_optimization; bool ignore_dpref_ss; uint8_t psr_power_use_phy_fsm; }; @@ -1369,6 +1371,8 @@ struct dc_sink_init_data { bool converter_disable_audio; }; +bool dc_extended_blank_supported(struct dc *dc); + struct dc_sink *dc_sink_create(const struct dc_sink_init_data *init_params); /* Newer interfaces */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index 99a750f561f8..c4168c11257c 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -131,6 +131,7 @@ union stream_update_flags { uint32_t wb_update:1; uint32_t dsc_changed : 1; uint32_t mst_bw : 1; + uint32_t crtc_timing_adjust : 1; } bits; uint32_t raw; @@ -289,6 +290,7 @@ struct dc_stream_update { struct dc_3dlut *lut3d_func; struct test_pattern *pending_test_pattern; + struct dc_crtc_timing_adjust *crtc_timing_adjust; }; bool dc_is_stream_unchanged( diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c index ab910deed481..4290eaf11a04 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c @@ -1857,6 +1857,7 @@ void dcn20_optimize_bandwidth( struct dc_state *context) { struct hubbub *hubbub = dc->res_pool->hubbub; + int i; /* program dchubbub watermarks */ hubbub->funcs->program_watermarks(hubbub, @@ -1873,6 +1874,17 @@ void dcn20_optimize_bandwidth( dc->clk_mgr, context, true); + if (dc_extended_blank_supported(dc) && context->bw_ctx.bw.dcn.clk.zstate_support == DCN_ZSTATE_SUPPORT_ALLOW) { + for (i = 0; i < dc->res_pool->pipe_count; ++i) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream && pipe_ctx->plane_res.hubp->funcs->program_extended_blank + && pipe_ctx->stream->adjust.v_total_min == pipe_ctx->stream->adjust.v_total_max + && pipe_ctx->stream->adjust.v_total_max > pipe_ctx->stream->timing.v_total) + pipe_ctx->plane_res.hubp->funcs->program_extended_blank(pipe_ctx->plane_res.hubp, + pipe_ctx->dlg_regs.optimized_min_dst_y_next_start); + } + } /* increase compbuf size */ if (hubbub->funcs->program_compbuf_size) hubbub->funcs->program_compbuf_size(hubbub, context->bw_ctx.bw.dcn.compbuf_size_kb, true); diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.c index 53b792b997b7..8ae6117953ca 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.c @@ -54,6 +54,13 @@ void hubp31_soft_reset(struct hubp *hubp, bool reset) REG_UPDATE(DCHUBP_CNTL, HUBP_SOFT_RESET, reset); } +void hubp31_program_extended_blank(struct hubp *hubp, unsigned int min_dst_y_next_start_optimized) +{ + struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + + REG_SET(BLANK_OFFSET_1, 0, MIN_DST_Y_NEXT_START, min_dst_y_next_start_optimized); +} + static struct hubp_funcs dcn31_hubp_funcs = { .hubp_enable_tripleBuffer = hubp2_enable_triplebuffer, .hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled, @@ -80,6 +87,7 @@ static struct hubp_funcs dcn31_hubp_funcs = { .set_unbounded_requesting = hubp31_set_unbounded_requesting, .hubp_soft_reset = hubp31_soft_reset, .hubp_in_blank = hubp1_in_blank, + .program_extended_blank = hubp31_program_extended_blank, }; bool hubp31_construct( diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c index 89b7b6b7254a..338235bcef4a 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c @@ -2232,6 +2232,7 @@ static bool dcn31_resource_construct( dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; dc->caps.is_apu = true; + dc->caps.zstate_support = true; /* Color pipeline capabilities */ dc->caps.color.dpp.dcn_arch = 1; 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 2f6122153bdb..f93af45aeab4 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 @@ -722,8 +722,10 @@ static enum dcn_zstate_support_state decide_zstate_support(struct dc *dc, struc { int plane_count; int i; + unsigned int optimized_min_dst_y_next_start_us; plane_count = 0; + optimized_min_dst_y_next_start_us = 0; for (i = 0; i < dc->res_pool->pipe_count; i++) { if (context->res_ctx.pipe_ctx[i].plane_state) plane_count++; @@ -744,11 +746,22 @@ static enum dcn_zstate_support_state decide_zstate_support(struct dc *dc, struc struct dc_link *link = context->streams[0]->sink->link; struct dc_stream_status *stream_status = &context->stream_status[0]; + if (dc_extended_blank_supported(dc)) { + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (context->res_ctx.pipe_ctx[i].stream == context->streams[0] + && context->res_ctx.pipe_ctx[i].stream->adjust.v_total_min == context->res_ctx.pipe_ctx[i].stream->adjust.v_total_max + && context->res_ctx.pipe_ctx[i].stream->adjust.v_total_min > context->res_ctx.pipe_ctx[i].stream->timing.v_total) { + optimized_min_dst_y_next_start_us = + context->res_ctx.pipe_ctx[i].dlg_regs.optimized_min_dst_y_next_start_us; + break; + } + } + } /* zstate only supported on PWRSEQ0 and when there's <2 planes*/ if (link->link_index != 0 || stream_status->plane_count > 1) return DCN_ZSTATE_SUPPORT_DISALLOW; - if (context->bw_ctx.dml.vba.StutterPeriod > 5000.0) + if (context->bw_ctx.dml.vba.StutterPeriod > 5000.0 || optimized_min_dst_y_next_start_us > 5000) return DCN_ZSTATE_SUPPORT_ALLOW; else if (link->psr_settings.psr_version == DC_PSR_VERSION_1 && !dc->debug.disable_psr) return DCN_ZSTATE_SUPPORT_ALLOW_Z10_ONLY; @@ -786,8 +799,6 @@ void dcn20_calculate_dlg_params( != dm_dram_clock_change_unsupported; context->bw_ctx.bw.dcn.clk.dppclk_khz = 0; - context->bw_ctx.bw.dcn.clk.zstate_support = decide_zstate_support(dc, context); - context->bw_ctx.bw.dcn.clk.dtbclk_en = is_dtbclk_required(dc, context); if (context->bw_ctx.bw.dcn.clk.dispclk_khz < dc->debug.min_disp_clk_khz) @@ -843,6 +854,7 @@ void dcn20_calculate_dlg_params( &pipes[pipe_idx].pipe); pipe_idx++; } + context->bw_ctx.bw.dcn.clk.zstate_support = decide_zstate_support(dc, context); } static void swizzle_to_dml_params( 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 e0fecf127bd5..53d760e169e6 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 @@ -1055,6 +1055,7 @@ static void dml_rq_dlg_get_dlg_params( float vba__refcyc_per_req_delivery_pre_l = get_refcyc_per_req_delivery_pre_l_in_us(mode_lib, e2e_pipe_param, num_pipes, pipe_idx) * refclk_freq_in_mhz; // From VBA float vba__refcyc_per_req_delivery_l = get_refcyc_per_req_delivery_l_in_us(mode_lib, e2e_pipe_param, num_pipes, pipe_idx) * refclk_freq_in_mhz; // From VBA + int blank_lines; memset(disp_dlg_regs, 0, sizeof(*disp_dlg_regs)); memset(disp_ttu_regs, 0, sizeof(*disp_ttu_regs)); @@ -1080,6 +1081,18 @@ static void dml_rq_dlg_get_dlg_params( dlg_vblank_start = interlaced ? (vblank_start / 2) : vblank_start; disp_dlg_regs->min_dst_y_next_start = (unsigned int) (((double) dlg_vblank_start) * dml_pow(2, 2)); + blank_lines = (dst->vblank_end + dst->vtotal_min - dst->vblank_start - dst->vstartup_start - 1); + if (blank_lines < 0) + blank_lines = 0; + if (blank_lines != 0) { + disp_dlg_regs->optimized_min_dst_y_next_start_us = + ((unsigned int) blank_lines * dst->hactive) / (unsigned int) dst->pixel_rate_mhz; + disp_dlg_regs->optimized_min_dst_y_next_start = + (unsigned int)(((double) (dlg_vblank_start + blank_lines)) * dml_pow(2, 2)); + } else { + // use unoptimized value + disp_dlg_regs->optimized_min_dst_y_next_start = disp_dlg_regs->min_dst_y_next_start; + } ASSERT(disp_dlg_regs->min_dst_y_next_start < (unsigned int)dml_pow(2, 18)); dml_print("DML_DLG: %s: min_ttu_vblank (us) = %3.2f\n", __func__, min_ttu_vblank); diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h index 59f0a61c33cf..2df660cd8801 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h @@ -446,6 +446,8 @@ struct _vcs_dpi_display_dlg_regs_st { unsigned int refcyc_h_blank_end; unsigned int dlg_vblank_end; unsigned int min_dst_y_next_start; + unsigned int optimized_min_dst_y_next_start; + unsigned int optimized_min_dst_y_next_start_us; unsigned int refcyc_per_htotal; unsigned int refcyc_x_after_scaler; unsigned int dst_y_after_scaler; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index e45b7993c5c5..ad69d78c4ac3 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -195,6 +195,9 @@ struct hubp_funcs { void (*hubp_set_flip_int)(struct hubp *hubp); + void (*program_extended_blank)(struct hubp *hubp, + unsigned int min_dst_y_next_start_optimized); + void (*hubp_wait_pipe_read_start)(struct hubp *hubp); }; -- cgit From a572f7055067d95455850fd242d8b54ff5786cac Mon Sep 17 00:00:00 2001 From: Charlene Liu Date: Tue, 22 Feb 2022 15:53:32 -0500 Subject: drm/amd/display: remove destructive verify link for TMDS [why and how] TMDS does not need destructive verify link Reviewed-by: Aric Cyr Acked-by: Alan Liu Acked-by: Alex Hung Signed-off-by: Charlene Liu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_link.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index cb87dd643180..bbaa5abdf888 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -983,8 +983,7 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, destrictive = false; } } - } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) - destrictive = true; + } return destrictive; } -- cgit From b7dfbd2e601f3fee545bc158feceba4f340fe7cf Mon Sep 17 00:00:00 2001 From: Tushar Patel Date: Thu, 17 Mar 2022 15:31:22 -0400 Subject: drm/amdkfd: Fix Incorrect VMIDs passed to HWS Compute-only GPUs have more than 8 VMIDs allocated to KFD. Fix this by passing correct number of VMIDs to HWS v2: squash in warning fix (Alex) Signed-off-by: Tushar Patel Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_device.c | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index bb1c025d9001..b03663f42cc9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -680,7 +680,7 @@ MODULE_PARM_DESC(sched_policy, * Maximum number of processes that HWS can schedule concurrently. The maximum is the * number of VMIDs assigned to the HWS, which is also the default. */ -int hws_max_conc_proc = 8; +int hws_max_conc_proc = -1; module_param(hws_max_conc_proc, int, 0444); MODULE_PARM_DESC(hws_max_conc_proc, "Max # processes HWS can execute concurrently when sched_policy=0 (0 = no concurrency, #VMIDs for KFD = Maximum(default))"); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 339e12c94cff..0887e26ce23b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -483,15 +483,10 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, } /* Verify module parameters regarding mapped process number*/ - if ((hws_max_conc_proc < 0) - || (hws_max_conc_proc > kfd->vm_info.vmid_num_kfd)) { - dev_err(kfd_device, - "hws_max_conc_proc %d must be between 0 and %d, use %d instead\n", - hws_max_conc_proc, kfd->vm_info.vmid_num_kfd, - kfd->vm_info.vmid_num_kfd); + if (hws_max_conc_proc >= 0) + kfd->max_proc_per_quantum = min((u32)hws_max_conc_proc, kfd->vm_info.vmid_num_kfd); + else kfd->max_proc_per_quantum = kfd->vm_info.vmid_num_kfd; - } else - kfd->max_proc_per_quantum = hws_max_conc_proc; /* calculate max size of mqds needed for queues */ size = max_num_of_queues_per_device * -- cgit From c5650327aba02d15cbd6a1846dcde9231493d549 Mon Sep 17 00:00:00 2001 From: Divya Shikre Date: Tue, 22 Mar 2022 15:00:12 -0400 Subject: drm/amdkfd: Check use_xgmi_p2p before reporting hive_id Recently introduced commit 158a05a0b885 ("drm/amdgpu: Add use_xgmi_p2p module parameter") did not update XGMI iolinks when use_xgmi_p2p is disabled. Add fix to not create XGMI iolinks in KFD topology when this parameter is disabled. Fixes: 158a05a0b885 ("drm/amdgpu: Add use_xgmi_p2p module parameter") Signed-off-by: Divya Shikre Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 0887e26ce23b..62aa6c9d5123 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -531,7 +531,8 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, goto kfd_doorbell_error; } - kfd->hive_id = kfd->adev->gmc.xgmi.hive_id; + if (amdgpu_use_xgmi_p2p) + kfd->hive_id = kfd->adev->gmc.xgmi.hive_id; kfd->noretry = kfd->adev->gmc.noretry; -- cgit From 6ea239adc2a712eb318f04f5c29b018ba65ea38a Mon Sep 17 00:00:00 2001 From: Tianci Yin Date: Wed, 23 Mar 2022 23:54:58 +0800 Subject: drm/amdgpu/vcn: improve vcn dpg stop procedure Prior to disabling dpg, VCN need unpausing dpg mode, or VCN will hang in S3 resuming. Reviewed-by: James Zhu Signed-off-by: Tianci Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index b16c56aa2d22..0d590183328f 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -1480,8 +1480,11 @@ static int vcn_v3_0_start_sriov(struct amdgpu_device *adev) static int vcn_v3_0_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx) { + struct dpg_pause_state state = {.fw_based = VCN_DPG_STATE__UNPAUSE}; uint32_t tmp; + vcn_v3_0_pause_dpg_mode(adev, 0, &state); + /* Wait for power status to be 1 */ SOC15_WAIT_ON_RREG(VCN, inst_idx, mmUVD_POWER_STATUS, 1, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK); -- cgit From ebbb7bb9e80305820dc2328a371c1b35679f2667 Mon Sep 17 00:00:00 2001 From: QintaoShen Date: Thu, 24 Mar 2022 16:26:23 +0800 Subject: drm/amdkfd: Check for potential null return of kmalloc_array() As the kmalloc_array() may return null, the 'event_waiters[i].wait' would lead to null-pointer dereference. Therefore, it is better to check the return value of kmalloc_array() to avoid this confusion. Signed-off-by: QintaoShen Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index deecccebe5b6..64f4a51cc880 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -749,6 +749,8 @@ static struct kfd_event_waiter *alloc_event_waiters(uint32_t num_events) event_waiters = kmalloc_array(num_events, sizeof(struct kfd_event_waiter), GFP_KERNEL); + if (!event_waiters) + return NULL; for (i = 0; (event_waiters) && (i < num_events) ; i++) { init_wait(&event_waiters[i].wait); -- cgit From 5f3854f1f4e211f494018160b348a1c16e58013f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 24 Mar 2022 18:04:00 -0400 Subject: drm/amdgpu: add more cases to noretry=1 Port current list from amd-staging-drm-next. Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index ca2cfb65f976..f7216afe8a6a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -564,6 +564,9 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) case IP_VERSION(9, 4, 0): case IP_VERSION(9, 4, 1): case IP_VERSION(9, 4, 2): + case IP_VERSION(10, 3, 5): + case IP_VERSION(10, 3, 3): + case IP_VERSION(9, 3, 0): /* * noretry = 0 will cause kfd page fault tests fail * for some ASICs, so set default to 1 for these ASICs. -- cgit From 7057c81773ac32fd0dba00e2bb869928f008d3e2 Mon Sep 17 00:00:00 2001 From: Yifan Zhang Date: Tue, 1 Mar 2022 22:31:40 +0800 Subject: drm/amdgpu: set noretry=1 for gc 10.3.6 this patch to set noretry=1 for gc 10.3.6. Signed-off-by: Yifan Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index f7216afe8a6a..7021e8f390bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -567,6 +567,7 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) case IP_VERSION(10, 3, 5): case IP_VERSION(10, 3, 3): case IP_VERSION(9, 3, 0): + case IP_VERSION(10, 3, 6): /* * noretry = 0 will cause kfd page fault tests fail * for some ASICs, so set default to 1 for these ASICs. -- cgit From 609910db56e72e87755d9745442bfdfa009dc61b Mon Sep 17 00:00:00 2001 From: Felix Kuehling Date: Wed, 2 Mar 2022 15:18:18 -0500 Subject: drm/amdgpu: set noretry=1 for GFX 10.3.4 Retry faults are not supported on GFX 10.3.4. Signed-off-by: Felix Kuehling Reviewed-by: Guchun Chen Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 7021e8f390bd..e1635a3f2553 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -561,12 +561,13 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) switch (adev->ip_versions[GC_HWIP][0]) { case IP_VERSION(9, 0, 1): + case IP_VERSION(9, 3, 0): case IP_VERSION(9, 4, 0): case IP_VERSION(9, 4, 1): case IP_VERSION(9, 4, 2): - case IP_VERSION(10, 3, 5): case IP_VERSION(10, 3, 3): - case IP_VERSION(9, 3, 0): + case IP_VERSION(10, 3, 4): + case IP_VERSION(10, 3, 5): case IP_VERSION(10, 3, 6): /* * noretry = 0 will cause kfd page fault tests fail -- cgit From 0dc386add50b07e1cf9341b4e6e4fea77295c98a Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Tue, 22 Mar 2022 16:25:29 +0800 Subject: drm/amdgpu: set noretry for gfx 10.3.7 Disable xnack on the gfx10.3.7 for the KFD test. Signed-off-by: Prike Liang Acked-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index e1635a3f2553..a66a0881a934 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -569,6 +569,7 @@ void amdgpu_gmc_noretry_set(struct amdgpu_device *adev) case IP_VERSION(10, 3, 4): case IP_VERSION(10, 3, 5): case IP_VERSION(10, 3, 6): + case IP_VERSION(10, 3, 7): /* * noretry = 0 will cause kfd page fault tests fail * for some ASICs, so set default to 1 for these ASICs. -- cgit From 15f9cd4334c83716fa32647652a609e3ba6c998d Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Thu, 24 Mar 2022 16:14:16 +0800 Subject: drm/amdgpu/gfx10: enable gfx1037 clock counter retrieval function Enable gfx1037 clock counter retrieval function for KFDPerfCountersTest.ClockCountersBasicTest. Signed-off-by: Prike Liang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index f4c6accd3226..a98b78e0b507 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -7689,6 +7689,7 @@ static uint64_t gfx_v10_0_get_gpu_clock_counter(struct amdgpu_device *adev) switch (adev->ip_versions[GC_HWIP][0]) { case IP_VERSION(10, 3, 1): case IP_VERSION(10, 3, 3): + case IP_VERSION(10, 3, 7): preempt_disable(); clock_hi = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Vangogh); clock_lo = RREG32_SOC15_NO_KIQ(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Vangogh); -- cgit From 86ad0397250c585096d242dc3c0f774320ed1a80 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 19 Jan 2022 12:23:09 +0200 Subject: drm: omapdrm: simplify omap_gem_pin Move tiler related code to its own function. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/1642587791-13222-2-git-send-email-ivo.g.dimitrov.75@gmail.com --- drivers/gpu/drm/omapdrm/omap_gem.c | 75 +++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index b0fa17409b66..bb12cb49c92a 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -750,6 +750,46 @@ void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, } } +static int omap_gem_pin_tiler(struct drm_gem_object *obj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + u32 npages = obj->size >> PAGE_SHIFT; + enum tiler_fmt fmt = gem2fmt(omap_obj->flags); + struct tiler_block *block; + int ret; + + BUG_ON(omap_obj->block); + + if (omap_obj->flags & OMAP_BO_TILED_MASK) { + block = tiler_reserve_2d(fmt, omap_obj->width, omap_obj->height, + PAGE_SIZE); + } else { + block = tiler_reserve_1d(obj->size); + } + + if (IS_ERR(block)) { + ret = PTR_ERR(block); + dev_err(obj->dev->dev, "could not remap: %d (%d)\n", ret, fmt); + goto fail; + } + + /* TODO: enable async refill.. */ + ret = tiler_pin(block, omap_obj->pages, npages, omap_obj->roll, true); + if (ret) { + tiler_release(block); + dev_err(obj->dev->dev, "could not pin: %d\n", ret); + goto fail; + } + + omap_obj->dma_addr = tiler_ssptr(block); + omap_obj->block = block; + + DBG("got dma address: %pad", &omap_obj->dma_addr); + +fail: + return ret; +} + /** * omap_gem_pin() - Pin a GEM object in memory * @obj: the GEM object @@ -774,11 +814,6 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) if (!omap_gem_is_contiguous(omap_obj) && priv->has_dmm) { if (refcount_read(&omap_obj->dma_addr_cnt) == 0) { - u32 npages = obj->size >> PAGE_SHIFT; - enum tiler_fmt fmt = gem2fmt(omap_obj->flags); - struct tiler_block *block; - - BUG_ON(omap_obj->block); refcount_set(&omap_obj->dma_addr_cnt, 1); @@ -786,35 +821,9 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) if (ret) goto fail; - if (omap_obj->flags & OMAP_BO_TILED_MASK) { - block = tiler_reserve_2d(fmt, - omap_obj->width, - omap_obj->height, PAGE_SIZE); - } else { - block = tiler_reserve_1d(obj->size); - } - - if (IS_ERR(block)) { - ret = PTR_ERR(block); - dev_err(obj->dev->dev, - "could not remap: %d (%d)\n", ret, fmt); - goto fail; - } - - /* TODO: enable async refill.. */ - ret = tiler_pin(block, omap_obj->pages, npages, - omap_obj->roll, true); - if (ret) { - tiler_release(block); - dev_err(obj->dev->dev, - "could not pin: %d\n", ret); + ret = omap_gem_pin_tiler(obj); + if (ret) goto fail; - } - - omap_obj->dma_addr = tiler_ssptr(block); - omap_obj->block = block; - - DBG("got dma address: %pad", &omap_obj->dma_addr); } else { refcount_inc(&omap_obj->dma_addr_cnt); } -- cgit From 1948d28db6215d055930f0cbb6e300622ede5931 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 19 Jan 2022 12:23:10 +0200 Subject: drm: omapdrm: Support exporting of non-contiguous GEM BOs Currently code allocates non-scanout BOs from SHMEM and those objects are accessible to userspace by mmap(). However, on devices with no DMM (like OMAP3), the same objects are not accessible by kernel drivers that want to render to them as code refuses to export them. In turn this means that on devices with no DMM, all buffers must be allocated as scanout, otherwise only CPU can access them. On those devices, scanout buffers are allocated from CMA, making those allocations highly unreliable. Fix that by implementing functionality to export SHMEM backed buffers on devices with no DMM. This makes CMA memory only being used when needed, instead for every buffer that has to be off-CPU rendered. Tested on Motorola Droid4 and Nokia N900 Signed-off-by: Ivaylo Dimitrov Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/1642587791-13222-3-git-send-email-ivo.g.dimitrov.75@gmail.com --- drivers/gpu/drm/omapdrm/omap_gem.c | 125 +++++++++++++++++------------- drivers/gpu/drm/omapdrm/omap_gem.h | 3 +- drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 5 +- 3 files changed, 73 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index bb12cb49c92a..41c1a6d83b2b 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -38,7 +38,7 @@ struct omap_gem_object { /** roll applied when mapping to DMM */ u32 roll; - /** protects dma_addr_cnt, block, pages, dma_addrs and vaddr */ + /** protects pin_cnt, block, pages, dma_addrs and vaddr */ struct mutex lock; /** @@ -50,24 +50,24 @@ struct omap_gem_object { * - buffers imported from dmabuf (with the OMAP_BO_MEM_DMABUF flag set) * if they are physically contiguous (when sgt->orig_nents == 1) * - * - buffers mapped through the TILER when dma_addr_cnt is not zero, in - * which case the DMA address points to the TILER aperture + * - buffers mapped through the TILER when pin_cnt is not zero, in which + * case the DMA address points to the TILER aperture * * Physically contiguous buffers have their DMA address equal to the * physical address as we don't remap those buffers through the TILER. * * Buffers mapped to the TILER have their DMA address pointing to the - * TILER aperture. As TILER mappings are refcounted (through - * dma_addr_cnt) the DMA address must be accessed through omap_gem_pin() - * to ensure that the mapping won't disappear unexpectedly. References - * must be released with omap_gem_unpin(). + * TILER aperture. As TILER mappings are refcounted (through pin_cnt) + * the DMA address must be accessed through omap_gem_pin() to ensure + * that the mapping won't disappear unexpectedly. References must be + * released with omap_gem_unpin(). */ dma_addr_t dma_addr; /** - * # of users of dma_addr + * # of users */ - refcount_t dma_addr_cnt; + refcount_t pin_cnt; /** * If the buffer has been imported from a dmabuf the OMAP_DB_DMABUF flag @@ -812,32 +812,28 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) mutex_lock(&omap_obj->lock); - if (!omap_gem_is_contiguous(omap_obj) && priv->has_dmm) { - if (refcount_read(&omap_obj->dma_addr_cnt) == 0) { + if (!omap_gem_is_contiguous(omap_obj)) { + if (refcount_read(&omap_obj->pin_cnt) == 0) { - refcount_set(&omap_obj->dma_addr_cnt, 1); + refcount_set(&omap_obj->pin_cnt, 1); ret = omap_gem_attach_pages(obj); if (ret) goto fail; - ret = omap_gem_pin_tiler(obj); - if (ret) - goto fail; + if (priv->has_dmm) { + ret = omap_gem_pin_tiler(obj); + if (ret) + goto fail; + } } else { - refcount_inc(&omap_obj->dma_addr_cnt); + refcount_inc(&omap_obj->pin_cnt); } - - if (dma_addr) - *dma_addr = omap_obj->dma_addr; - } else if (omap_gem_is_contiguous(omap_obj)) { - if (dma_addr) - *dma_addr = omap_obj->dma_addr; - } else { - ret = -EINVAL; - goto fail; } + if (dma_addr) + *dma_addr = omap_obj->dma_addr; + fail: mutex_unlock(&omap_obj->lock); @@ -856,27 +852,29 @@ static void omap_gem_unpin_locked(struct drm_gem_object *obj) struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret; - if (omap_gem_is_contiguous(omap_obj) || !priv->has_dmm) + if (omap_gem_is_contiguous(omap_obj)) return; - if (refcount_dec_and_test(&omap_obj->dma_addr_cnt)) { + if (refcount_dec_and_test(&omap_obj->pin_cnt)) { if (omap_obj->sgt) { sg_free_table(omap_obj->sgt); kfree(omap_obj->sgt); omap_obj->sgt = NULL; } - ret = tiler_unpin(omap_obj->block); - if (ret) { - dev_err(obj->dev->dev, - "could not unpin pages: %d\n", ret); - } - ret = tiler_release(omap_obj->block); - if (ret) { - dev_err(obj->dev->dev, - "could not release unmap: %d\n", ret); + if (priv->has_dmm) { + ret = tiler_unpin(omap_obj->block); + if (ret) { + dev_err(obj->dev->dev, + "could not unpin pages: %d\n", ret); + } + ret = tiler_release(omap_obj->block); + if (ret) { + dev_err(obj->dev->dev, + "could not release unmap: %d\n", ret); + } + omap_obj->dma_addr = 0; + omap_obj->block = NULL; } - omap_obj->dma_addr = 0; - omap_obj->block = NULL; } } @@ -909,7 +907,7 @@ int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, u32 orient, mutex_lock(&omap_obj->lock); - if ((refcount_read(&omap_obj->dma_addr_cnt) > 0) && omap_obj->block && + if ((refcount_read(&omap_obj->pin_cnt) > 0) && omap_obj->block && (omap_obj->flags & OMAP_BO_TILED_MASK)) { *dma_addr = tiler_tsptr(omap_obj->block, orient, x, y); ret = 0; @@ -977,7 +975,8 @@ int omap_gem_put_pages(struct drm_gem_object *obj) return 0; } -struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj) +struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj, + enum dma_data_direction dir) { struct omap_gem_object *omap_obj = to_omap_bo(obj); dma_addr_t addr; @@ -1002,28 +1001,44 @@ struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj) goto err_unpin; } - if (omap_obj->flags & OMAP_BO_TILED_MASK) { - enum tiler_fmt fmt = gem2fmt(omap_obj->flags); + if (addr) { + if (omap_obj->flags & OMAP_BO_TILED_MASK) { + enum tiler_fmt fmt = gem2fmt(omap_obj->flags); - len = omap_obj->width << (int)fmt; - count = omap_obj->height; - stride = tiler_stride(fmt, 0); + len = omap_obj->width << (int)fmt; + count = omap_obj->height; + stride = tiler_stride(fmt, 0); + } else { + len = obj->size; + count = 1; + stride = 0; + } } else { - len = obj->size; - count = 1; - stride = 0; + count = obj->size >> PAGE_SHIFT; } ret = sg_alloc_table(sgt, count, GFP_KERNEL); if (ret) goto err_free; - for_each_sg(sgt->sgl, sg, count, i) { - sg_set_page(sg, phys_to_page(addr), len, offset_in_page(addr)); - sg_dma_address(sg) = addr; - sg_dma_len(sg) = len; + /* this must be after omap_gem_pin() to ensure we have pages attached */ + omap_gem_dma_sync_buffer(obj, dir); + + if (addr) { + for_each_sg(sgt->sgl, sg, count, i) { + sg_set_page(sg, phys_to_page(addr), len, + offset_in_page(addr)); + sg_dma_address(sg) = addr; + sg_dma_len(sg) = len; - addr += stride; + addr += stride; + } + } else { + for_each_sg(sgt->sgl, sg, count, i) { + sg_set_page(sg, omap_obj->pages[i], PAGE_SIZE, 0); + sg_dma_address(sg) = omap_obj->dma_addrs[i]; + sg_dma_len(sg) = PAGE_SIZE; + } } omap_obj->sgt = sgt; @@ -1133,7 +1148,7 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d", omap_obj->flags, obj->name, kref_read(&obj->refcount), off, &omap_obj->dma_addr, - refcount_read(&omap_obj->dma_addr_cnt), + refcount_read(&omap_obj->pin_cnt), omap_obj->vaddr, omap_obj->roll); if (omap_obj->flags & OMAP_BO_TILED_MASK) { @@ -1196,7 +1211,7 @@ static void omap_gem_free_object(struct drm_gem_object *obj) mutex_lock(&omap_obj->lock); /* The object should not be pinned. */ - WARN_ON(refcount_read(&omap_obj->dma_addr_cnt) > 0); + WARN_ON(refcount_read(&omap_obj->pin_cnt) > 0); if (omap_obj->pages) { if (omap_obj->flags & OMAP_BO_MEM_DMABUF) diff --git a/drivers/gpu/drm/omapdrm/omap_gem.h b/drivers/gpu/drm/omapdrm/omap_gem.h index 19209e319663..4d4488939f6b 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.h +++ b/drivers/gpu/drm/omapdrm/omap_gem.h @@ -82,7 +82,8 @@ u32 omap_gem_flags(struct drm_gem_object *obj); int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, u32 orient, int x, int y, dma_addr_t *dma_addr); int omap_gem_tiled_stride(struct drm_gem_object *obj, u32 orient); -struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj); +struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj, + enum dma_data_direction dir); void omap_gem_put_sg(struct drm_gem_object *obj, struct sg_table *sgt); #endif /* __OMAPDRM_GEM_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index ab3fc86e7256..393f82e26927 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -23,13 +23,10 @@ static struct sg_table *omap_gem_map_dma_buf( { struct drm_gem_object *obj = attachment->dmabuf->priv; struct sg_table *sg; - sg = omap_gem_get_sg(obj); + sg = omap_gem_get_sg(obj, dir); if (IS_ERR(sg)) return sg; - /* this must be after omap_gem_pin() to ensure we have pages attached */ - omap_gem_dma_sync_buffer(obj, dir); - return sg; } -- cgit From 3ccd59f93004c369cc4c0f0939f80cb069f0fc2d Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 19 Jan 2022 12:23:11 +0200 Subject: drm: omapdrm: Do no allocate non-scanout GEMs through DMM/TILER On devices with DMM, all allocations are done through either DMM or TILER. DMM/TILER being a limited resource means that such allocations will start to fail before actual free memory is exhausted. What is even worse is that with time DMM/TILER space gets fragmented to the point that even if we have enough free DMM/TILER space and free memory, allocation fails because there is no big enough free block in DMM/TILER space. Such failures can be easily observed with OMAP xorg DDX, for example - starting few GUI applications (so buffers for their windows are allocated) and then rotating landscape<->portrait while closing and opening new windows soon results in allocation failures. Fix that by mapping buffers through DMM/TILER only when really needed, like, for scanout buffers. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/1642587791-13222-4-git-send-email-ivo.g.dimitrov.75@gmail.com --- drivers/gpu/drm/omapdrm/omap_gem.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 41c1a6d83b2b..cf571796fd26 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -821,10 +821,12 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) if (ret) goto fail; - if (priv->has_dmm) { - ret = omap_gem_pin_tiler(obj); - if (ret) - goto fail; + if (omap_obj->flags & OMAP_BO_SCANOUT) { + if (priv->has_dmm) { + ret = omap_gem_pin_tiler(obj); + if (ret) + goto fail; + } } } else { refcount_inc(&omap_obj->pin_cnt); @@ -861,6 +863,8 @@ static void omap_gem_unpin_locked(struct drm_gem_object *obj) kfree(omap_obj->sgt); omap_obj->sgt = NULL; } + if (!(omap_obj->flags & OMAP_BO_SCANOUT)) + return; if (priv->has_dmm) { ret = tiler_unpin(omap_obj->block); if (ret) { -- cgit From af41d2866f7d75bbb38d487f6ec7770425d70e45 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sun, 27 Mar 2022 09:32:26 +0200 Subject: powerpc/64: Fix build failure with allyesconfig in book3s_64_entry.S Using conditional branches between two files is hasardous, they may get linked too far from each other. arch/powerpc/kvm/book3s_64_entry.o:(.text+0x3ec): relocation truncated to fit: R_PPC64_REL14 (stub) against symbol `system_reset_common' defined in .text section in arch/powerpc/kernel/head_64.o Reorganise the code to use non conditional branches. Fixes: 89d35b239101 ("KVM: PPC: Book3S HV P9: Implement the rest of the P9 path in C") Signed-off-by: Christophe Leroy [mpe: Avoid odd-looking bne ., use named local labels] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/89cf27bf43ee07a0b2879b9e8e2f5cd6386a3645.1648366338.git.christophe.leroy@csgroup.eu --- arch/powerpc/kvm/book3s_64_entry.S | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kvm/book3s_64_entry.S b/arch/powerpc/kvm/book3s_64_entry.S index 05e003eb5d90..e42d1c609e47 100644 --- a/arch/powerpc/kvm/book3s_64_entry.S +++ b/arch/powerpc/kvm/book3s_64_entry.S @@ -414,10 +414,16 @@ END_FTR_SECTION_IFSET(CPU_FTR_DAWR1) */ ld r10,HSTATE_SCRATCH0(r13) cmpwi r10,BOOK3S_INTERRUPT_MACHINE_CHECK - beq machine_check_common + beq .Lcall_machine_check_common cmpwi r10,BOOK3S_INTERRUPT_SYSTEM_RESET - beq system_reset_common + beq .Lcall_system_reset_common b . + +.Lcall_machine_check_common: + b machine_check_common + +.Lcall_system_reset_common: + b system_reset_common #endif -- cgit From 27f26359de9b63131a4990ec341dba969860f2fa Mon Sep 17 00:00:00 2001 From: Xin Ji Date: Tue, 22 Mar 2022 16:02:12 +0800 Subject: drm/bridge: anx7625: Set downstream sink into normal status As downstream sink was set into standby mode while bridge disabled, this patch used for setting downstream sink into normal status while enable bridge. Signed-off-by: Xin Ji Reviewed-by: Pin-Yen Lin Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220322080213.1487134-1-xji@analogixsemi.com --- drivers/gpu/drm/bridge/analogix/anx7625.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 9a2a19ad4202..dcf3275a00fe 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -924,12 +924,20 @@ static void anx7625_dp_start(struct anx7625_data *ctx) { int ret; struct device *dev = &ctx->client->dev; + u8 data; if (!ctx->display_timing_valid) { DRM_DEV_ERROR(dev, "mipi not set display timing yet.\n"); return; } + dev_dbg(dev, "set downstream sink into normal\n"); + /* Downstream sink enter into normal mode */ + data = 1; + ret = anx7625_aux_trans(ctx, DP_AUX_NATIVE_WRITE, 0x000600, 1, &data); + if (ret < 0) + dev_err(dev, "IO error : set sink into normal mode fail\n"); + /* Disable HDCP */ anx7625_write_and(ctx, ctx->i2c.rx_p1_client, 0xee, 0x9f); -- cgit From e795df5bbcf27abd6250a0fb9fa8ad7a9748959f Mon Sep 17 00:00:00 2001 From: Andrey Grodzovsky Date: Mon, 28 Mar 2022 09:25:32 -0400 Subject: drm/sched: Fix htmldoc warning. Fixes the warning. Signed-off-by: Andrey Grodzovsky Reported-by: Stephen Rothwell Reviewed-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20220328132532.406572-1-andrey.grodzovsky@amd.com --- include/drm/gpu_scheduler.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 944f83ef9f2e..0fca8f38bee4 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -270,6 +270,7 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * @sched: the scheduler instance on which this job is scheduled. * @s_fence: contains the fences for the scheduling of job. * @finish_cb: the callback for the finished fence. + * @work: Helper to reschdeule job kill to different context. * @id: a unique id assigned to each job scheduled on the scheduler. * @karma: increment on every hang caused by this job. If this exceeds the hang * limit of the scheduler then the job is marked guilty and will not -- cgit From 6a9b028994025f5033f10d1da30b29dfdc713384 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 16 Jul 2021 14:00:10 +0200 Subject: drm/ttm: move the LRU into resource handling v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way we finally fix the problem that new resource are not immediately evict-able after allocation. That has caused numerous problems including OOM on GDS handling and not being able to use TTM as general resource manager. v2: stop assuming in ttm_resource_fini that res->bo is still valid. v3: cleanup kerneldoc, add more lockdep annotation v4: consistently use res->num_pages Signed-off-by: Christian König Tested-by: Bas Nieuwenhuizen Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220321132601.2161-1-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 8 +- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 115 ++--------------------------- drivers/gpu/drm/ttm/ttm_bo_util.c | 1 - drivers/gpu/drm/ttm/ttm_device.c | 64 ++++++++-------- drivers/gpu/drm/ttm/ttm_resource.c | 126 ++++++++++++++++++++++++++++++-- include/drm/ttm/ttm_bo_api.h | 16 ---- include/drm/ttm/ttm_bo_driver.h | 29 +------- include/drm/ttm/ttm_resource.h | 40 ++++++++++ 9 files changed, 204 insertions(+), 197 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index b37fc7d7d2c7..f2ce5a0defd9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -683,12 +683,12 @@ void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev, if (vm->bulk_moveable) { spin_lock(&adev->mman.bdev.lru_lock); - ttm_bo_bulk_move_lru_tail(&vm->lru_bulk_move); + ttm_lru_bulk_move_tail(&vm->lru_bulk_move); spin_unlock(&adev->mman.bdev.lru_lock); return; } - memset(&vm->lru_bulk_move, 0, sizeof(vm->lru_bulk_move)); + ttm_lru_bulk_move_init(&vm->lru_bulk_move); spin_lock(&adev->mman.bdev.lru_lock); list_for_each_entry(bo_base, &vm->idle, vm_status) { @@ -698,11 +698,9 @@ void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev, if (!bo->parent) continue; - ttm_bo_move_to_lru_tail(&bo->tbo, bo->tbo.resource, - &vm->lru_bulk_move); + ttm_bo_move_to_lru_tail(&bo->tbo, &vm->lru_bulk_move); if (shadow) ttm_bo_move_to_lru_tail(&shadow->tbo, - shadow->tbo.resource, &vm->lru_bulk_move); } spin_unlock(&adev->mman.bdev.lru_lock); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index de3fe79b665a..582e8dc9bc8c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -849,7 +849,7 @@ void i915_ttm_adjust_lru(struct drm_i915_gem_object *obj) bo->priority = I915_TTM_PRIO_NO_PAGES; } - ttm_bo_move_to_lru_tail(bo, bo->resource, NULL); + ttm_bo_move_to_lru_tail(bo, NULL); spin_unlock(&bo->bdev->lru_lock); } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index db3dc7ef5382..cb0fa932d495 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -69,108 +69,16 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, } } -static inline void ttm_bo_move_to_pinned(struct ttm_buffer_object *bo) -{ - struct ttm_device *bdev = bo->bdev; - - list_move_tail(&bo->lru, &bdev->pinned); - - if (bdev->funcs->del_from_lru_notify) - bdev->funcs->del_from_lru_notify(bo); -} - -static inline void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) -{ - struct ttm_device *bdev = bo->bdev; - - list_del_init(&bo->lru); - - if (bdev->funcs->del_from_lru_notify) - bdev->funcs->del_from_lru_notify(bo); -} - -static void ttm_bo_bulk_move_set_pos(struct ttm_lru_bulk_move_pos *pos, - struct ttm_buffer_object *bo) -{ - if (!pos->first) - pos->first = bo; - pos->last = bo; -} - void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo, - struct ttm_resource *mem, struct ttm_lru_bulk_move *bulk) { - struct ttm_device *bdev = bo->bdev; - struct ttm_resource_manager *man; - - if (!bo->deleted) - dma_resv_assert_held(bo->base.resv); - - if (bo->pin_count) { - ttm_bo_move_to_pinned(bo); - return; - } - - if (!mem) - return; - - man = ttm_manager_type(bdev, mem->mem_type); - list_move_tail(&bo->lru, &man->lru[bo->priority]); - - if (bdev->funcs->del_from_lru_notify) - bdev->funcs->del_from_lru_notify(bo); - - if (bulk && !bo->pin_count) { - switch (bo->resource->mem_type) { - case TTM_PL_TT: - ttm_bo_bulk_move_set_pos(&bulk->tt[bo->priority], bo); - break; + dma_resv_assert_held(bo->base.resv); - case TTM_PL_VRAM: - ttm_bo_bulk_move_set_pos(&bulk->vram[bo->priority], bo); - break; - } - } + if (bo->resource) + ttm_resource_move_to_lru_tail(bo->resource, bulk); } EXPORT_SYMBOL(ttm_bo_move_to_lru_tail); -void ttm_bo_bulk_move_lru_tail(struct ttm_lru_bulk_move *bulk) -{ - unsigned i; - - for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { - struct ttm_lru_bulk_move_pos *pos = &bulk->tt[i]; - struct ttm_resource_manager *man; - - if (!pos->first) - continue; - - dma_resv_assert_held(pos->first->base.resv); - dma_resv_assert_held(pos->last->base.resv); - - man = ttm_manager_type(pos->first->bdev, TTM_PL_TT); - list_bulk_move_tail(&man->lru[i], &pos->first->lru, - &pos->last->lru); - } - - for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { - struct ttm_lru_bulk_move_pos *pos = &bulk->vram[i]; - struct ttm_resource_manager *man; - - if (!pos->first) - continue; - - dma_resv_assert_held(pos->first->base.resv); - dma_resv_assert_held(pos->last->base.resv); - - man = ttm_manager_type(pos->first->bdev, TTM_PL_VRAM); - list_bulk_move_tail(&man->lru[i], &pos->first->lru, - &pos->last->lru); - } -} -EXPORT_SYMBOL(ttm_bo_bulk_move_lru_tail); - static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_resource *mem, bool evict, struct ttm_operation_ctx *ctx, @@ -344,7 +252,6 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, return ret; } - ttm_bo_move_to_pinned(bo); list_del_init(&bo->ddestroy); spin_unlock(&bo->bdev->lru_lock); ttm_bo_cleanup_memtype_use(bo); @@ -445,7 +352,7 @@ static void ttm_bo_release(struct kref *kref) */ if (bo->pin_count) { bo->pin_count = 0; - ttm_bo_move_to_lru_tail(bo, bo->resource, NULL); + ttm_resource_move_to_lru_tail(bo->resource, NULL); } kref_init(&bo->kref); @@ -458,7 +365,6 @@ static void ttm_bo_release(struct kref *kref) } spin_lock(&bo->bdev->lru_lock); - ttm_bo_del_from_lru(bo); list_del(&bo->ddestroy); spin_unlock(&bo->bdev->lru_lock); @@ -673,15 +579,17 @@ int ttm_mem_evict_first(struct ttm_device *bdev, struct ww_acquire_ctx *ticket) { struct ttm_buffer_object *bo = NULL, *busy_bo = NULL; + struct ttm_resource *res; bool locked = false; unsigned i; int ret; spin_lock(&bdev->lru_lock); for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { - list_for_each_entry(bo, &man->lru[i], lru) { + list_for_each_entry(res, &man->lru[i], lru) { bool busy; + bo = res->bo; if (!ttm_bo_evict_swapout_allowable(bo, ctx, place, &locked, &busy)) { if (busy && !busy_bo && ticket != @@ -699,7 +607,7 @@ int ttm_mem_evict_first(struct ttm_device *bdev, } /* If the inner loop terminated early, we have our candidate */ - if (&bo->lru != &man->lru[i]) + if (&res->lru != &man->lru[i]) break; bo = NULL; @@ -875,9 +783,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, } error: - if (bo->resource->mem_type == TTM_PL_SYSTEM && !bo->pin_count) - ttm_bo_move_to_lru_tail_unlocked(bo); - return ret; } EXPORT_SYMBOL(ttm_bo_mem_space); @@ -971,7 +876,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, bo->destroy = destroy ? destroy : ttm_bo_default_destroy; kref_init(&bo->kref); - INIT_LIST_HEAD(&bo->lru); INIT_LIST_HEAD(&bo->ddestroy); bo->bdev = bdev; bo->type = type; @@ -1021,8 +925,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, return ret; } - ttm_bo_move_to_lru_tail_unlocked(bo); - return ret; } EXPORT_SYMBOL(ttm_bo_init_reserved); @@ -1123,7 +1025,6 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, return ret == -EBUSY ? -ENOSPC : ret; } - ttm_bo_move_to_pinned(bo); /* TODO: Cleanup the locking */ spin_unlock(&bo->bdev->lru_lock); diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 544a84fa6589..0163e92b57af 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -231,7 +231,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, atomic_inc(&ttm_glob.bo_count); INIT_LIST_HEAD(&fbo->base.ddestroy); - INIT_LIST_HEAD(&fbo->base.lru); fbo->base.moving = NULL; drm_vma_node_reset(&fbo->base.base.vma_node); diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index be24bb6cefd0..ba35887147ba 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -144,6 +144,7 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, { struct ttm_resource_manager *man; struct ttm_buffer_object *bo; + struct ttm_resource *res; unsigned i, j; int ret; @@ -154,8 +155,11 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, continue; for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) { - list_for_each_entry(bo, &man->lru[j], lru) { - uint32_t num_pages = PFN_UP(bo->base.size); + list_for_each_entry(res, &man->lru[j], lru) { + uint32_t num_pages; + + bo = res->bo; + num_pages = PFN_UP(bo->base.size); ret = ttm_bo_swapout(bo, ctx, gfp_flags); /* ttm_bo_swapout has dropped the lru_lock */ @@ -259,49 +263,45 @@ void ttm_device_fini(struct ttm_device *bdev) } EXPORT_SYMBOL(ttm_device_fini); -void ttm_device_clear_dma_mappings(struct ttm_device *bdev) +static void ttm_device_clear_lru_dma_mappings(struct ttm_device *bdev, + struct list_head *list) { - struct ttm_resource_manager *man; - struct ttm_buffer_object *bo; - unsigned int i, j; + struct ttm_resource *res; spin_lock(&bdev->lru_lock); - while (!list_empty(&bdev->pinned)) { - bo = list_first_entry(&bdev->pinned, struct ttm_buffer_object, lru); + while ((res = list_first_entry_or_null(list, typeof(*res), lru))) { + struct ttm_buffer_object *bo = res->bo; + /* Take ref against racing releases once lru_lock is unlocked */ - if (ttm_bo_get_unless_zero(bo)) { - list_del_init(&bo->lru); - spin_unlock(&bdev->lru_lock); + if (!ttm_bo_get_unless_zero(bo)) + continue; - if (bo->ttm) - ttm_tt_unpopulate(bo->bdev, bo->ttm); + list_del_init(&res->lru); + spin_unlock(&bdev->lru_lock); - ttm_bo_put(bo); - spin_lock(&bdev->lru_lock); - } + if (bo->ttm) + ttm_tt_unpopulate(bo->bdev, bo->ttm); + + ttm_bo_put(bo); + spin_lock(&bdev->lru_lock); } + spin_unlock(&bdev->lru_lock); +} + +void ttm_device_clear_dma_mappings(struct ttm_device *bdev) +{ + struct ttm_resource_manager *man; + unsigned int i, j; + + ttm_device_clear_lru_dma_mappings(bdev, &bdev->pinned); for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) { man = ttm_manager_type(bdev, i); if (!man || !man->use_tt) continue; - for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) { - while (!list_empty(&man->lru[j])) { - bo = list_first_entry(&man->lru[j], struct ttm_buffer_object, lru); - if (ttm_bo_get_unless_zero(bo)) { - list_del_init(&bo->lru); - spin_unlock(&bdev->lru_lock); - - if (bo->ttm) - ttm_tt_unpopulate(bo->bdev, bo->ttm); - - ttm_bo_put(bo); - spin_lock(&bdev->lru_lock); - } - } - } + for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) + ttm_device_clear_lru_dma_mappings(bdev, &man->lru[j]); } - spin_unlock(&bdev->lru_lock); } EXPORT_SYMBOL(ttm_device_clear_dma_mappings); diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index cbd47a104962..2b76ecdf12e8 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -29,13 +29,117 @@ #include #include +/** + * ttm_lru_bulk_move_init - initialize a bulk move structure + * @bulk: the structure to init + * + * For now just memset the structure to zero. + */ +void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk) +{ + memset(bulk, 0, sizeof(*bulk)); +} +EXPORT_SYMBOL(ttm_lru_bulk_move_init); + +/** + * ttm_lru_bulk_move_tail - bulk move range of resources to the LRU tail. + * + * @bulk: bulk move structure + * + * Bulk move BOs to the LRU tail, only valid to use when driver makes sure that + * resource order never changes. Should be called with &ttm_device.lru_lock held. + */ +void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk) +{ + unsigned i; + + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { + struct ttm_lru_bulk_move_pos *pos = &bulk->tt[i]; + struct ttm_resource_manager *man; + + if (!pos->first) + continue; + + lockdep_assert_held(&pos->first->bo->bdev->lru_lock); + dma_resv_assert_held(pos->first->bo->base.resv); + dma_resv_assert_held(pos->last->bo->base.resv); + + man = ttm_manager_type(pos->first->bo->bdev, TTM_PL_TT); + list_bulk_move_tail(&man->lru[i], &pos->first->lru, + &pos->last->lru); + } + + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { + struct ttm_lru_bulk_move_pos *pos = &bulk->vram[i]; + struct ttm_resource_manager *man; + + if (!pos->first) + continue; + + lockdep_assert_held(&pos->first->bo->bdev->lru_lock); + dma_resv_assert_held(pos->first->bo->base.resv); + dma_resv_assert_held(pos->last->bo->base.resv); + + man = ttm_manager_type(pos->first->bo->bdev, TTM_PL_VRAM); + list_bulk_move_tail(&man->lru[i], &pos->first->lru, + &pos->last->lru); + } +} +EXPORT_SYMBOL(ttm_lru_bulk_move_tail); + +/* Record a resource position in a bulk move structure */ +static void ttm_lru_bulk_move_set_pos(struct ttm_lru_bulk_move_pos *pos, + struct ttm_resource *res) +{ + if (!pos->first) + pos->first = res; + pos->last = res; +} + +/* Move a resource to the LRU tail and track the bulk position */ +void ttm_resource_move_to_lru_tail(struct ttm_resource *res, + struct ttm_lru_bulk_move *bulk) +{ + struct ttm_buffer_object *bo = res->bo; + struct ttm_device *bdev = bo->bdev; + struct ttm_resource_manager *man; + + lockdep_assert_held(&bo->bdev->lru_lock); + + if (bo->pin_count) { + list_move_tail(&res->lru, &bdev->pinned); + if (bdev->funcs->del_from_lru_notify) + bdev->funcs->del_from_lru_notify(res->bo); + return; + } + + man = ttm_manager_type(bdev, res->mem_type); + list_move_tail(&res->lru, &man->lru[bo->priority]); + + if (bdev->funcs->del_from_lru_notify) + bdev->funcs->del_from_lru_notify(bo); + + if (!bulk) + return; + + switch (res->mem_type) { + case TTM_PL_TT: + ttm_lru_bulk_move_set_pos(&bulk->tt[bo->priority], res); + break; + + case TTM_PL_VRAM: + ttm_lru_bulk_move_set_pos(&bulk->vram[bo->priority], res); + break; + } +} + /** * ttm_resource_init - resource object constructure * @bo: buffer object this resources is allocated for * @place: placement of the resource * @res: the resource object to inistilize * - * Initialize a new resource object. Counterpart of &ttm_resource_fini. + * Initialize a new resource object. Counterpart of ttm_resource_fini(). */ void ttm_resource_init(struct ttm_buffer_object *bo, const struct ttm_place *place, @@ -52,10 +156,12 @@ void ttm_resource_init(struct ttm_buffer_object *bo, res->bus.is_iomem = false; res->bus.caching = ttm_cached; res->bo = bo; + INIT_LIST_HEAD(&res->lru); man = ttm_manager_type(bo->bdev, place->mem_type); spin_lock(&bo->bdev->lru_lock); - man->usage += bo->base.size; + man->usage += res->num_pages << PAGE_SHIFT; + ttm_resource_move_to_lru_tail(res, NULL); spin_unlock(&bo->bdev->lru_lock); } EXPORT_SYMBOL(ttm_resource_init); @@ -66,15 +172,21 @@ EXPORT_SYMBOL(ttm_resource_init); * @res: the resource to clean up * * Should be used by resource manager backends to clean up the TTM resource - * objects before freeing the underlying structure. Counterpart of - * &ttm_resource_init + * objects before freeing the underlying structure. Makes sure the resource is + * removed from the LRU before destruction. + * Counterpart of ttm_resource_init(). */ void ttm_resource_fini(struct ttm_resource_manager *man, struct ttm_resource *res) { - spin_lock(&man->bdev->lru_lock); - man->usage -= res->bo->base.size; - spin_unlock(&man->bdev->lru_lock); + struct ttm_device *bdev = man->bdev; + + spin_lock(&bdev->lru_lock); + list_del_init(&res->lru); + if (res->bo && bdev->funcs->del_from_lru_notify) + bdev->funcs->del_from_lru_notify(res->bo); + man->usage -= res->num_pages << PAGE_SHIFT; + spin_unlock(&bdev->lru_lock); } EXPORT_SYMBOL(ttm_resource_fini); diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index c17b2df9178b..3da77fc54552 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -55,8 +55,6 @@ struct ttm_placement; struct ttm_place; -struct ttm_lru_bulk_move; - /** * enum ttm_bo_type * @@ -94,7 +92,6 @@ struct ttm_tt; * @ttm: TTM structure holding system pages. * @evicted: Whether the object was evicted without user-space knowing. * @deleted: True if the object is only a zombie and already deleted. - * @lru: List head for the lru list. * @ddestroy: List head for the delayed destroy list. * @swap: List head for swap LRU list. * @moving: Fence set when BO is moving @@ -143,7 +140,6 @@ struct ttm_buffer_object { * Members protected by the bdev::lru_lock. */ - struct list_head lru; struct list_head ddestroy; /** @@ -295,7 +291,6 @@ void ttm_bo_put(struct ttm_buffer_object *bo); * ttm_bo_move_to_lru_tail * * @bo: The buffer object. - * @mem: Resource object. * @bulk: optional bulk move structure to remember BO positions * * Move this BO to the tail of all lru lists used to lookup and reserve an @@ -303,19 +298,8 @@ void ttm_bo_put(struct ttm_buffer_object *bo); * held, and is used to make a BO less likely to be considered for eviction. */ void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo, - struct ttm_resource *mem, struct ttm_lru_bulk_move *bulk); -/** - * ttm_bo_bulk_move_lru_tail - * - * @bulk: bulk move structure - * - * Bulk move BOs to the LRU tail, only valid to use when driver makes sure that - * BO order never changes. Should be called with ttm_global::lru_lock held. - */ -void ttm_bo_bulk_move_lru_tail(struct ttm_lru_bulk_move *bulk); - /** * ttm_bo_lock_delayed_workqueue * diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 5f087575194b..6c7352e13708 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -45,33 +45,6 @@ #include "ttm_tt.h" #include "ttm_pool.h" -/** - * struct ttm_lru_bulk_move_pos - * - * @first: first BO in the bulk move range - * @last: last BO in the bulk move range - * - * Positions for a lru bulk move. - */ -struct ttm_lru_bulk_move_pos { - struct ttm_buffer_object *first; - struct ttm_buffer_object *last; -}; - -/** - * struct ttm_lru_bulk_move - * - * @tt: first/last lru entry for BOs in the TT domain - * @vram: first/last lru entry for BOs in the VRAM domain - * @swap: first/last lru entry for BOs on the swap list - * - * Helper structure for bulk moves on the LRU list. - */ -struct ttm_lru_bulk_move { - struct ttm_lru_bulk_move_pos tt[TTM_MAX_BO_PRIORITY]; - struct ttm_lru_bulk_move_pos vram[TTM_MAX_BO_PRIORITY]; -}; - /* * ttm_bo.c */ @@ -182,7 +155,7 @@ static inline void ttm_bo_move_to_lru_tail_unlocked(struct ttm_buffer_object *bo) { spin_lock(&bo->bdev->lru_lock); - ttm_bo_move_to_lru_tail(bo, bo->resource, NULL); + ttm_bo_move_to_lru_tail(bo, NULL); spin_unlock(&bo->bdev->lru_lock); } diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 323c14a30c6b..f8f766b092a6 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -26,10 +26,12 @@ #define _TTM_RESOURCE_H_ #include +#include #include #include #include #include + #include #include #include @@ -179,6 +181,38 @@ struct ttm_resource { uint32_t placement; struct ttm_bus_placement bus; struct ttm_buffer_object *bo; + + /** + * @lru: Least recently used list, see &ttm_resource_manager.lru + */ + struct list_head lru; +}; + +/** + * struct ttm_lru_bulk_move_pos + * + * @first: first res in the bulk move range + * @last: last res in the bulk move range + * + * Positions for a lru bulk move. + */ +struct ttm_lru_bulk_move_pos { + struct ttm_resource *first; + struct ttm_resource *last; +}; + +/** + * struct ttm_lru_bulk_move + * + * @tt: first/last lru entry for resources in the TT domain + * @vram: first/last lru entry for resources in the VRAM domain + * + * Container for the current bulk move state. Should be used with + * ttm_lru_bulk_move_init() and ttm_bo_move_to_lru_tail(). + */ +struct ttm_lru_bulk_move { + struct ttm_lru_bulk_move_pos tt[TTM_MAX_BO_PRIORITY]; + struct ttm_lru_bulk_move_pos vram[TTM_MAX_BO_PRIORITY]; }; /** @@ -267,6 +301,12 @@ ttm_resource_manager_cleanup(struct ttm_resource_manager *man) man->move = NULL; } +void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk); +void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk); + +void ttm_resource_move_to_lru_tail(struct ttm_resource *res, + struct ttm_lru_bulk_move *bulk); + void ttm_resource_init(struct ttm_buffer_object *bo, const struct ttm_place *place, struct ttm_resource *res); -- cgit From 5d05b988f1c0fddc4aaffa5fa3c3f5a9e7b84d5b Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 8 Jun 2021 11:13:21 +0200 Subject: drm/ttm: add resource iterator v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of duplicating that at different places add an iterator over all the resources in a resource manager. v2: add lockdep annotation and kerneldoc v3: fix various bugs pointed out by Felix v4: simplify the code a bit more Signed-off-by: Christian König Tested-by: Bas Nieuwenhuizen Reviewed-by: Daniel Vetter (v2) Reviewed-by: Felix Kuehling Link: https://patchwork.freedesktop.org/patch/msgid/20220321132601.2161-2-christian.koenig@amd.com --- drivers/gpu/drm/ttm/ttm_bo.c | 37 +++++++++++---------------- drivers/gpu/drm/ttm/ttm_device.c | 26 ++++++++----------- drivers/gpu/drm/ttm/ttm_resource.c | 51 ++++++++++++++++++++++++++++++++++++++ include/drm/ttm/ttm_resource.h | 23 +++++++++++++++++ 4 files changed, 99 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index cb0fa932d495..b119af33e7d7 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -579,38 +579,29 @@ int ttm_mem_evict_first(struct ttm_device *bdev, struct ww_acquire_ctx *ticket) { struct ttm_buffer_object *bo = NULL, *busy_bo = NULL; + struct ttm_resource_cursor cursor; struct ttm_resource *res; bool locked = false; - unsigned i; int ret; spin_lock(&bdev->lru_lock); - for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { - list_for_each_entry(res, &man->lru[i], lru) { - bool busy; + ttm_resource_manager_for_each_res(man, &cursor, res) { + bool busy; + + if (!ttm_bo_evict_swapout_allowable(res->bo, ctx, place, + &locked, &busy)) { + if (busy && !busy_bo && ticket != + dma_resv_locking_ctx(res->bo->base.resv)) + busy_bo = res->bo; + continue; + } + if (ttm_bo_get_unless_zero(res->bo)) { bo = res->bo; - if (!ttm_bo_evict_swapout_allowable(bo, ctx, place, - &locked, &busy)) { - if (busy && !busy_bo && ticket != - dma_resv_locking_ctx(bo->base.resv)) - busy_bo = bo; - continue; - } - - if (!ttm_bo_get_unless_zero(bo)) { - if (locked) - dma_resv_unlock(bo->base.resv); - continue; - } break; } - - /* If the inner loop terminated early, we have our candidate */ - if (&res->lru != &man->lru[i]) - break; - - bo = NULL; + if (locked) + dma_resv_unlock(res->bo->base.resv); } if (!bo) { diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index ba35887147ba..a0562ab386f5 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -142,10 +142,10 @@ EXPORT_SYMBOL(ttm_global_swapout); int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, gfp_t gfp_flags) { + struct ttm_resource_cursor cursor; struct ttm_resource_manager *man; - struct ttm_buffer_object *bo; struct ttm_resource *res; - unsigned i, j; + unsigned i; int ret; spin_lock(&bdev->lru_lock); @@ -154,20 +154,16 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, if (!man || !man->use_tt) continue; - for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) { - list_for_each_entry(res, &man->lru[j], lru) { - uint32_t num_pages; - - bo = res->bo; - num_pages = PFN_UP(bo->base.size); + ttm_resource_manager_for_each_res(man, &cursor, res) { + struct ttm_buffer_object *bo = res->bo; + uint32_t num_pages = PFN_UP(bo->base.size); - ret = ttm_bo_swapout(bo, ctx, gfp_flags); - /* ttm_bo_swapout has dropped the lru_lock */ - if (!ret) - return num_pages; - if (ret != -EBUSY) - return ret; - } + ret = ttm_bo_swapout(bo, ctx, gfp_flags); + /* ttm_bo_swapout has dropped the lru_lock */ + if (!ret) + return num_pages; + if (ret != -EBUSY) + return ret; } } spin_unlock(&bdev->lru_lock); diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 2b76ecdf12e8..e1d4130e6863 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -385,6 +385,57 @@ void ttm_resource_manager_debug(struct ttm_resource_manager *man, } EXPORT_SYMBOL(ttm_resource_manager_debug); +/** + * ttm_resource_manager_first + * + * @man: resource manager to iterate over + * @cursor: cursor to record the position + * + * Returns the first resource from the resource manager. + */ +struct ttm_resource * +ttm_resource_manager_first(struct ttm_resource_manager *man, + struct ttm_resource_cursor *cursor) +{ + struct ttm_resource *res; + + lockdep_assert_held(&man->bdev->lru_lock); + + for (cursor->priority = 0; cursor->priority < TTM_MAX_BO_PRIORITY; + ++cursor->priority) + list_for_each_entry(res, &man->lru[cursor->priority], lru) + return res; + + return NULL; +} + +/** + * ttm_resource_manager_next + * + * @man: resource manager to iterate over + * @cursor: cursor to record the position + * @res: the current resource pointer + * + * Returns the next resource from the resource manager. + */ +struct ttm_resource * +ttm_resource_manager_next(struct ttm_resource_manager *man, + struct ttm_resource_cursor *cursor, + struct ttm_resource *res) +{ + lockdep_assert_held(&man->bdev->lru_lock); + + list_for_each_entry_continue(res, &man->lru[cursor->priority], lru) + return res; + + for (++cursor->priority; cursor->priority < TTM_MAX_BO_PRIORITY; + ++cursor->priority) + list_for_each_entry(res, &man->lru[cursor->priority], lru) + return res; + + return NULL; +} + static void ttm_kmap_iter_iomap_map_local(struct ttm_kmap_iter *iter, struct dma_buf_map *dmap, pgoff_t i) diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index f8f766b092a6..d8e880110dc6 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -188,6 +188,17 @@ struct ttm_resource { struct list_head lru; }; +/** + * struct ttm_resource_cursor + * + * @priority: the current priority + * + * Cursor to iterate over the resources in a manager. + */ +struct ttm_resource_cursor { + unsigned int priority; +}; + /** * struct ttm_lru_bulk_move_pos * @@ -333,6 +344,18 @@ uint64_t ttm_resource_manager_usage(struct ttm_resource_manager *man); void ttm_resource_manager_debug(struct ttm_resource_manager *man, struct drm_printer *p); +struct ttm_resource * +ttm_resource_manager_first(struct ttm_resource_manager *man, + struct ttm_resource_cursor *cursor); +struct ttm_resource * +ttm_resource_manager_next(struct ttm_resource_manager *man, + struct ttm_resource_cursor *cursor, + struct ttm_resource *res); + +#define ttm_resource_manager_for_each_res(man, cursor, res) \ + for (res = ttm_resource_manager_first(man, cursor); res; \ + res = ttm_resource_manager_next(man, cursor, res)) + struct ttm_kmap_iter * ttm_kmap_iter_iomap_init(struct ttm_kmap_iter_iomap *iter_io, struct io_mapping *iomap, -- cgit From b0e2c9ea5afc769476fd85a6a28cc370ddd44ee8 Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 24 Jan 2022 09:37:43 +0100 Subject: drm/ttm: allow bulk moves for all domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not just TT and VRAM. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Tested-by: Bas Nieuwenhuizen Link: https://patchwork.freedesktop.org/patch/msgid/20220321132601.2161-3-christian.koenig@amd.com --- drivers/gpu/drm/ttm/ttm_resource.c | 52 +++++++++++--------------------------- include/drm/ttm/ttm_device.h | 2 -- include/drm/ttm/ttm_resource.h | 4 +-- 3 files changed, 17 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index e1d4130e6863..19df7350de0f 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -51,38 +51,24 @@ EXPORT_SYMBOL(ttm_lru_bulk_move_init); */ void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk) { - unsigned i; - - for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { - struct ttm_lru_bulk_move_pos *pos = &bulk->tt[i]; - struct ttm_resource_manager *man; + unsigned i, j; - if (!pos->first) - continue; + for (i = 0; i < TTM_NUM_MEM_TYPES; ++i) { + for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) { + struct ttm_lru_bulk_move_pos *pos = &bulk->pos[i][j]; + struct ttm_resource_manager *man; - lockdep_assert_held(&pos->first->bo->bdev->lru_lock); - dma_resv_assert_held(pos->first->bo->base.resv); - dma_resv_assert_held(pos->last->bo->base.resv); + if (!pos->first) + continue; - man = ttm_manager_type(pos->first->bo->bdev, TTM_PL_TT); - list_bulk_move_tail(&man->lru[i], &pos->first->lru, - &pos->last->lru); - } - - for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { - struct ttm_lru_bulk_move_pos *pos = &bulk->vram[i]; - struct ttm_resource_manager *man; + lockdep_assert_held(&pos->first->bo->bdev->lru_lock); + dma_resv_assert_held(pos->first->bo->base.resv); + dma_resv_assert_held(pos->last->bo->base.resv); - if (!pos->first) - continue; - - lockdep_assert_held(&pos->first->bo->bdev->lru_lock); - dma_resv_assert_held(pos->first->bo->base.resv); - dma_resv_assert_held(pos->last->bo->base.resv); - - man = ttm_manager_type(pos->first->bo->bdev, TTM_PL_VRAM); - list_bulk_move_tail(&man->lru[i], &pos->first->lru, - &pos->last->lru); + man = ttm_manager_type(pos->first->bo->bdev, i); + list_bulk_move_tail(&man->lru[j], &pos->first->lru, + &pos->last->lru); + } } } EXPORT_SYMBOL(ttm_lru_bulk_move_tail); @@ -122,15 +108,7 @@ void ttm_resource_move_to_lru_tail(struct ttm_resource *res, if (!bulk) return; - switch (res->mem_type) { - case TTM_PL_TT: - ttm_lru_bulk_move_set_pos(&bulk->tt[bo->priority], res); - break; - - case TTM_PL_VRAM: - ttm_lru_bulk_move_set_pos(&bulk->vram[bo->priority], res); - break; - } + ttm_lru_bulk_move_set_pos(&bulk->pos[res->mem_type][bo->priority], res); } /** diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h index 0a4ddec78d8f..425150f35fbe 100644 --- a/include/drm/ttm/ttm_device.h +++ b/include/drm/ttm/ttm_device.h @@ -30,8 +30,6 @@ #include #include -#define TTM_NUM_MEM_TYPES 8 - struct ttm_device; struct ttm_placement; struct ttm_buffer_object; diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index d8e880110dc6..62e7bb782596 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -37,6 +37,7 @@ #include #define TTM_MAX_BO_PRIORITY 4U +#define TTM_NUM_MEM_TYPES 8 struct ttm_device; struct ttm_resource_manager; @@ -222,8 +223,7 @@ struct ttm_lru_bulk_move_pos { * ttm_lru_bulk_move_init() and ttm_bo_move_to_lru_tail(). */ struct ttm_lru_bulk_move { - struct ttm_lru_bulk_move_pos tt[TTM_MAX_BO_PRIORITY]; - struct ttm_lru_bulk_move_pos vram[TTM_MAX_BO_PRIORITY]; + struct ttm_lru_bulk_move_pos pos[TTM_NUM_MEM_TYPES][TTM_MAX_BO_PRIORITY]; }; /** -- cgit From 6ce4431c7ba7954c4fa6a96ce16ca1b2943e1a83 Mon Sep 17 00:00:00 2001 From: Xiaomeng Tong Date: Sun, 27 Mar 2022 15:39:25 +0800 Subject: drm/nouveau/kms/nv50-: atom: fix an incorrect NULL check on list iterator The bug is here: return encoder; The list iterator value 'encoder' will *always* be set and non-NULL by drm_for_each_encoder_mask(), so it is incorrect to assume that the iterator value will be NULL if the list is empty or no element found. Otherwise it will bypass some NULL checks and lead to invalid memory access passing the check. To fix this bug, just return 'encoder' when found, otherwise return NULL. Cc: stable@vger.kernel.org Fixes: 12885ecbfe62d ("drm/nouveau/kms/nvd9-: Add CRC support") Signed-off-by: Xiaomeng Tong Reviewed-by: Lyude Paul [Changed commit title] Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20220327073925.11121-1-xiam0nd.tong@gmail.com --- drivers/gpu/drm/nouveau/dispnv50/atom.h | 6 +++--- drivers/gpu/drm/nouveau/dispnv50/crc.c | 27 ++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h index 3d82b3c67dec..93f8f4f64578 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/atom.h +++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h @@ -160,14 +160,14 @@ nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc) static inline struct drm_encoder * nv50_head_atom_get_encoder(struct nv50_head_atom *atom) { - struct drm_encoder *encoder = NULL; + struct drm_encoder *encoder; /* We only ever have a single encoder */ drm_for_each_encoder_mask(encoder, atom->state.crtc->dev, atom->state.encoder_mask) - break; + return encoder; - return encoder; + return NULL; } #define nv50_wndw_atom(p) container_of((p), struct nv50_wndw_atom, state) diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.c b/drivers/gpu/drm/nouveau/dispnv50/crc.c index 29428e770f14..b834e8a9ae77 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/crc.c +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.c @@ -390,9 +390,18 @@ void nv50_crc_atomic_check_outp(struct nv50_atom *atom) struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); struct nv50_outp_atom *outp_atom; - struct nouveau_encoder *outp = - nv50_real_outp(nv50_head_atom_get_encoder(armh)); - struct drm_encoder *encoder = &outp->base.base; + struct nouveau_encoder *outp; + struct drm_encoder *encoder, *enc; + + enc = nv50_head_atom_get_encoder(armh); + if (!enc) + continue; + + outp = nv50_real_outp(enc); + if (!outp) + continue; + + encoder = &outp->base.base; if (!asyh->clr.crc) continue; @@ -443,8 +452,16 @@ void nv50_crc_atomic_set(struct nv50_head *head, struct drm_device *dev = crtc->dev; struct nv50_crc *crc = &head->crc; const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; - struct nouveau_encoder *outp = - nv50_real_outp(nv50_head_atom_get_encoder(asyh)); + struct nouveau_encoder *outp; + struct drm_encoder *encoder; + + encoder = nv50_head_atom_get_encoder(asyh); + if (!encoder) + return; + + outp = nv50_real_outp(encoder); + if (!outp) + return; func->set_src(head, outp->or, nv50_crc_source_type(outp, asyh->crc.src), &crc->ctx[crc->ctx_idx]); -- cgit From 21d139d73f776aed1e86f3175a1e9fb8a10930c7 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 29 Mar 2022 08:45:04 +0200 Subject: dma-buf/sync-file: fix logic error in new fence merge code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the array is empty because everything is signaled we can't use add_fence() to add something because that would filter the signaled fence again. Signed-off-by: Christian König Fixes: 519f490db07e ("dma-buf/sync-file: fix warning about fence containers") Reviewed-by: Thomas Hellström Link: https://patchwork.freedesktop.org/patch/msgid/20220329070001.134180-1-christian.koenig@amd.com --- drivers/dma-buf/sync_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index b8dea4ec123b..514d213261df 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -262,7 +262,7 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a, } if (index == 0) - add_fence(fences, &index, dma_fence_get_stub()); + fences[index++] = dma_fence_get_stub(); if (num_fences > index) { struct dma_fence **tmp; -- cgit From c42ee39c1e78224d3a81bdbe0600abe4581226ed Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 25 Mar 2022 16:38:54 +0100 Subject: dma-buf: handle empty dma_fence_arrays gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bug inside the new sync-file merge code created empty dma_fence_array instances. Warn about that and handle those without crashing. Signed-off-by: Christian König Reviewed-by: Thomas Hellström Link: https://patchwork.freedesktop.org/patch/msgid/20220329070001.134180-2-christian.koenig@amd.com --- drivers/dma-buf/dma-fence-array.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dma-buf/dma-fence-array.c b/drivers/dma-buf/dma-fence-array.c index 52b85d292383..5c8a7084577b 100644 --- a/drivers/dma-buf/dma-fence-array.c +++ b/drivers/dma-buf/dma-fence-array.c @@ -159,6 +159,8 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, struct dma_fence_array *array; size_t size = sizeof(*array); + WARN_ON(!num_fences || !fences); + /* Allocate the callback structures behind the array. */ size += num_fences * sizeof(struct dma_fence_array_cb); array = kzalloc(size, GFP_KERNEL); @@ -231,6 +233,9 @@ struct dma_fence *dma_fence_array_first(struct dma_fence *head) if (!array) return head; + if (!array->num_fences) + return NULL; + return array->fences[0]; } EXPORT_SYMBOL(dma_fence_array_first); -- cgit From 7842cf65b0401814a9df518a86a41641255c84d3 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 15 Feb 2022 17:29:59 +0100 Subject: drm/ttm: de-inline ttm_bo_pin/unpin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those functions are going to become more complex, don't inline them any more. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220321132601.2161-4-christian.koenig@amd.com --- drivers/gpu/drm/ttm/ttm_bo.c | 32 ++++++++++++++++++++++++++++++++ include/drm/ttm/ttm_bo_api.h | 30 ++---------------------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index b119af33e7d7..fe0fa5fb7945 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -633,6 +633,38 @@ int ttm_mem_evict_first(struct ttm_device *bdev, return ret; } +/** + * ttm_bo_pin - Pin the buffer object. + * @bo: The buffer object to pin + * + * Make sure the buffer is not evicted any more during memory pressure. + * @bo must be unpinned again by calling ttm_bo_unpin(). + */ +void ttm_bo_pin(struct ttm_buffer_object *bo) +{ + dma_resv_assert_held(bo->base.resv); + WARN_ON_ONCE(!kref_read(&bo->kref)); + ++bo->pin_count; +} +EXPORT_SYMBOL(ttm_bo_pin); + +/** + * ttm_bo_unpin - Unpin the buffer object. + * @bo: The buffer object to unpin + * + * Allows the buffer object to be evicted again during memory pressure. + */ +void ttm_bo_unpin(struct ttm_buffer_object *bo) +{ + dma_resv_assert_held(bo->base.resv); + WARN_ON_ONCE(!kref_read(&bo->kref)); + if (bo->pin_count) + --bo->pin_count; + else + WARN_ON_ONCE(true); +} +EXPORT_SYMBOL(ttm_bo_unpin); + /* * Add the last move fence to the BO and reserve a new shared slot. We only use * a shared slot to avoid unecessary sync and rely on the subsequent bo move to diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 3da77fc54552..885b7698fd65 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -524,34 +524,8 @@ ssize_t ttm_bo_io(struct ttm_device *bdev, struct file *filp, int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, gfp_t gfp_flags); -/** - * ttm_bo_pin - Pin the buffer object. - * @bo: The buffer object to pin - * - * Make sure the buffer is not evicted any more during memory pressure. - */ -static inline void ttm_bo_pin(struct ttm_buffer_object *bo) -{ - dma_resv_assert_held(bo->base.resv); - WARN_ON_ONCE(!kref_read(&bo->kref)); - ++bo->pin_count; -} - -/** - * ttm_bo_unpin - Unpin the buffer object. - * @bo: The buffer object to unpin - * - * Allows the buffer object to be evicted again during memory pressure. - */ -static inline void ttm_bo_unpin(struct ttm_buffer_object *bo) -{ - dma_resv_assert_held(bo->base.resv); - WARN_ON_ONCE(!kref_read(&bo->kref)); - if (bo->pin_count) - --bo->pin_count; - else - WARN_ON_ONCE(true); -} +void ttm_bo_pin(struct ttm_buffer_object *bo); +void ttm_bo_unpin(struct ttm_buffer_object *bo); int ttm_mem_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man, -- cgit From fee2ede155423b0f7a559050a39750b98fe9db69 Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 24 Jan 2022 11:07:15 +0100 Subject: drm/ttm: rework bulk move handling v5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of providing the bulk move structure for each LRU update set this as property of the BO. This should avoid costly bulk move rebuilds with some games under RADV. v2: some name polishing, add a few more kerneldoc words. v3: add some lockdep v4: fix bugs, handle pin/unpin as well v5: improve kerneldoc Signed-off-by: Christian König Tested-by: Bas Nieuwenhuizen Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20220321132601.2161-5-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 1 - drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 72 ++++---------------------- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 3 -- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 61 ++++++++++++++++++---- drivers/gpu/drm/ttm/ttm_resource.c | 90 ++++++++++++++++++++++++--------- include/drm/ttm/ttm_bo_api.h | 16 ++---- include/drm/ttm/ttm_bo_driver.h | 2 +- include/drm/ttm/ttm_device.h | 9 ---- include/drm/ttm/ttm_resource.h | 11 ++-- 10 files changed, 141 insertions(+), 126 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 5859ed0552a4..57ac118fc266 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1498,7 +1498,6 @@ static struct ttm_device_funcs amdgpu_bo_driver = { .io_mem_reserve = &amdgpu_ttm_io_mem_reserve, .io_mem_pfn = amdgpu_ttm_io_mem_pfn, .access_memory = &amdgpu_ttm_access_memory, - .del_from_lru_notify = &amdgpu_vm_del_from_lru_notify }; /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index f2ce5a0defd9..28f5e8b21a99 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -375,7 +375,7 @@ static void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base, if (bo->tbo.base.resv != vm->root.bo->tbo.base.resv) return; - vm->bulk_moveable = false; + ttm_bo_set_bulk_move(&bo->tbo, &vm->lru_bulk_move); if (bo->tbo.type == ttm_bo_type_kernel && bo->parent) amdgpu_vm_bo_relocated(base); else @@ -637,36 +637,6 @@ void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, list_add(&entry->tv.head, validated); } -/** - * amdgpu_vm_del_from_lru_notify - update bulk_moveable flag - * - * @bo: BO which was removed from the LRU - * - * Make sure the bulk_moveable flag is updated when a BO is removed from the - * LRU. - */ -void amdgpu_vm_del_from_lru_notify(struct ttm_buffer_object *bo) -{ - struct amdgpu_bo *abo; - struct amdgpu_vm_bo_base *bo_base; - - if (!amdgpu_bo_is_amdgpu_bo(bo)) - return; - - if (bo->pin_count) - return; - - abo = ttm_to_amdgpu_bo(bo); - if (!abo->parent) - return; - for (bo_base = abo->vm_bo; bo_base; bo_base = bo_base->next) { - struct amdgpu_vm *vm = bo_base->vm; - - if (abo->tbo.base.resv == vm->root.bo->tbo.base.resv) - vm->bulk_moveable = false; - } - -} /** * amdgpu_vm_move_to_lru_tail - move all BOs to the end of LRU * @@ -679,33 +649,9 @@ void amdgpu_vm_del_from_lru_notify(struct ttm_buffer_object *bo) void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev, struct amdgpu_vm *vm) { - struct amdgpu_vm_bo_base *bo_base; - - if (vm->bulk_moveable) { - spin_lock(&adev->mman.bdev.lru_lock); - ttm_lru_bulk_move_tail(&vm->lru_bulk_move); - spin_unlock(&adev->mman.bdev.lru_lock); - return; - } - - ttm_lru_bulk_move_init(&vm->lru_bulk_move); - spin_lock(&adev->mman.bdev.lru_lock); - list_for_each_entry(bo_base, &vm->idle, vm_status) { - struct amdgpu_bo *bo = bo_base->bo; - struct amdgpu_bo *shadow = amdgpu_bo_shadowed(bo); - - if (!bo->parent) - continue; - - ttm_bo_move_to_lru_tail(&bo->tbo, &vm->lru_bulk_move); - if (shadow) - ttm_bo_move_to_lru_tail(&shadow->tbo, - &vm->lru_bulk_move); - } + ttm_lru_bulk_move_tail(&vm->lru_bulk_move); spin_unlock(&adev->mman.bdev.lru_lock); - - vm->bulk_moveable = true; } /** @@ -728,8 +674,6 @@ int amdgpu_vm_validate_pt_bos(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_vm_bo_base *bo_base, *tmp; int r; - vm->bulk_moveable &= list_empty(&vm->evicted); - list_for_each_entry_safe(bo_base, tmp, &vm->evicted, vm_status) { struct amdgpu_bo *bo = bo_base->bo; struct amdgpu_bo *shadow = amdgpu_bo_shadowed(bo); @@ -1047,10 +991,16 @@ static void amdgpu_vm_free_table(struct amdgpu_vm_bo_base *entry) if (!entry->bo) return; + shadow = amdgpu_bo_shadowed(entry->bo); + if (shadow) { + ttm_bo_set_bulk_move(&shadow->tbo, NULL); + amdgpu_bo_unref(&shadow); + } + + ttm_bo_set_bulk_move(&entry->bo->tbo, NULL); entry->bo->vm_bo = NULL; list_del(&entry->vm_status); - amdgpu_bo_unref(&shadow); amdgpu_bo_unref(&entry->bo); } @@ -1070,8 +1020,6 @@ static void amdgpu_vm_free_pts(struct amdgpu_device *adev, struct amdgpu_vm_pt_cursor cursor; struct amdgpu_vm_bo_base *entry; - vm->bulk_moveable = false; - for_each_amdgpu_vm_pt_dfs_safe(adev, vm, start, cursor, entry) amdgpu_vm_free_table(entry); @@ -2651,7 +2599,7 @@ void amdgpu_vm_bo_rmv(struct amdgpu_device *adev, if (bo) { if (bo->tbo.base.resv == vm->root.bo->tbo.base.resv) - vm->bulk_moveable = false; + ttm_bo_set_bulk_move(&bo->tbo, NULL); for (base = &bo_va->base.bo->vm_bo; *base; base = &(*base)->next) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 85fcfb8c5efd..4d236682a118 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -317,8 +317,6 @@ struct amdgpu_vm { /* Store positions of group of BOs */ struct ttm_lru_bulk_move lru_bulk_move; - /* mark whether can do the bulk move */ - bool bulk_moveable; /* Flag to indicate if VM is used for compute */ bool is_compute_context; }; @@ -454,7 +452,6 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm); void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev, struct amdgpu_vm *vm); -void amdgpu_vm_del_from_lru_notify(struct ttm_buffer_object *bo); void amdgpu_vm_get_memory(struct amdgpu_vm *vm, uint64_t *vram_mem, uint64_t *gtt_mem, uint64_t *cpu_mem); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 582e8dc9bc8c..6fc192082d8c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -849,7 +849,7 @@ void i915_ttm_adjust_lru(struct drm_i915_gem_object *obj) bo->priority = I915_TTM_PRIO_NO_PAGES; } - ttm_bo_move_to_lru_tail(bo, NULL); + ttm_bo_move_to_lru_tail(bo); spin_unlock(&bo->bdev->lru_lock); } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index fe0fa5fb7945..e5fd0f2c0299 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -69,16 +69,55 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, } } -void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo, - struct ttm_lru_bulk_move *bulk) +/** + * ttm_bo_move_to_lru_tail + * + * @bo: The buffer object. + * + * Move this BO to the tail of all lru lists used to lookup and reserve an + * object. This function must be called with struct ttm_global::lru_lock + * held, and is used to make a BO less likely to be considered for eviction. + */ +void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo) { dma_resv_assert_held(bo->base.resv); if (bo->resource) - ttm_resource_move_to_lru_tail(bo->resource, bulk); + ttm_resource_move_to_lru_tail(bo->resource); } EXPORT_SYMBOL(ttm_bo_move_to_lru_tail); +/** + * ttm_bo_set_bulk_move - update BOs bulk move object + * + * @bo: The buffer object. + * + * Update the BOs bulk move object, making sure that resources are added/removed + * as well. A bulk move allows to move many resource on the LRU at once, + * resulting in much less overhead of maintaining the LRU. + * The only requirement is that the resources stay together on the LRU and are + * never separated. This is enforces by setting the bulk_move structure on a BO. + * ttm_lru_bulk_move_tail() should be used to move all resources to the tail of + * their LRU list. + */ +void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo, + struct ttm_lru_bulk_move *bulk) +{ + dma_resv_assert_held(bo->base.resv); + + if (bo->bulk_move == bulk) + return; + + spin_lock(&bo->bdev->lru_lock); + if (bo->bulk_move && bo->resource) + ttm_lru_bulk_move_del(bo->bulk_move, bo->resource); + bo->bulk_move = bulk; + if (bo->bulk_move && bo->resource) + ttm_lru_bulk_move_add(bo->bulk_move, bo->resource); + spin_unlock(&bo->bdev->lru_lock); +} +EXPORT_SYMBOL(ttm_bo_set_bulk_move); + static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_resource *mem, bool evict, struct ttm_operation_ctx *ctx, @@ -316,6 +355,7 @@ static void ttm_bo_release(struct kref *kref) int ret; WARN_ON_ONCE(bo->pin_count); + WARN_ON_ONCE(bo->bulk_move); if (!bo->deleted) { ret = ttm_bo_individualize_resv(bo); @@ -352,7 +392,7 @@ static void ttm_bo_release(struct kref *kref) */ if (bo->pin_count) { bo->pin_count = 0; - ttm_resource_move_to_lru_tail(bo->resource, NULL); + ttm_resource_move_to_lru_tail(bo->resource); } kref_init(&bo->kref); @@ -644,7 +684,8 @@ void ttm_bo_pin(struct ttm_buffer_object *bo) { dma_resv_assert_held(bo->base.resv); WARN_ON_ONCE(!kref_read(&bo->kref)); - ++bo->pin_count; + if (!(bo->pin_count++) && bo->bulk_move && bo->resource) + ttm_lru_bulk_move_del(bo->bulk_move, bo->resource); } EXPORT_SYMBOL(ttm_bo_pin); @@ -658,10 +699,11 @@ void ttm_bo_unpin(struct ttm_buffer_object *bo) { dma_resv_assert_held(bo->base.resv); WARN_ON_ONCE(!kref_read(&bo->kref)); - if (bo->pin_count) - --bo->pin_count; - else - WARN_ON_ONCE(true); + if (WARN_ON_ONCE(!bo->pin_count)) + return; + + if (!(--bo->pin_count) && bo->bulk_move && bo->resource) + ttm_lru_bulk_move_add(bo->bulk_move, bo->resource); } EXPORT_SYMBOL(ttm_bo_unpin); @@ -906,6 +948,7 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, bo->moving = NULL; bo->pin_count = 0; bo->sg = sg; + bo->bulk_move = NULL; if (resv) { bo->base.resv = resv; dma_resv_assert_held(bo->base.resv); diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 19df7350de0f..492ba3157e75 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -73,42 +73,77 @@ void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk) } EXPORT_SYMBOL(ttm_lru_bulk_move_tail); -/* Record a resource position in a bulk move structure */ -static void ttm_lru_bulk_move_set_pos(struct ttm_lru_bulk_move_pos *pos, - struct ttm_resource *res) +/* Return the bulk move pos object for this resource */ +static struct ttm_lru_bulk_move_pos * +ttm_lru_bulk_move_pos(struct ttm_lru_bulk_move *bulk, struct ttm_resource *res) { - if (!pos->first) + return &bulk->pos[res->mem_type][res->bo->priority]; +} + +/* Move the resource to the tail of the bulk move range */ +static void ttm_lru_bulk_move_pos_tail(struct ttm_lru_bulk_move_pos *pos, + struct ttm_resource *res) +{ + if (pos->last != res) { + list_move(&res->lru, &pos->last->lru); + pos->last = res; + } +} + +/* Add the resource to a bulk_move cursor */ +void ttm_lru_bulk_move_add(struct ttm_lru_bulk_move *bulk, + struct ttm_resource *res) +{ + struct ttm_lru_bulk_move_pos *pos = ttm_lru_bulk_move_pos(bulk, res); + + if (!pos->first) { pos->first = res; - pos->last = res; + pos->last = res; + } else { + ttm_lru_bulk_move_pos_tail(pos, res); + } +} + +/* Remove the resource from a bulk_move range */ +void ttm_lru_bulk_move_del(struct ttm_lru_bulk_move *bulk, + struct ttm_resource *res) +{ + struct ttm_lru_bulk_move_pos *pos = ttm_lru_bulk_move_pos(bulk, res); + + if (unlikely(pos->first == res && pos->last == res)) { + pos->first = NULL; + pos->last = NULL; + } else if (pos->first == res) { + pos->first = list_next_entry(res, lru); + } else if (pos->last == res) { + pos->last = list_prev_entry(res, lru); + } else { + list_move(&res->lru, &pos->last->lru); + } } -/* Move a resource to the LRU tail and track the bulk position */ -void ttm_resource_move_to_lru_tail(struct ttm_resource *res, - struct ttm_lru_bulk_move *bulk) +/* Move a resource to the LRU or bulk tail */ +void ttm_resource_move_to_lru_tail(struct ttm_resource *res) { struct ttm_buffer_object *bo = res->bo; struct ttm_device *bdev = bo->bdev; - struct ttm_resource_manager *man; lockdep_assert_held(&bo->bdev->lru_lock); if (bo->pin_count) { list_move_tail(&res->lru, &bdev->pinned); - if (bdev->funcs->del_from_lru_notify) - bdev->funcs->del_from_lru_notify(res->bo); - return; - } - man = ttm_manager_type(bdev, res->mem_type); - list_move_tail(&res->lru, &man->lru[bo->priority]); + } else if (bo->bulk_move) { + struct ttm_lru_bulk_move_pos *pos = + ttm_lru_bulk_move_pos(bo->bulk_move, res); - if (bdev->funcs->del_from_lru_notify) - bdev->funcs->del_from_lru_notify(bo); - - if (!bulk) - return; + ttm_lru_bulk_move_pos_tail(pos, res); + } else { + struct ttm_resource_manager *man; - ttm_lru_bulk_move_set_pos(&bulk->pos[res->mem_type][bo->priority], res); + man = ttm_manager_type(bdev, res->mem_type); + list_move_tail(&res->lru, &man->lru[bo->priority]); + } } /** @@ -139,7 +174,10 @@ void ttm_resource_init(struct ttm_buffer_object *bo, man = ttm_manager_type(bo->bdev, place->mem_type); spin_lock(&bo->bdev->lru_lock); man->usage += res->num_pages << PAGE_SHIFT; - ttm_resource_move_to_lru_tail(res, NULL); + if (bo->bulk_move) + ttm_lru_bulk_move_add(bo->bulk_move, res); + else + ttm_resource_move_to_lru_tail(res); spin_unlock(&bo->bdev->lru_lock); } EXPORT_SYMBOL(ttm_resource_init); @@ -161,8 +199,6 @@ void ttm_resource_fini(struct ttm_resource_manager *man, spin_lock(&bdev->lru_lock); list_del_init(&res->lru); - if (res->bo && bdev->funcs->del_from_lru_notify) - bdev->funcs->del_from_lru_notify(res->bo); man->usage -= res->num_pages << PAGE_SHIFT; spin_unlock(&bdev->lru_lock); } @@ -185,6 +221,12 @@ void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res) if (!*res) return; + if (bo->bulk_move) { + spin_lock(&bo->bdev->lru_lock); + ttm_lru_bulk_move_del(bo->bulk_move, *res); + spin_unlock(&bo->bdev->lru_lock); + } + man = ttm_manager_type(bo->bdev, (*res)->mem_type); man->func->free(man, *res); *res = NULL; diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 885b7698fd65..c61e1e5ceb83 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -135,6 +135,7 @@ struct ttm_buffer_object { struct ttm_resource *resource; struct ttm_tt *ttm; bool deleted; + struct ttm_lru_bulk_move *bulk_move; /** * Members protected by the bdev::lru_lock. @@ -287,18 +288,9 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, */ void ttm_bo_put(struct ttm_buffer_object *bo); -/** - * ttm_bo_move_to_lru_tail - * - * @bo: The buffer object. - * @bulk: optional bulk move structure to remember BO positions - * - * Move this BO to the tail of all lru lists used to lookup and reserve an - * object. This function must be called with struct ttm_global::lru_lock - * held, and is used to make a BO less likely to be considered for eviction. - */ -void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo, - struct ttm_lru_bulk_move *bulk); +void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo); +void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo, + struct ttm_lru_bulk_move *bulk); /** * ttm_bo_lock_delayed_workqueue diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 6c7352e13708..059a595e14e5 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -155,7 +155,7 @@ static inline void ttm_bo_move_to_lru_tail_unlocked(struct ttm_buffer_object *bo) { spin_lock(&bo->bdev->lru_lock); - ttm_bo_move_to_lru_tail(bo, NULL); + ttm_bo_move_to_lru_tail(bo); spin_unlock(&bo->bdev->lru_lock); } diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h index 425150f35fbe..95b3c04b1ab9 100644 --- a/include/drm/ttm/ttm_device.h +++ b/include/drm/ttm/ttm_device.h @@ -198,15 +198,6 @@ struct ttm_device_funcs { int (*access_memory)(struct ttm_buffer_object *bo, unsigned long offset, void *buf, int len, int write); - /** - * struct ttm_bo_driver member del_from_lru_notify - * - * @bo: the buffer object deleted from lru - * - * notify driver that a BO was deleted from LRU. - */ - void (*del_from_lru_notify)(struct ttm_buffer_object *bo); - /** * Notify the driver that we're about to release a BO * diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 62e7bb782596..4ca960b5cde1 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -206,7 +206,7 @@ struct ttm_resource_cursor { * @first: first res in the bulk move range * @last: last res in the bulk move range * - * Positions for a lru bulk move. + * Range of resources for a lru bulk move. */ struct ttm_lru_bulk_move_pos { struct ttm_resource *first; @@ -220,7 +220,7 @@ struct ttm_lru_bulk_move_pos { * @vram: first/last lru entry for resources in the VRAM domain * * Container for the current bulk move state. Should be used with - * ttm_lru_bulk_move_init() and ttm_bo_move_to_lru_tail(). + * ttm_lru_bulk_move_init() and ttm_bo_set_bulk_move(). */ struct ttm_lru_bulk_move { struct ttm_lru_bulk_move_pos pos[TTM_NUM_MEM_TYPES][TTM_MAX_BO_PRIORITY]; @@ -313,10 +313,13 @@ ttm_resource_manager_cleanup(struct ttm_resource_manager *man) } void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk); +void ttm_lru_bulk_move_add(struct ttm_lru_bulk_move *bulk, + struct ttm_resource *res); +void ttm_lru_bulk_move_del(struct ttm_lru_bulk_move *bulk, + struct ttm_resource *res); void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk); -void ttm_resource_move_to_lru_tail(struct ttm_resource *res, - struct ttm_lru_bulk_move *bulk); +void ttm_resource_move_to_lru_tail(struct ttm_resource *res); void ttm_resource_init(struct ttm_buffer_object *bo, const struct ttm_place *place, -- cgit From 77ef271faee9c3be65fc5e5dcd66c45d4ffbe494 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 16 Jul 2021 14:37:14 +0200 Subject: drm/amdgpu: drop amdgpu_gtt_node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have the BO pointer in the base structure now as well. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Tested-by: Bas Nieuwenhuizen Link: https://patchwork.freedesktop.org/patch/msgid/20220321132601.2161-6-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 49 +++++++++++------------------ include/drm/ttm/ttm_resource.h | 8 +++++ 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index 3bcd27ae379d..68494b959116 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -26,23 +26,12 @@ #include "amdgpu.h" -struct amdgpu_gtt_node { - struct ttm_buffer_object *tbo; - struct ttm_range_mgr_node base; -}; - static inline struct amdgpu_gtt_mgr * to_gtt_mgr(struct ttm_resource_manager *man) { return container_of(man, struct amdgpu_gtt_mgr, manager); } -static inline struct amdgpu_gtt_node * -to_amdgpu_gtt_node(struct ttm_resource *res) -{ - return container_of(res, struct amdgpu_gtt_node, base.base); -} - /** * DOC: mem_info_gtt_total * @@ -106,9 +95,9 @@ const struct attribute_group amdgpu_gtt_mgr_attr_group = { */ bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *res) { - struct amdgpu_gtt_node *node = to_amdgpu_gtt_node(res); + struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res); - return drm_mm_node_allocated(&node->base.mm_nodes[0]); + return drm_mm_node_allocated(&node->mm_nodes[0]); } /** @@ -128,15 +117,14 @@ static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man, { struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man); uint32_t num_pages = PFN_UP(tbo->base.size); - struct amdgpu_gtt_node *node; + struct ttm_range_mgr_node *node; int r; - node = kzalloc(struct_size(node, base.mm_nodes, 1), GFP_KERNEL); + node = kzalloc(struct_size(node, mm_nodes, 1), GFP_KERNEL); if (!node) return -ENOMEM; - node->tbo = tbo; - ttm_resource_init(tbo, place, &node->base.base); + ttm_resource_init(tbo, place, &node->base); if (!(place->flags & TTM_PL_FLAG_TEMPORARY) && ttm_resource_manager_usage(man) > man->size) { r = -ENOSPC; @@ -145,8 +133,7 @@ static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man, if (place->lpfn) { spin_lock(&mgr->lock); - r = drm_mm_insert_node_in_range(&mgr->mm, - &node->base.mm_nodes[0], + r = drm_mm_insert_node_in_range(&mgr->mm, &node->mm_nodes[0], num_pages, tbo->page_alignment, 0, place->fpfn, place->lpfn, DRM_MM_INSERT_BEST); @@ -154,18 +141,18 @@ static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man, if (unlikely(r)) goto err_free; - node->base.base.start = node->base.mm_nodes[0].start; + node->base.start = node->mm_nodes[0].start; } else { - node->base.mm_nodes[0].start = 0; - node->base.mm_nodes[0].size = node->base.base.num_pages; - node->base.base.start = AMDGPU_BO_INVALID_OFFSET; + node->mm_nodes[0].start = 0; + node->mm_nodes[0].size = node->base.num_pages; + node->base.start = AMDGPU_BO_INVALID_OFFSET; } - *res = &node->base.base; + *res = &node->base; return 0; err_free: - ttm_resource_fini(man, &node->base.base); + ttm_resource_fini(man, &node->base); kfree(node); return r; } @@ -181,12 +168,12 @@ err_free: static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man, struct ttm_resource *res) { - struct amdgpu_gtt_node *node = to_amdgpu_gtt_node(res); + struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res); struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man); spin_lock(&mgr->lock); - if (drm_mm_node_allocated(&node->base.mm_nodes[0])) - drm_mm_remove_node(&node->base.mm_nodes[0]); + if (drm_mm_node_allocated(&node->mm_nodes[0])) + drm_mm_remove_node(&node->mm_nodes[0]); spin_unlock(&mgr->lock); ttm_resource_fini(man, res); @@ -202,7 +189,7 @@ static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man, */ int amdgpu_gtt_mgr_recover(struct amdgpu_gtt_mgr *mgr) { - struct amdgpu_gtt_node *node; + struct ttm_range_mgr_node *node; struct drm_mm_node *mm_node; struct amdgpu_device *adev; int r = 0; @@ -210,8 +197,8 @@ int amdgpu_gtt_mgr_recover(struct amdgpu_gtt_mgr *mgr) adev = container_of(mgr, typeof(*adev), mman.gtt_mgr); spin_lock(&mgr->lock); drm_mm_for_each_node(mm_node, &mgr->mm) { - node = container_of(mm_node, typeof(*node), base.mm_nodes[0]); - r = amdgpu_ttm_recover_gart(node->tbo); + node = container_of(mm_node, typeof(*node), mm_nodes[0]); + r = amdgpu_ttm_recover_gart(node->base.bo); if (r) break; } diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 4ca960b5cde1..4428a62e5f0e 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -355,6 +355,14 @@ ttm_resource_manager_next(struct ttm_resource_manager *man, struct ttm_resource_cursor *cursor, struct ttm_resource *res); +/** + * ttm_resource_manager_for_each_res - iterate over all resources + * @man: the resource manager + * @cursor: struct ttm_resource_cursor for the current position + * @res: the current resource + * + * Iterate over all the evictable resources in a resource manager. + */ #define ttm_resource_manager_for_each_res(man, cursor, res) \ for (res = ttm_resource_manager_first(man, cursor); res; \ res = ttm_resource_manager_next(man, cursor, res)) -- cgit From bdef417d84536715145f6dc9cc3275c46f26295a Mon Sep 17 00:00:00 2001 From: Xiaomeng Tong Date: Sun, 27 Mar 2022 13:20:28 +0800 Subject: gma500: fix an incorrect NULL check on list iterator The bug is here: return crtc; The list iterator value 'crtc' will *always* be set and non-NULL by list_for_each_entry(), so it is incorrect to assume that the iterator value will be NULL if the list is empty or no element is found. To fix the bug, return 'crtc' when found, otherwise return NULL. Cc: stable@vger.kernel.org fixes: 89c78134cc54d ("gma500: Add Poulsbo support") Signed-off-by: Xiaomeng Tong Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20220327052028.2013-1-xiam0nd.tong@gmail.com --- drivers/gpu/drm/gma500/psb_intel_display.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index fb8234f4d128..dc467996cce4 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -524,14 +524,15 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe, struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) { - struct drm_crtc *crtc = NULL; + struct drm_crtc *crtc; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + if (gma_crtc->pipe == pipe) - break; + return crtc; } - return crtc; + return NULL; } int gma_connector_clones(struct drm_device *dev, int type_mask) -- cgit From 8b917cbe38e9b0d002492477a9fc2bfee2412ce4 Mon Sep 17 00:00:00 2001 From: Xiaomeng Tong Date: Sun, 27 Mar 2022 14:15:16 +0800 Subject: tilcdc: tilcdc_external: fix an incorrect NULL check on list iterator The bug is here: if (!encoder) { The list iterator value 'encoder' will *always* be set and non-NULL by list_for_each_entry(), so it is incorrect to assume that the iterator value will be NULL if the list is empty or no element is found. To fix the bug, use a new variable 'iter' as the list iterator, while use the original variable 'encoder' as a dedicated pointer to point to the found element. Cc: stable@vger.kernel.org Fixes: ec9eab097a500 ("drm/tilcdc: Add drm bridge support for attaching drm bridge drivers") Signed-off-by: Xiaomeng Tong Reviewed-by: Jyri Sarha Tested-by: Jyri Sarha Signed-off-by: Jyri Sarha Link: https://patchwork.freedesktop.org/patch/msgid/20220327061516.5076-1-xiam0nd.tong@gmail.com --- drivers/gpu/drm/tilcdc/tilcdc_external.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index 7594cf6e186e..3b86d002ef62 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -60,11 +60,13 @@ struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev, int tilcdc_add_component_encoder(struct drm_device *ddev) { struct tilcdc_drm_private *priv = ddev->dev_private; - struct drm_encoder *encoder; + struct drm_encoder *encoder = NULL, *iter; - list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head) - if (encoder->possible_crtcs & (1 << priv->crtc->index)) + list_for_each_entry(iter, &ddev->mode_config.encoder_list, head) + if (iter->possible_crtcs & (1 << priv->crtc->index)) { + encoder = iter; break; + } if (!encoder) { dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__); -- cgit From faacff8e6a5209b0b6a57fa58ba292742ab442d2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:22 +0300 Subject: drm/edid: don't modify EDID while parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll want to keep the EDID immutable while parsing. Stop modifying the EDID because of the quirks. In theory, this does have userspace implications, but the userspace is supposed to use the modes exposed via KMS API, not by parsing the EDID directly. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/45d5cf067eaad49b321ac82836090d9de524374e.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a7663f9a11d2..0e434ab7908c 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2740,9 +2740,9 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, return NULL; if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) - timing->pixel_clock = cpu_to_le16(1088); - - mode->clock = le16_to_cpu(timing->pixel_clock) * 10; + mode->clock = 1088 * 10; + else + mode->clock = le16_to_cpu(timing->pixel_clock) * 10; mode->hdisplay = hactive; mode->hsync_start = mode->hdisplay + hsync_offset; @@ -2763,14 +2763,14 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, drm_mode_do_interlace_quirk(mode, pt); if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { - pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; + mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; + } else { + mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? + DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? + DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; } - mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? - DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; - mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? - DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; - set_size: mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; -- cgit From 73091e4fb78b5d88913593012ad74a1c0ff266da Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:23 +0300 Subject: drm/edid: fix reduced blanking support check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reduced blanking bit is valid only for CVT, indicated by display range limits flags 0x04. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/5dea5ee24065450716bbc177dd6850d3193dbeec.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 0e434ab7908c..aa4ba35b40cf 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2408,7 +2408,7 @@ is_rb(struct detailed_timing *t, void *data) if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE)) return; - if (r[15] & 0x10) + if (r[10] == DRM_EDID_CVT_SUPPORT_FLAG && r[15] & 0x10) *(bool *)data = true; } -- cgit From e379814b5fb719b22c01c7a81f14b98cbaac3c42 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:24 +0300 Subject: drm/edid: pass a timing pointer to is_display_descriptor() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use struct member access instead of direct offsets to avoid lots of casts all over the place. Use BUILD_BUG_ON() for sanity check. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/ccc54b45ea628874c0290dd64114da6cefff1819.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index aa4ba35b40cf..f6b9b818aaf9 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2331,10 +2331,15 @@ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_find_dmt); -static bool is_display_descriptor(const u8 d[18], u8 tag) +static bool is_display_descriptor(const struct detailed_timing *descriptor, u8 type) { - return d[0] == 0x00 && d[1] == 0x00 && - d[2] == 0x00 && d[3] == tag; + BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0); + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.pad1) != 2); + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.type) != 3); + + return descriptor->pixel_clock == 0 && + descriptor->data.other_data.pad1 == 0 && + descriptor->data.other_data.type == type; } static bool is_detailed_timing_descriptor(const u8 d[18]) @@ -2405,7 +2410,7 @@ is_rb(struct detailed_timing *t, void *data) { u8 *r = (u8 *)t; - if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(t, EDID_DETAIL_MONITOR_RANGE)) return; if (r[10] == DRM_EDID_CVT_SUPPORT_FLAG && r[15] & 0x10) @@ -2431,7 +2436,7 @@ find_gtf2(struct detailed_timing *t, void *data) { u8 *r = (u8 *)t; - if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(t, EDID_DETAIL_MONITOR_RANGE)) return; if (r[10] == 0x02) @@ -2987,7 +2992,7 @@ do_inferred_modes(struct detailed_timing *timing, void *c) struct detailed_non_pixel *data = &timing->data.other_data; struct detailed_data_monitor_range *range = &data->data.range; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE)) return; closure->modes += drm_dmt_modes_for_range(closure->connector, @@ -3067,7 +3072,7 @@ do_established_modes(struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_EST_TIMINGS)) + if (!is_display_descriptor(timing, EDID_DETAIL_EST_TIMINGS)) return; closure->modes += drm_est3_modes(closure->connector, timing); @@ -3122,7 +3127,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) struct edid *edid = closure->edid; int i; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_STD_MODES)) + if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES)) return; for (i = 0; i < 6; i++) { @@ -3231,7 +3236,7 @@ do_cvt_mode(struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_CVT_3BYTE)) + if (!is_display_descriptor(timing, EDID_DETAIL_CVT_3BYTE)) return; closure->modes += drm_cvt_modes(closure->connector, timing); @@ -4491,7 +4496,7 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) static void monitor_name(struct detailed_timing *t, void *data) { - if (!is_display_descriptor((const u8 *)t, EDID_DETAIL_MONITOR_NAME)) + if (!is_display_descriptor(t, EDID_DETAIL_MONITOR_NAME)) return; *(u8 **)data = t->data.other_data.data.str.str; @@ -5221,7 +5226,7 @@ void get_monitor_range(struct detailed_timing *timing, const struct detailed_non_pixel *data = &timing->data.other_data; const struct detailed_data_monitor_range *range = &data->data.range; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE)) return; /* -- cgit From a9b1f15f53313f967edd5088a60970cc829ab5a6 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:25 +0300 Subject: drm/edid: pass a timing pointer to is_detailed_timing_descriptor() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use struct member access instead of direct offsets to avoid a cast. Use BUILD_BUG_ON() for sanity check. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/0b5213383e14f11c6a505b10a7342fb2ff4f2a11.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index f6b9b818aaf9..1ebf434b2f0b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2342,9 +2342,11 @@ static bool is_display_descriptor(const struct detailed_timing *descriptor, u8 t descriptor->data.other_data.type == type; } -static bool is_detailed_timing_descriptor(const u8 d[18]) +static bool is_detailed_timing_descriptor(const struct detailed_timing *descriptor) { - return d[0] != 0x00 || d[1] != 0x00; + BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0); + + return descriptor->pixel_clock != 0; } typedef void detailed_cb(struct detailed_timing *timing, void *closure); @@ -3266,7 +3268,7 @@ do_detailed_mode(struct detailed_timing *timing, void *c) struct detailed_mode_closure *closure = c; struct drm_display_mode *newmode; - if (!is_detailed_timing_descriptor((const u8 *)timing)) + if (!is_detailed_timing_descriptor(timing)) return; newmode = drm_mode_detailed(closure->connector->dev, -- cgit From 90fd588f03a6392533c9e46c16dd4e50a12184ae Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:26 +0300 Subject: drm/edid: use struct detailed_timing member access in is_rb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use struct detailed_timing member access instead of direct offsets to avoid casting. Use BUILD_BUG_ON() for sanity check. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/c069669c2fe8f9c3061c7d1a413c75a33ec48813.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1ebf434b2f0b..d842d5fc0b81 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2408,15 +2408,19 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) } static void -is_rb(struct detailed_timing *t, void *data) +is_rb(struct detailed_timing *descriptor, void *data) { - u8 *r = (u8 *)t; + bool *res = data; - if (!is_display_descriptor(t, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE)) return; - if (r[10] == DRM_EDID_CVT_SUPPORT_FLAG && r[15] & 0x10) - *(bool *)data = true; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10); + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.cvt.flags) != 15); + + if (descriptor->data.other_data.data.range.flags == DRM_EDID_CVT_SUPPORT_FLAG && + descriptor->data.other_data.data.range.formula.cvt.flags & 0x10) + *res = true; } /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ -- cgit From c8a4beba19a56cb45cfc8b68f146eb63a25f7841 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:27 +0300 Subject: drm/edid: use struct detailed_timing member access in gtf2 functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use struct detailed_timing member access instead of direct offsets to avoid casting. Use BUILD_BUG_ON() for sanity check. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/9fe5f5c39039e585fecfffb390297d49262e5fd3.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 57 ++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d842d5fc0b81..d188ac8ce2e2 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2438,61 +2438,78 @@ drm_monitor_supports_rb(struct edid *edid) } static void -find_gtf2(struct detailed_timing *t, void *data) +find_gtf2(struct detailed_timing *descriptor, void *data) { - u8 *r = (u8 *)t; + struct detailed_timing **res = data; - if (!is_display_descriptor(t, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE)) return; - if (r[10] == 0x02) - *(u8 **)data = r; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10); + + if (descriptor->data.other_data.data.range.flags == 0x02) + *res = descriptor; } /* Secondary GTF curve kicks in above some break frequency */ static int drm_gtf2_hbreak(struct edid *edid) { - u8 *r = NULL; + struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? (r[12] * 2) : 0; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12); + + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.hfreq_start_khz * 2 : 0; } static int drm_gtf2_2c(struct edid *edid) { - u8 *r = NULL; + struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[13] : 0; + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.c : 0; } static int drm_gtf2_m(struct edid *edid) { - u8 *r = NULL; + struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? (r[15] << 8) + r[14] : 0; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14); + + return descriptor ? le16_to_cpu(descriptor->data.other_data.data.range.formula.gtf2.m) : 0; } static int drm_gtf2_k(struct edid *edid) { - u8 *r = NULL; + struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[16] : 0; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16); + + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.k : 0; } static int drm_gtf2_2j(struct edid *edid) { - u8 *r = NULL; + struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[17] : 0; + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0; } /** -- cgit From fcfb2ea19868f18065ba49add81cc10533d237da Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:28 +0300 Subject: drm/edid: constify struct detailed_timing in lower level parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start constifying the struct detailed_timing pointers being passed around from bottom up. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/0b7fafcc7784db0003e454544916c273a9eb1250.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d188ac8ce2e2..0d34d9906324 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2560,7 +2560,7 @@ static int drm_mode_hsync(const struct drm_display_mode *mode) */ static struct drm_display_mode * drm_mode_std(struct drm_connector *connector, struct edid *edid, - struct std_timing *t) + const struct std_timing *t) { struct drm_device *dev = connector->dev; struct drm_display_mode *m, *mode = NULL; @@ -2678,7 +2678,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid, */ static void drm_mode_do_interlace_quirk(struct drm_display_mode *mode, - struct detailed_pixel_timing *pt) + const struct detailed_pixel_timing *pt) { int i; static const struct { @@ -2722,11 +2722,11 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, */ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, struct edid *edid, - struct detailed_timing *timing, + const struct detailed_timing *timing, u32 quirks) { struct drm_display_mode *mode; - struct detailed_pixel_timing *pt = &timing->data.pixel_data; + const struct detailed_pixel_timing *pt = &timing->data.pixel_data; unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; @@ -2821,7 +2821,7 @@ set_size: static bool mode_in_hsync_range(const struct drm_display_mode *mode, - struct edid *edid, u8 *t) + struct edid *edid, const u8 *t) { int hsync, hmin, hmax; @@ -2838,7 +2838,7 @@ mode_in_hsync_range(const struct drm_display_mode *mode, static bool mode_in_vsync_range(const struct drm_display_mode *mode, - struct edid *edid, u8 *t) + struct edid *edid, const u8 *t) { int vsync, vmin, vmax; @@ -2854,7 +2854,7 @@ mode_in_vsync_range(const struct drm_display_mode *mode, } static u32 -range_pixel_clock(struct edid *edid, u8 *t) +range_pixel_clock(struct edid *edid, const u8 *t) { /* unspecified */ if (t[9] == 0 || t[9] == 255) @@ -2870,10 +2870,10 @@ range_pixel_clock(struct edid *edid, u8 *t) static bool mode_in_range(const struct drm_display_mode *mode, struct edid *edid, - struct detailed_timing *timing) + const struct detailed_timing *timing) { u32 max_clock; - u8 *t = (u8 *)timing; + const u8 *t = (const u8 *)timing; if (!mode_in_hsync_range(mode, edid, t)) return false; @@ -2916,7 +2916,7 @@ static bool valid_inferred_mode(const struct drm_connector *connector, static int drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -2951,7 +2951,7 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode) static int drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -2980,7 +2980,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, static int drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -3012,8 +3012,8 @@ static void do_inferred_modes(struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; - struct detailed_data_monitor_range *range = &data->data.range; + const struct detailed_non_pixel *data = &timing->data.other_data; + const struct detailed_data_monitor_range *range = &data->data.range; if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE)) return; @@ -3062,11 +3062,11 @@ add_inferred_modes(struct drm_connector *connector, struct edid *edid) } static int -drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) +drm_est3_modes(struct drm_connector *connector, const struct detailed_timing *timing) { int i, j, m, modes = 0; struct drm_display_mode *mode; - u8 *est = ((u8 *)timing) + 6; + const u8 *est = ((const u8 *)timing) + 6; for (i = 0; i < 6; i++) { for (j = 7; j >= 0; j--) { @@ -3145,7 +3145,7 @@ static void do_standard_modes(struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; + const struct detailed_non_pixel *data = &timing->data.other_data; struct drm_connector *connector = closure->connector; struct edid *edid = closure->edid; int i; @@ -3154,7 +3154,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) return; for (i = 0; i < 6; i++) { - struct std_timing *std = &data->data.timings[i]; + const struct std_timing *std = &data->data.timings[i]; struct drm_display_mode *newmode; newmode = drm_mode_std(connector, edid, std); @@ -3203,12 +3203,12 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid) } static int drm_cvt_modes(struct drm_connector *connector, - struct detailed_timing *timing) + const struct detailed_timing *timing) { int i, j, modes = 0; struct drm_display_mode *newmode; struct drm_device *dev = connector->dev; - struct cvt_timing *cvt; + const struct cvt_timing *cvt; const int rates[] = { 60, 85, 75, 60, 50 }; const u8 empty[3] = { 0, 0, 0 }; -- cgit From 4194442daf053754cc6b726b3d624223dd5458d8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:29 +0300 Subject: drm/edid: constify struct detailed_timing in parsing callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving one level higher, constify struct detailed_timing pointers in callbacks. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/9b617068d2349a574a837ad6207b1d45c4d79eb5.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 0d34d9906324..e90dc6ed3498 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2349,7 +2349,7 @@ static bool is_detailed_timing_descriptor(const struct detailed_timing *descript return descriptor->pixel_clock != 0; } -typedef void detailed_cb(struct detailed_timing *timing, void *closure); +typedef void detailed_cb(const struct detailed_timing *timing, void *closure); static void cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) @@ -2408,7 +2408,7 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) } static void -is_rb(struct detailed_timing *descriptor, void *data) +is_rb(const struct detailed_timing *descriptor, void *data) { bool *res = data; @@ -2438,9 +2438,9 @@ drm_monitor_supports_rb(struct edid *edid) } static void -find_gtf2(struct detailed_timing *descriptor, void *data) +find_gtf2(const struct detailed_timing *descriptor, void *data) { - struct detailed_timing **res = data; + const struct detailed_timing **res = data; if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE)) return; @@ -2455,7 +2455,7 @@ find_gtf2(struct detailed_timing *descriptor, void *data) static int drm_gtf2_hbreak(struct edid *edid) { - struct detailed_timing *descriptor = NULL; + const struct detailed_timing *descriptor = NULL; drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); @@ -2467,7 +2467,7 @@ drm_gtf2_hbreak(struct edid *edid) static int drm_gtf2_2c(struct edid *edid) { - struct detailed_timing *descriptor = NULL; + const struct detailed_timing *descriptor = NULL; drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); @@ -2479,7 +2479,7 @@ drm_gtf2_2c(struct edid *edid) static int drm_gtf2_m(struct edid *edid) { - struct detailed_timing *descriptor = NULL; + const struct detailed_timing *descriptor = NULL; drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); @@ -2491,7 +2491,7 @@ drm_gtf2_m(struct edid *edid) static int drm_gtf2_k(struct edid *edid) { - struct detailed_timing *descriptor = NULL; + const struct detailed_timing *descriptor = NULL; drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); @@ -2503,7 +2503,7 @@ drm_gtf2_k(struct edid *edid) static int drm_gtf2_2j(struct edid *edid) { - struct detailed_timing *descriptor = NULL; + const struct detailed_timing *descriptor = NULL; drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); @@ -3009,7 +3009,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, } static void -do_inferred_modes(struct detailed_timing *timing, void *c) +do_inferred_modes(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; const struct detailed_non_pixel *data = &timing->data.other_data; @@ -3091,7 +3091,7 @@ drm_est3_modes(struct drm_connector *connector, const struct detailed_timing *ti } static void -do_established_modes(struct detailed_timing *timing, void *c) +do_established_modes(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; @@ -3142,7 +3142,7 @@ add_established_modes(struct drm_connector *connector, struct edid *edid) } static void -do_standard_modes(struct detailed_timing *timing, void *c) +do_standard_modes(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; const struct detailed_non_pixel *data = &timing->data.other_data; @@ -3255,7 +3255,7 @@ static int drm_cvt_modes(struct drm_connector *connector, } static void -do_cvt_mode(struct detailed_timing *timing, void *c) +do_cvt_mode(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; @@ -3284,7 +3284,7 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid) static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode); static void -do_detailed_mode(struct detailed_timing *timing, void *c) +do_detailed_mode(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; struct drm_display_mode *newmode; @@ -4517,17 +4517,19 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) } static void -monitor_name(struct detailed_timing *t, void *data) +monitor_name(const struct detailed_timing *timing, void *data) { - if (!is_display_descriptor(t, EDID_DETAIL_MONITOR_NAME)) + const char **res = data; + + if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_NAME)) return; - *(u8 **)data = t->data.other_data.data.str.str; + *res = timing->data.other_data.data.str.str; } static int get_monitor_name(struct edid *edid, char name[13]) { - char *edid_name = NULL; + const char *edid_name = NULL; int mnl; if (!edid || !name) @@ -5242,7 +5244,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector, } static -void get_monitor_range(struct detailed_timing *timing, +void get_monitor_range(const struct detailed_timing *timing, void *info_monitor_range) { struct drm_monitor_range_info *monitor_range = info_monitor_range; -- cgit From eed628f116a7004f9ef751537d603f8b3d785298 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:30 +0300 Subject: drm/edid: constify struct edid passed to detailed blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constify the first level of struct edid in detailed timing parsing. Also switch to struct edid instead of u8. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/ec7acb8a9ee0e2868bbb2afdfdd7db114ae180e1.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 48 ++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e90dc6ed3498..f04e3d241c8d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2352,38 +2352,37 @@ static bool is_detailed_timing_descriptor(const struct detailed_timing *descript typedef void detailed_cb(const struct detailed_timing *timing, void *closure); static void -cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) +cea_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure) { int i, n; u8 d = ext[0x02]; - u8 *det_base = ext + d; + const u8 *det_base = ext + d; if (d < 4 || d > 127) return; n = (127 - d) / 18; for (i = 0; i < n; i++) - cb((struct detailed_timing *)(det_base + 18 * i), closure); + cb((const struct detailed_timing *)(det_base + 18 * i), closure); } static void -vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) +vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure) { unsigned int i, n = min((int)ext[0x02], 6); - u8 *det_base = ext + 5; + const u8 *det_base = ext + 5; if (ext[0x01] != 1) return; /* unknown version */ for (i = 0; i < n; i++) - cb((struct detailed_timing *)(det_base + 18 * i), closure); + cb((const struct detailed_timing *)(det_base + 18 * i), closure); } static void -drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) +drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *closure) { int i; - struct edid *edid = (struct edid *)raw_edid; if (edid == NULL) return; @@ -2391,8 +2390,8 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) for (i = 0; i < EDID_DETAILED_TIMINGS; i++) cb(&(edid->detailed_timings[i]), closure); - for (i = 1; i <= raw_edid[0x7e]; i++) { - u8 *ext = raw_edid + (i * EDID_LENGTH); + for (i = 1; i <= edid->extensions; i++) { + const u8 *ext = (const u8 *)edid + (i * EDID_LENGTH); switch (*ext) { case CEA_EXT: @@ -2430,7 +2429,7 @@ drm_monitor_supports_rb(struct edid *edid) if (edid->revision >= 4) { bool ret = false; - drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); + drm_for_each_detailed_block(edid, is_rb, &ret); return ret; } @@ -2457,7 +2456,7 @@ drm_gtf2_hbreak(struct edid *edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12); @@ -2469,7 +2468,7 @@ drm_gtf2_2c(struct edid *edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13); @@ -2481,7 +2480,7 @@ drm_gtf2_m(struct edid *edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14); @@ -2493,7 +2492,7 @@ drm_gtf2_k(struct edid *edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16); @@ -2505,7 +2504,7 @@ drm_gtf2_2j(struct edid *edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17); @@ -3055,8 +3054,7 @@ add_inferred_modes(struct drm_connector *connector, struct edid *edid) }; if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, - &closure); + drm_for_each_detailed_block(edid, do_inferred_modes, &closure); return closure.modes; } @@ -3135,8 +3133,8 @@ add_established_modes(struct drm_connector *connector, struct edid *edid) } if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, - do_established_modes, &closure); + drm_for_each_detailed_block(edid, do_established_modes, + &closure); return modes + closure.modes; } @@ -3194,7 +3192,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid) } if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_standard_modes, + drm_for_each_detailed_block(edid, do_standard_modes, &closure); /* XXX should also look for standard codes in VTB blocks */ @@ -3274,7 +3272,7 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid) }; if (version_greater(edid, 1, 2)) - drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); + drm_for_each_detailed_block(edid, do_cvt_mode, &closure); /* XXX should also look for CVT codes in VTB blocks */ @@ -3334,7 +3332,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, closure.preferred = (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); - drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); + drm_for_each_detailed_block(edid, do_detailed_mode, &closure); return closure.modes; } @@ -4535,7 +4533,7 @@ static int get_monitor_name(struct edid *edid, char name[13]) if (!edid || !name) return 0; - drm_for_each_detailed_block((u8 *)edid, monitor_name, &edid_name); + drm_for_each_detailed_block(edid, monitor_name, &edid_name); for (mnl = 0; edid_name && mnl < 13; mnl++) { if (edid_name[mnl] == 0x0a) break; @@ -5276,7 +5274,7 @@ void drm_get_monitor_range(struct drm_connector *connector, if (!version_greater(edid, 1, 1)) return; - drm_for_each_detailed_block((u8 *)edid, get_monitor_range, + drm_for_each_detailed_block(edid, get_monitor_range, &info->monitor_range); DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n", -- cgit From c14e724114fe0e90685297e6c10ebdb14d40a7ec Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:31 +0300 Subject: drm/edid: constify struct edid passed around in callbacks and closure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finalize detailed timing parsing constness by making struct edid also const in callbacks and closure. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/8229be49b5a7686856c17428aa9291b4c4cf1bd5.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index f04e3d241c8d..fee47ac1bdd8 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -97,7 +97,7 @@ static int oui(u8 first, u8 second, u8 third) struct detailed_mode_closure { struct drm_connector *connector; - struct edid *edid; + const struct edid *edid; bool preferred; u32 quirks; int modes; @@ -2424,7 +2424,7 @@ is_rb(const struct detailed_timing *descriptor, void *data) /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ static bool -drm_monitor_supports_rb(struct edid *edid) +drm_monitor_supports_rb(const struct edid *edid) { if (edid->revision >= 4) { bool ret = false; @@ -2452,7 +2452,7 @@ find_gtf2(const struct detailed_timing *descriptor, void *data) /* Secondary GTF curve kicks in above some break frequency */ static int -drm_gtf2_hbreak(struct edid *edid) +drm_gtf2_hbreak(const struct edid *edid) { const struct detailed_timing *descriptor = NULL; @@ -2464,7 +2464,7 @@ drm_gtf2_hbreak(struct edid *edid) } static int -drm_gtf2_2c(struct edid *edid) +drm_gtf2_2c(const struct edid *edid) { const struct detailed_timing *descriptor = NULL; @@ -2476,7 +2476,7 @@ drm_gtf2_2c(struct edid *edid) } static int -drm_gtf2_m(struct edid *edid) +drm_gtf2_m(const struct edid *edid) { const struct detailed_timing *descriptor = NULL; @@ -2488,7 +2488,7 @@ drm_gtf2_m(struct edid *edid) } static int -drm_gtf2_k(struct edid *edid) +drm_gtf2_k(const struct edid *edid) { const struct detailed_timing *descriptor = NULL; @@ -2500,7 +2500,7 @@ drm_gtf2_k(struct edid *edid) } static int -drm_gtf2_2j(struct edid *edid) +drm_gtf2_2j(const struct edid *edid) { const struct detailed_timing *descriptor = NULL; @@ -2515,7 +2515,7 @@ drm_gtf2_2j(struct edid *edid) * standard_timing_level - get std. timing level(CVT/GTF/DMT) * @edid: EDID block to scan */ -static int standard_timing_level(struct edid *edid) +static int standard_timing_level(const struct edid *edid) { if (edid->revision >= 2) { if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) @@ -2558,7 +2558,7 @@ static int drm_mode_hsync(const struct drm_display_mode *mode) * and convert them into a real mode using CVT/GTF/DMT. */ static struct drm_display_mode * -drm_mode_std(struct drm_connector *connector, struct edid *edid, +drm_mode_std(struct drm_connector *connector, const struct edid *edid, const struct std_timing *t) { struct drm_device *dev = connector->dev; @@ -2720,7 +2720,7 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, * return a new struct drm_display_mode. */ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, - struct edid *edid, + const struct edid *edid, const struct detailed_timing *timing, u32 quirks) { @@ -2820,7 +2820,7 @@ set_size: static bool mode_in_hsync_range(const struct drm_display_mode *mode, - struct edid *edid, const u8 *t) + const struct edid *edid, const u8 *t) { int hsync, hmin, hmax; @@ -2837,7 +2837,7 @@ mode_in_hsync_range(const struct drm_display_mode *mode, static bool mode_in_vsync_range(const struct drm_display_mode *mode, - struct edid *edid, const u8 *t) + const struct edid *edid, const u8 *t) { int vsync, vmin, vmax; @@ -2853,7 +2853,7 @@ mode_in_vsync_range(const struct drm_display_mode *mode, } static u32 -range_pixel_clock(struct edid *edid, const u8 *t) +range_pixel_clock(const struct edid *edid, const u8 *t) { /* unspecified */ if (t[9] == 0 || t[9] == 255) @@ -2868,7 +2868,7 @@ range_pixel_clock(struct edid *edid, const u8 *t) } static bool -mode_in_range(const struct drm_display_mode *mode, struct edid *edid, +mode_in_range(const struct drm_display_mode *mode, const struct edid *edid, const struct detailed_timing *timing) { u32 max_clock; @@ -2914,7 +2914,7 @@ static bool valid_inferred_mode(const struct drm_connector *connector, } static int -drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, +drm_dmt_modes_for_range(struct drm_connector *connector, const struct edid *edid, const struct detailed_timing *timing) { int i, modes = 0; @@ -2949,7 +2949,7 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode) } static int -drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, +drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid, const struct detailed_timing *timing) { int i, modes = 0; @@ -2978,7 +2978,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, } static int -drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, +drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid, const struct detailed_timing *timing) { int i, modes = 0; @@ -3046,7 +3046,7 @@ do_inferred_modes(const struct detailed_timing *timing, void *c) } static int -add_inferred_modes(struct drm_connector *connector, struct edid *edid) +add_inferred_modes(struct drm_connector *connector, const struct edid *edid) { struct detailed_mode_closure closure = { .connector = connector, @@ -3108,7 +3108,7 @@ do_established_modes(const struct detailed_timing *timing, void *c) * (defined above). Tease them out and add them to the global modes list. */ static int -add_established_modes(struct drm_connector *connector, struct edid *edid) +add_established_modes(struct drm_connector *connector, const struct edid *edid) { struct drm_device *dev = connector->dev; unsigned long est_bits = edid->established_timings.t1 | @@ -3145,7 +3145,7 @@ do_standard_modes(const struct detailed_timing *timing, void *c) struct detailed_mode_closure *closure = c; const struct detailed_non_pixel *data = &timing->data.other_data; struct drm_connector *connector = closure->connector; - struct edid *edid = closure->edid; + const struct edid *edid = closure->edid; int i; if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES)) @@ -3172,7 +3172,7 @@ do_standard_modes(const struct detailed_timing *timing, void *c) * GTF or CVT. Grab them from @edid and add them to the list. */ static int -add_standard_modes(struct drm_connector *connector, struct edid *edid) +add_standard_modes(struct drm_connector *connector, const struct edid *edid) { int i, modes = 0; struct detailed_mode_closure closure = { @@ -3264,7 +3264,7 @@ do_cvt_mode(const struct detailed_timing *timing, void *c) } static int -add_cvt_modes(struct drm_connector *connector, struct edid *edid) +add_cvt_modes(struct drm_connector *connector, const struct edid *edid) { struct detailed_mode_closure closure = { .connector = connector, @@ -3318,7 +3318,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) * @quirks: quirks to apply */ static int -add_detailed_modes(struct drm_connector *connector, struct edid *edid, +add_detailed_modes(struct drm_connector *connector, const struct edid *edid, u32 quirks) { struct detailed_mode_closure closure = { @@ -4525,7 +4525,7 @@ monitor_name(const struct detailed_timing *timing, void *data) *res = timing->data.other_data.data.str.str; } -static int get_monitor_name(struct edid *edid, char name[13]) +static int get_monitor_name(const struct edid *edid, char name[13]) { const char *edid_name = NULL; int mnl; -- cgit From f4e558ec9d9fb9484c30221a41cc5bfa73d33e6c Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:32 +0300 Subject: drm/edid: add more general struct edid constness in the interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this, the remaining non-const parts are the ones that actually modify the EDID, for example to fix corrupt EDID. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/c7156f22494b585c55a00a6732462bde0cc19dbf.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 21 +++++++++++---------- include/drm/drm_edid.h | 10 +++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fee47ac1bdd8..2bdc0eef8f35 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2150,7 +2150,7 @@ static u32 edid_extract_panel_id(const struct edid *edid) u32 drm_edid_get_panel_id(struct i2c_adapter *adapter) { - struct edid *edid; + const struct edid *edid; u32 panel_id; edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter); @@ -3664,7 +3664,7 @@ static bool drm_valid_hdmi_vic(u8 vic) } static int -add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) +add_alternate_cea_modes(struct drm_connector *connector, const struct edid *edid) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *tmp; @@ -4345,7 +4345,7 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, } static int -add_cea_modes(struct drm_connector *connector, struct edid *edid) +add_cea_modes(struct drm_connector *connector, const struct edid *edid) { const u8 *cea = drm_find_cea_extension(edid); const u8 *db, *hdmi = NULL, *video = NULL; @@ -4551,7 +4551,7 @@ static int get_monitor_name(const struct edid *edid, char name[13]) * @bufsize: The size of the name buffer (should be at least 14 chars.) * */ -void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize) +void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize) { int name_length; char buf[13]; @@ -4585,7 +4585,8 @@ static void clear_eld(struct drm_connector *connector) * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The * HDCP and Port_ID ELD fields are left for the graphics driver to fill in. */ -static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) +static void drm_edid_to_eld(struct drm_connector *connector, + const struct edid *edid) { uint8_t *eld = connector->eld; const u8 *cea; @@ -4681,7 +4682,7 @@ static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) * * Return: The number of found SADs or negative number on error. */ -int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) +int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads) { int count = 0; int i, start, end, dbl; @@ -4743,7 +4744,7 @@ EXPORT_SYMBOL(drm_edid_to_sad); * Return: The number of found Speaker Allocation Blocks or negative number on * error. */ -int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) +int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb) { int count = 0; int i, start, end, dbl; @@ -4838,7 +4839,7 @@ EXPORT_SYMBOL(drm_av_sync_delay); * * Return: True if the monitor is HDMI, false if not or unknown. */ -bool drm_detect_hdmi_monitor(struct edid *edid) +bool drm_detect_hdmi_monitor(const struct edid *edid) { const u8 *edid_ext; int i; @@ -4876,7 +4877,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor); * * Return: True if the monitor supports audio, false otherwise. */ -bool drm_detect_monitor_audio(struct edid *edid) +bool drm_detect_monitor_audio(const struct edid *edid) { const u8 *edid_ext; int i, j; @@ -5538,7 +5539,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, } static int add_displayid_detailed_modes(struct drm_connector *connector, - struct edid *edid) + const struct edid *edid) { const struct displayid_block *block; struct displayid_iter iter; diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 144c495b99c4..48b1bf9c315a 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -372,8 +372,8 @@ struct drm_connector; struct drm_connector_state; struct drm_display_mode; -int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads); -int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb); +int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads); +int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb); int drm_av_sync_delay(struct drm_connector *connector, const struct drm_display_mode *mode); @@ -569,8 +569,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); int drm_add_override_edid_modes(struct drm_connector *connector); u8 drm_match_cea_mode(const struct drm_display_mode *to_match); -bool drm_detect_hdmi_monitor(struct edid *edid); -bool drm_detect_monitor_audio(struct edid *edid); +bool drm_detect_hdmi_monitor(const struct edid *edid); +bool drm_detect_monitor_audio(const struct edid *edid); enum hdmi_quantization_range drm_default_rgb_quant_range(const struct drm_display_mode *mode); int drm_add_modes_noedid(struct drm_connector *connector, @@ -582,7 +582,7 @@ int drm_edid_header_is_valid(const u8 *raw_edid); bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, bool *edid_corrupt); bool drm_edid_is_valid(struct edid *edid); -void drm_edid_get_monitor_name(struct edid *edid, char *name, +void drm_edid_get_monitor_name(const struct edid *edid, char *name, int buflen); struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, -- cgit From f40ab034b6aebb7ee9461d402915ca83e693d1bd Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:33 +0300 Subject: drm/edid: split drm_add_edid_modes() to two MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the size of the function that actually modifies the EDID. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/437c3c79f68d1144444fb2dd18a678f3aa97272c.1648477901.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 2bdc0eef8f35..359c52e5318a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5556,18 +5556,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -/** - * drm_add_edid_modes - add modes from EDID data, if available - * @connector: connector we're probing - * @edid: EDID data - * - * Add the specified modes to the connector's mode list. Also fills out the - * &drm_display_info structure and ELD in @connector with any information which - * can be derived from the edid. - * - * Return: The number of modes added or 0 if we couldn't find any. - */ -int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +static int drm_edid_connector_update(struct drm_connector *connector, + const struct edid *edid) { int num_modes = 0; u32 quirks; @@ -5576,12 +5566,6 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) clear_eld(connector); return 0; } - if (!drm_edid_is_valid(edid)) { - clear_eld(connector); - drm_warn(connector->dev, "%s: EDID invalid.\n", - connector->name); - return 0; - } drm_edid_to_eld(connector, edid); @@ -5633,6 +5617,28 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) return num_modes; } + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @connector: connector we're probing + * @edid: EDID data + * + * Add the specified modes to the connector's mode list. Also fills out the + * &drm_display_info structure and ELD in @connector with any information which + * can be derived from the edid. + * + * Return: The number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +{ + if (edid && !drm_edid_is_valid(edid)) { + drm_warn(connector->dev, "%s: EDID invalid.\n", + connector->name); + edid = NULL; + } + + return drm_edid_connector_update(connector, edid); +} EXPORT_SYMBOL(drm_add_edid_modes); /** -- cgit From 9f8b577f7b43b2170628d6c537252785dcc2dcea Mon Sep 17 00:00:00 2001 From: "Andrea Parri (Microsoft)" Date: Tue, 1 Mar 2022 15:11:35 +0100 Subject: Drivers: hv: vmbus: Deactivate sysctl_record_panic_msg by default in isolated guests hv_panic_page might contain guest-sensitive information, do not dump it over to Hyper-V by default in isolated guests. While at it, update some comments in hyperv_{panic,die}_event(). Reported-by: Dexuan Cui Signed-off-by: Andrea Parri (Microsoft) Reviewed-by: Dexuan Cui Link: https://lore.kernel.org/r/20220301141135.2232-1-parri.andrea@gmail.com Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 12a2b37e87f3..a963b970ffb2 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -77,8 +77,8 @@ static int hyperv_panic_event(struct notifier_block *nb, unsigned long val, /* * Hyper-V should be notified only once about a panic. If we will be - * doing hyperv_report_panic_msg() later with kmsg data, don't do - * the notification here. + * doing hv_kmsg_dump() with kmsg data later, don't do the notification + * here. */ if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE && hyperv_report_reg()) { @@ -100,8 +100,8 @@ static int hyperv_die_event(struct notifier_block *nb, unsigned long val, /* * Hyper-V should be notified only once about a panic. If we will be - * doing hyperv_report_panic_msg() later with kmsg data, don't do - * the notification here. + * doing hv_kmsg_dump() with kmsg data later, don't do the notification + * here. */ if (hyperv_report_reg()) hyperv_report_panic(regs, val, true); @@ -1546,14 +1546,20 @@ static int vmbus_bus_init(void) if (ret) goto err_connect; + if (hv_is_isolation_supported()) + sysctl_record_panic_msg = 0; + /* * Only register if the crash MSRs are available */ if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { u64 hyperv_crash_ctl; /* - * Sysctl registration is not fatal, since by default - * reporting is enabled. + * Panic message recording (sysctl_record_panic_msg) + * is enabled by default in non-isolated guests and + * disabled by default in isolated guests; the panic + * message recording won't be available in isolated + * guests should the following registration fail. */ hv_ctl_table_hdr = register_sysctl_table(hv_root_table); if (!hv_ctl_table_hdr) -- cgit From 3a5469582c241abca22500f36a9cb8e9331969cf Mon Sep 17 00:00:00 2001 From: "Andrea Parri (Microsoft)" Date: Tue, 15 Mar 2022 15:10:53 +0100 Subject: Drivers: hv: vmbus: Fix initialization of device object in vmbus_device_register() Initialize the device's dma_{mask,parms} pointers and the device's dma_mask value before invoking device_register(). Address the following trace with 5.17-rc7: [ 49.646839] WARNING: CPU: 0 PID: 189 at include/linux/dma-mapping.h:543 netvsc_probe+0x37a/0x3a0 [hv_netvsc] [ 49.646928] Call Trace: [ 49.646930] [ 49.646935] vmbus_probe+0x40/0x60 [hv_vmbus] [ 49.646942] really_probe+0x1ce/0x3b0 [ 49.646948] __driver_probe_device+0x109/0x180 [ 49.646952] driver_probe_device+0x23/0xa0 [ 49.646955] __device_attach_driver+0x76/0xe0 [ 49.646958] ? driver_allows_async_probing+0x50/0x50 [ 49.646961] bus_for_each_drv+0x84/0xd0 [ 49.646964] __device_attach+0xed/0x170 [ 49.646967] device_initial_probe+0x13/0x20 [ 49.646970] bus_probe_device+0x8f/0xa0 [ 49.646973] device_add+0x41a/0x8e0 [ 49.646975] ? hrtimer_init+0x28/0x80 [ 49.646981] device_register+0x1b/0x20 [ 49.646983] vmbus_device_register+0x5e/0xf0 [hv_vmbus] [ 49.646991] vmbus_add_channel_work+0x12d/0x190 [hv_vmbus] [ 49.646999] process_one_work+0x21d/0x3f0 [ 49.647002] worker_thread+0x4a/0x3b0 [ 49.647005] ? process_one_work+0x3f0/0x3f0 [ 49.647007] kthread+0xff/0x130 [ 49.647011] ? kthread_complete_and_exit+0x20/0x20 [ 49.647015] ret_from_fork+0x22/0x30 [ 49.647020] [ 49.647021] ---[ end trace 0000000000000000 ]--- Fixes: 743b237c3a7b0 ("scsi: storvsc: Add Isolation VM support for storvsc driver") Signed-off-by: Andrea Parri (Microsoft) Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20220315141053.3223-1-parri.andrea@gmail.com Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a963b970ffb2..df22fbdc2ae2 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2103,6 +2103,10 @@ int vmbus_device_register(struct hv_device *child_device_obj) child_device_obj->device.parent = &hv_acpi_dev->dev; child_device_obj->device.release = vmbus_device_release; + child_device_obj->device.dma_parms = &child_device_obj->dma_parms; + child_device_obj->device.dma_mask = &child_device_obj->dma_mask; + dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64)); + /* * Register with the LDM. This will kick off the driver/device * binding...which will eventually call vmbus_match() and vmbus_probe() @@ -2128,9 +2132,6 @@ int vmbus_device_register(struct hv_device *child_device_obj) } hv_debug_add_dev_dir(child_device_obj); - child_device_obj->device.dma_parms = &child_device_obj->dma_parms; - child_device_obj->device.dma_mask = &child_device_obj->dma_mask; - dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64)); return 0; err_kset_unregister: -- cgit From 792f232d57ff28bbd5f9c4abe0466b23d5879dc8 Mon Sep 17 00:00:00 2001 From: "Guilherme G. Piccoli" Date: Tue, 15 Mar 2022 17:35:35 -0300 Subject: Drivers: hv: vmbus: Fix potential crash on module unload The vmbus driver relies on the panic notifier infrastructure to perform some operations when a panic event is detected. Since vmbus can be built as module, it is required that the driver handles both registering and unregistering such panic notifier callback. After commit 74347a99e73a ("x86/Hyper-V: Unload vmbus channel in hv panic callback") though, the panic notifier registration is done unconditionally in the module initialization routine whereas the unregistering procedure is conditionally guarded and executes only if HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE capability is set. This patch fixes that by unconditionally unregistering the panic notifier in the module's exit routine as well. Fixes: 74347a99e73a ("x86/Hyper-V: Unload vmbus channel in hv panic callback") Signed-off-by: Guilherme G. Piccoli Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20220315203535.682306-1-gpiccoli@igalia.com Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index df22fbdc2ae2..6c057c76c2ca 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2787,10 +2787,15 @@ static void __exit vmbus_exit(void) if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { kmsg_dump_unregister(&hv_kmsg_dumper); unregister_die_notifier(&hyperv_die_block); - atomic_notifier_chain_unregister(&panic_notifier_list, - &hyperv_panic_block); } + /* + * The panic notifier is always registered, hence we should + * also unconditionally unregister it here as well. + */ + atomic_notifier_chain_unregister(&panic_notifier_list, + &hyperv_panic_block); + free_page((unsigned long)hv_panic_page); unregister_sysctl_table(hv_ctl_table_hdr); hv_ctl_table_hdr = NULL; -- cgit From 37200078ed6aa2ac3c88a01a64996133dccfdd34 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Thu, 24 Mar 2022 09:14:51 -0700 Subject: Drivers: hv: vmbus: Propagate VMbus coherence to each VMbus device VMbus synthetic devices are not represented in the ACPI DSDT -- only the top level VMbus device is represented. As a result, on ARM64 coherence information in the _CCA method is not specified for synthetic devices, so they default to not hardware coherent. Drivers for some of these synthetic devices have been recently updated to use the standard DMA APIs, and they are incurring extra overhead of unneeded software coherence management. Fix this by propagating coherence information from the VMbus node in ACPI to the individual synthetic devices. There's no effect on x86/x64 where devices are always hardware coherent. Signed-off-by: Michael Kelley Acked-by: Robin Murphy Link: https://lore.kernel.org/r/1648138492-2191-2-git-send-email-mikelley@microsoft.com Signed-off-by: Wei Liu --- drivers/hv/hv_common.c | 11 +++++++++++ drivers/hv/vmbus_drv.c | 31 +++++++++++++++++++++++++++++++ include/asm-generic/mshyperv.h | 1 + 3 files changed, 43 insertions(+) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 181d16bbf49d..820e81406251 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -216,6 +217,16 @@ bool hv_query_ext_cap(u64 cap_query) } EXPORT_SYMBOL_GPL(hv_query_ext_cap); +void hv_setup_dma_ops(struct device *dev, bool coherent) +{ + /* + * Hyper-V does not offer a vIOMMU in the guest + * VM, so pass 0/NULL for the IOMMU settings + */ + arch_setup_dma_ops(dev, 0, 0, NULL, coherent); +} +EXPORT_SYMBOL_GPL(hv_setup_dma_ops); + bool hv_is_hibernation_supported(void) { return !hv_root_partition && acpi_sleep_state_supported(ACPI_STATE_S4); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 6c057c76c2ca..3cd0d3a44fa2 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -920,6 +920,21 @@ static int vmbus_probe(struct device *child_device) return ret; } +/* + * vmbus_dma_configure -- Configure DMA coherence for VMbus device + */ +static int vmbus_dma_configure(struct device *child_device) +{ + /* + * On ARM64, propagate the DMA coherence setting from the top level + * VMbus ACPI device to the child VMbus device being added here. + * On x86/x64 coherence is assumed and these calls have no effect. + */ + hv_setup_dma_ops(child_device, + device_get_dma_attr(&hv_acpi_dev->dev) == DEV_DMA_COHERENT); + return 0; +} + /* * vmbus_remove - Remove a vmbus device */ @@ -1040,6 +1055,7 @@ static struct bus_type hv_bus = { .remove = vmbus_remove, .probe = vmbus_probe, .uevent = vmbus_uevent, + .dma_configure = vmbus_dma_configure, .dev_groups = vmbus_dev_groups, .drv_groups = vmbus_drv_groups, .bus_groups = vmbus_bus_groups, @@ -2435,6 +2451,21 @@ static int vmbus_acpi_add(struct acpi_device *device) hv_acpi_dev = device; + /* + * Older versions of Hyper-V for ARM64 fail to include the _CCA + * method on the top level VMbus device in the DSDT. But devices + * are hardware coherent in all current Hyper-V use cases, so fix + * up the ACPI device to behave as if _CCA is present and indicates + * hardware coherence. + */ + ACPI_COMPANION_SET(&device->dev, device); + if (IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED) && + device_get_dma_attr(&device->dev) == DEV_DMA_NOT_SUPPORTED) { + pr_info("No ACPI _CCA found; assuming coherent device I/O\n"); + device->flags.cca_seen = true; + device->flags.coherent_dma = true; + } + result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, vmbus_walk_resources, NULL); diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index c08758b6b364..c05d2ce9b6cd 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -269,6 +269,7 @@ bool hv_isolation_type_snp(void); u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size); void hyperv_cleanup(void); bool hv_query_ext_cap(u64 cap_query); +void hv_setup_dma_ops(struct device *dev, bool coherent); void *hv_map_memory(void *addr, unsigned long size); void hv_unmap_memory(void *addr); #else /* CONFIG_HYPERV */ -- cgit From 8d21732475c637c7efcdb91dc927a4c594e97898 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Thu, 24 Mar 2022 09:14:52 -0700 Subject: PCI: hv: Propagate coherence from VMbus device to PCI device PCI pass-thru devices in a Hyper-V VM are represented as a VMBus device and as a PCI device. The coherence of the VMbus device is set based on the VMbus node in ACPI, but the PCI device has no ACPI node and defaults to not hardware coherent. This results in extra software coherence management overhead on ARM64 when devices are hardware coherent. Fix this by setting up the PCI host bus so that normal PCI mechanisms will propagate the coherence of the VMbus device to the PCI device. There's no effect on x86/x64 where devices are always hardware coherent. Signed-off-by: Michael Kelley Acked-by: Boqun Feng Acked-by: Robin Murphy Link: https://lore.kernel.org/r/1648138492-2191-3-git-send-email-mikelley@microsoft.com Signed-off-by: Wei Liu --- drivers/pci/controller/pci-hyperv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index ae0bc2fee4ca..88b3b56d0522 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -3404,6 +3404,15 @@ static int hv_pci_probe(struct hv_device *hdev, hbus->bridge->domain_nr = dom; #ifdef CONFIG_X86 hbus->sysdata.domain = dom; +#elif defined(CONFIG_ARM64) + /* + * Set the PCI bus parent to be the corresponding VMbus + * device. Then the VMbus device will be assigned as the + * ACPI companion in pcibios_root_bridge_prepare() and + * pci_dma_configure() will propagate device coherence + * information to devices created on the bus. + */ + hbus->sysdata.parent = hdev->device.parent; #endif hbus->hdev = hdev; -- cgit From b6cae15b5710c8097aad26a2e5e752c323ee5348 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Sun, 27 Mar 2022 08:25:10 -0700 Subject: Drivers: hv: vmbus: Prevent load re-ordering when reading ring buffer When reading a packet from a host-to-guest ring buffer, there is no memory barrier between reading the write index (to see if there is a packet to read) and reading the contents of the packet. The Hyper-V host uses store-release when updating the write index to ensure that writes of the packet data are completed first. On the guest side, the processor can reorder and read the packet data before the write index, and sometimes get stale packet data. Getting such stale packet data has been observed in a reproducible case in a VM on ARM64. Fix this by using virt_load_acquire() to read the write index, ensuring that reads of the packet data cannot be reordered before it. Preventing such reordering is logically correct, and with this change, getting stale data can no longer be reproduced. Signed-off-by: Michael Kelley Reviewed-by: Andrea Parri (Microsoft) Link: https://lore.kernel.org/r/1648394710-33480-1-git-send-email-mikelley@microsoft.com Signed-off-by: Wei Liu --- drivers/hv/ring_buffer.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 71efacb90965..3d215d9dec43 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -439,7 +439,16 @@ int hv_ringbuffer_read(struct vmbus_channel *channel, static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi) { u32 priv_read_loc = rbi->priv_read_index; - u32 write_loc = READ_ONCE(rbi->ring_buffer->write_index); + u32 write_loc; + + /* + * The Hyper-V host writes the packet data, then uses + * store_release() to update the write_index. Use load_acquire() + * here to prevent loads of the packet data from being re-ordered + * before the read of the write_index and potentially getting + * stale data. + */ + write_loc = virt_load_acquire(&rbi->ring_buffer->write_index); if (write_loc >= priv_read_loc) return write_loc - priv_read_loc; -- cgit From ec57376fba5abc0e571617ff88e2ade7970c2e4b Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 24 Mar 2022 13:48:25 -0700 Subject: drm/panel-edp: Add AUO B133UAN01 Add support for the AUO B133UAN01 13.3" WUXGA panel. Signed-off-by: Matthias Kaehlcke Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220324134819.v2.1.I816014b6c62da5a33af5021f3cc35cea66552c00@changeid --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index e15e62f235d8..db4eea1d7f67 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1847,6 +1847,7 @@ static const struct panel_delay delay_100_500_e200 = { static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x8495, &delay_200_500_e50, "B133UAN01.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0786, &delay_200_500_p2e80, "NV116WHM-T01"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"), -- cgit From 31818213170caa51d116eb5dc1167b88523b4fe1 Mon Sep 17 00:00:00 2001 From: Jeremy Sowden Date: Sun, 27 Mar 2022 23:36:25 +0100 Subject: netfilter: bitwise: fix reduce comparisons The `nft_bitwise_reduce` and `nft_bitwise_fast_reduce` functions should compare the bitwise operation in `expr` with the tracked operation associated with the destination register of `expr`. However, instead of being called on `expr` and `track->regs[priv->dreg].selector`, `nft_expr_priv` is called on `expr` twice, so both reduce functions return true even when the operations differ. Fixes: be5650f8f47e ("netfilter: nft_bitwise: track register operations") Signed-off-by: Jeremy Sowden Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_bitwise.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 38caa66632b4..f590ee1c8a1b 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -290,7 +290,7 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track, if (!track->regs[priv->sreg].selector) return false; - bitwise = nft_expr_priv(expr); + bitwise = nft_expr_priv(track->regs[priv->dreg].selector); if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector && track->regs[priv->sreg].num_reg == 0 && track->regs[priv->dreg].bitwise && @@ -442,7 +442,7 @@ static bool nft_bitwise_fast_reduce(struct nft_regs_track *track, if (!track->regs[priv->sreg].selector) return false; - bitwise = nft_expr_priv(expr); + bitwise = nft_expr_priv(track->regs[priv->dreg].selector); if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector && track->regs[priv->dreg].bitwise && track->regs[priv->dreg].bitwise->ops == expr->ops && -- cgit From ab0fc21bc7105b54bafd85bd8b82742f9e68898a Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Tue, 29 Mar 2022 19:32:07 +0800 Subject: Revert "NFSv4: Handle the special Linux file open access mode" This reverts commit 44942b4e457beda00981f616402a1a791e8c616e. After secondly opening a file with O_ACCMODE|O_DIRECT flags, nfs4_valid_open_stateid() will dereference NULL nfs4_state when lseek(). Reproducer: 1. mount -t nfs -o vers=4.2 $server_ip:/ /mnt/ 2. fd = open("/mnt/file", O_ACCMODE|O_DIRECT|O_CREAT) 3. close(fd) 4. fd = open("/mnt/file", O_ACCMODE|O_DIRECT) 5. lseek(fd) Reported-by: Lyu Tao Signed-off-by: ChenXiaoSong Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 1 - fs/nfs/nfs4file.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index e51d86707fca..e72900c059ee 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1180,7 +1180,6 @@ int nfs_open(struct inode *inode, struct file *filp) nfs_fscache_open_file(inode, filp); return 0; } -EXPORT_SYMBOL_GPL(nfs_open); /* * This function is called whenever some part of NFS notices that diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index d258933cf8c8..f336d0a4190e 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -51,7 +51,7 @@ nfs4_file_open(struct inode *inode, struct file *filp) return err; if ((openflags & O_ACCMODE) == 3) - return nfs_open(inode, filp); + openflags--; /* We can't create new files here */ openflags &= ~(O_CREAT|O_EXCL); -- cgit From b243874f6f9568b2daf1a00e9222cacdc15e159c Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Tue, 29 Mar 2022 19:32:08 +0800 Subject: NFSv4: fix open failure with O_ACCMODE flag open() with O_ACCMODE|O_DIRECT flags secondly will fail. Reproducer: 1. mount -t nfs -o vers=4.2 $server_ip:/ /mnt/ 2. fd = open("/mnt/file", O_ACCMODE|O_DIRECT|O_CREAT) 3. close(fd) 4. fd = open("/mnt/file", O_ACCMODE|O_DIRECT) Server nfsd4_decode_share_access() will fail with error nfserr_bad_xdr when client use incorrect share access mode of 0. Fix this by using NFS4_SHARE_ACCESS_BOTH share access mode in client, just like firstly opening. Fixes: ce4ef7c0a8a05 ("NFS: Split out NFS v4 file operations") Signed-off-by: ChenXiaoSong Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 10 ---------- fs/nfs/internal.h | 10 ++++++++++ fs/nfs/nfs4file.c | 6 ++++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index bac4cf1a308e..0365063b85a2 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1991,16 +1991,6 @@ const struct dentry_operations nfs4_dentry_operations = { }; EXPORT_SYMBOL_GPL(nfs4_dentry_operations); -static fmode_t flags_to_mode(int flags) -{ - fmode_t res = (__force fmode_t)flags & FMODE_EXEC; - if ((flags & O_ACCMODE) != O_WRONLY) - res |= FMODE_READ; - if ((flags & O_ACCMODE) != O_RDONLY) - res |= FMODE_WRITE; - return res; -} - static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags, struct file *filp) { return alloc_nfs_open_context(dentry, flags_to_mode(open_flags), filp); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 57b0497105c8..7eefa16ed381 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -42,6 +42,16 @@ static inline bool nfs_lookup_is_soft_revalidate(const struct dentry *dentry) return true; } +static inline fmode_t flags_to_mode(int flags) +{ + fmode_t res = (__force fmode_t)flags & FMODE_EXEC; + if ((flags & O_ACCMODE) != O_WRONLY) + res |= FMODE_READ; + if ((flags & O_ACCMODE) != O_RDONLY) + res |= FMODE_WRITE; + return res; +} + /* * Note: RFC 1813 doesn't limit the number of auth flavors that * a server can return, so make something up. diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index f336d0a4190e..7b861e4f0533 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -32,6 +32,7 @@ nfs4_file_open(struct inode *inode, struct file *filp) struct dentry *parent = NULL; struct inode *dir; unsigned openflags = filp->f_flags; + fmode_t f_mode; struct iattr attr; int err; @@ -50,8 +51,9 @@ nfs4_file_open(struct inode *inode, struct file *filp) if (err) return err; + f_mode = filp->f_mode; if ((openflags & O_ACCMODE) == 3) - openflags--; + f_mode |= flags_to_mode(openflags); /* We can't create new files here */ openflags &= ~(O_CREAT|O_EXCL); @@ -59,7 +61,7 @@ nfs4_file_open(struct inode *inode, struct file *filp) parent = dget_parent(dentry); dir = d_inode(parent); - ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode, filp); + ctx = alloc_nfs_open_context(file_dentry(filp), f_mode, filp); err = PTR_ERR(ctx); if (IS_ERR(ctx)) goto out; -- cgit From eb07d5a4da041fd2e30e386e5fd12d23bb31cf9e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 30 Mar 2022 11:48:37 +1100 Subject: SUNRPC: handle malloc failure in ->request_prepare If ->request_prepare() detects an error, it sets ->rq_task->tk_status. This is easy for callers to ignore. The only caller is xprt_request_enqueue_receive() and it does ignore the error, as does call_encode() which calls it. This can result in a request being queued to receive a reply without an allocated receive buffer. So instead of setting rq_task->tk_status, return an error, and store in ->tk_status only in call_encode(); The call to xprt_request_enqueue_receive() is now earlier in call_encode(), where the error can still be handled. Signed-off-by: NeilBrown Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xprt.h | 5 ++--- net/sunrpc/clnt.c | 6 +++--- net/sunrpc/xprt.c | 23 +++++++++++++++-------- net/sunrpc/xprtsock.c | 4 ++-- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index eef5e87c03b4..f171f8c09e13 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -144,7 +144,7 @@ struct rpc_xprt_ops { unsigned short (*get_srcport)(struct rpc_xprt *xprt); int (*buf_alloc)(struct rpc_task *task); void (*buf_free)(struct rpc_task *task); - void (*prepare_request)(struct rpc_rqst *req); + int (*prepare_request)(struct rpc_rqst *req); int (*send_request)(struct rpc_rqst *req); void (*wait_for_reply_request)(struct rpc_task *task); void (*timer)(struct rpc_xprt *xprt, struct rpc_task *task); @@ -357,10 +357,9 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req); -void xprt_request_prepare(struct rpc_rqst *req); bool xprt_prepare_transmit(struct rpc_task *task); void xprt_request_enqueue_transmit(struct rpc_task *task); -void xprt_request_enqueue_receive(struct rpc_task *task); +int xprt_request_enqueue_receive(struct rpc_task *task); void xprt_request_wait_receive(struct rpc_task *task); void xprt_request_dequeue_xprt(struct rpc_task *task); bool xprt_request_need_retransmit(struct rpc_task *task); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 8bf2af8546d2..3c7407104d54 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1858,6 +1858,9 @@ call_encode(struct rpc_task *task) xprt_request_dequeue_xprt(task); /* Encode here so that rpcsec_gss can use correct sequence number. */ rpc_xdr_encode(task); + /* Add task to reply queue before transmission to avoid races */ + if (task->tk_status == 0 && rpc_reply_expected(task)) + task->tk_status = xprt_request_enqueue_receive(task); /* Did the encode result in an error condition? */ if (task->tk_status != 0) { /* Was the error nonfatal? */ @@ -1881,9 +1884,6 @@ call_encode(struct rpc_task *task) return; } - /* Add task to reply queue before transmission to avoid races */ - if (rpc_reply_expected(task)) - xprt_request_enqueue_receive(task); xprt_request_enqueue_transmit(task); out: task->tk_action = call_transmit; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 880bfe8dc7f6..73344ffb2692 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -69,10 +69,11 @@ /* * Local functions */ -static void xprt_init(struct rpc_xprt *xprt, struct net *net); +static void xprt_init(struct rpc_xprt *xprt, struct net *net); static __be32 xprt_alloc_xid(struct rpc_xprt *xprt); -static void xprt_destroy(struct rpc_xprt *xprt); -static void xprt_request_init(struct rpc_task *task); +static void xprt_destroy(struct rpc_xprt *xprt); +static void xprt_request_init(struct rpc_task *task); +static int xprt_request_prepare(struct rpc_rqst *req); static DEFINE_SPINLOCK(xprt_list_lock); static LIST_HEAD(xprt_list); @@ -1143,16 +1144,19 @@ xprt_request_need_enqueue_receive(struct rpc_task *task, struct rpc_rqst *req) * @task: RPC task * */ -void +int xprt_request_enqueue_receive(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; + int ret; if (!xprt_request_need_enqueue_receive(task, req)) - return; + return 0; - xprt_request_prepare(task->tk_rqstp); + ret = xprt_request_prepare(task->tk_rqstp); + if (ret) + return ret; spin_lock(&xprt->queue_lock); /* Update the softirq receive buffer */ @@ -1166,6 +1170,7 @@ xprt_request_enqueue_receive(struct rpc_task *task) /* Turn off autodisconnect */ del_singleshot_timer_sync(&xprt->timer); + return 0; } /** @@ -1452,14 +1457,16 @@ xprt_request_dequeue_xprt(struct rpc_task *task) * * Calls into the transport layer to do whatever is needed to prepare * the request for transmission or receive. + * Returns error, or zero. */ -void +static int xprt_request_prepare(struct rpc_rqst *req) { struct rpc_xprt *xprt = req->rq_xprt; if (xprt->ops->prepare_request) - xprt->ops->prepare_request(req); + return xprt->ops->prepare_request(req); + return 0; } /** diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 78af7518f263..9b75891b3cc0 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -822,11 +822,11 @@ static int xs_stream_nospace(struct rpc_rqst *req, bool vm_wait) return ret; } -static void +static int xs_stream_prepare_request(struct rpc_rqst *req) { xdr_free_bvec(&req->rq_rcv_buf); - req->rq_task->tk_status = xdr_alloc_bvec( + return xdr_alloc_bvec( &req->rq_rcv_buf, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); } -- cgit From 87d663d40801dffc99a5ad3b0188ad3e2b4d1557 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 22 Mar 2022 14:57:02 +0900 Subject: scsi: mpt3sas: Fix use after free in _scsih_expander_node_remove() The function mpt3sas_transport_port_remove() called in _scsih_expander_node_remove() frees the port field of the sas_expander structure, leading to the following use-after-free splat from KASAN when the ioc_info() call following that function is executed (e.g. when doing rmmod of the driver module): [ 3479.371167] ================================================================== [ 3479.378496] BUG: KASAN: use-after-free in _scsih_expander_node_remove+0x710/0x750 [mpt3sas] [ 3479.386936] Read of size 1 at addr ffff8881c037691c by task rmmod/1531 [ 3479.393524] [ 3479.395035] CPU: 18 PID: 1531 Comm: rmmod Not tainted 5.17.0-rc8+ #1436 [ 3479.401712] Hardware name: Supermicro Super Server/H12SSL-NT, BIOS 2.1 06/02/2021 [ 3479.409263] Call Trace: [ 3479.411743] [ 3479.413875] dump_stack_lvl+0x45/0x59 [ 3479.417582] print_address_description.constprop.0+0x1f/0x120 [ 3479.423389] ? _scsih_expander_node_remove+0x710/0x750 [mpt3sas] [ 3479.429469] kasan_report.cold+0x83/0xdf [ 3479.433438] ? _scsih_expander_node_remove+0x710/0x750 [mpt3sas] [ 3479.439514] _scsih_expander_node_remove+0x710/0x750 [mpt3sas] [ 3479.445411] ? _raw_spin_unlock_irqrestore+0x2d/0x40 [ 3479.452032] scsih_remove+0x525/0xc90 [mpt3sas] [ 3479.458212] ? mpt3sas_expander_remove+0x1d0/0x1d0 [mpt3sas] [ 3479.465529] ? down_write+0xde/0x150 [ 3479.470746] ? up_write+0x14d/0x460 [ 3479.475840] ? kernfs_find_ns+0x137/0x310 [ 3479.481438] pci_device_remove+0x65/0x110 [ 3479.487013] __device_release_driver+0x316/0x680 [ 3479.493180] driver_detach+0x1ec/0x2d0 [ 3479.498499] bus_remove_driver+0xe7/0x2d0 [ 3479.504081] pci_unregister_driver+0x26/0x250 [ 3479.510033] _mpt3sas_exit+0x2b/0x6cf [mpt3sas] [ 3479.516144] __x64_sys_delete_module+0x2fd/0x510 [ 3479.522315] ? free_module+0xaa0/0xaa0 [ 3479.527593] ? __cond_resched+0x1c/0x90 [ 3479.532951] ? lockdep_hardirqs_on_prepare+0x273/0x3e0 [ 3479.539607] ? syscall_enter_from_user_mode+0x21/0x70 [ 3479.546161] ? trace_hardirqs_on+0x1c/0x110 [ 3479.551828] do_syscall_64+0x35/0x80 [ 3479.556884] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 3479.563402] RIP: 0033:0x7f1fc482483b ... [ 3479.943087] ================================================================== Fix this by introducing the local variable port_id to store the port ID value before executing mpt3sas_transport_port_remove(). This local variable is then used in the call to ioc_info() instead of dereferencing the freed port structure. Link: https://lore.kernel.org/r/20220322055702.95276-1-damien.lemoal@opensource.wdc.com Fixes: 7d310f241001 ("scsi: mpt3sas: Get device objects using sas_address & portID") Cc: stable@vger.kernel.org Acked-by: Sreekanth Reddy Signed-off-by: Damien Le Moal Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 00792767c620..7e476f50935b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -11035,6 +11035,7 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, { struct _sas_port *mpt3sas_port, *next; unsigned long flags; + int port_id; /* remove sibling ports attached to this expander */ list_for_each_entry_safe(mpt3sas_port, next, @@ -11055,6 +11056,8 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, mpt3sas_port->hba_port); } + port_id = sas_expander->port->port_id; + mpt3sas_transport_port_remove(ioc, sas_expander->sas_address, sas_expander->sas_address_parent, sas_expander->port); @@ -11062,7 +11065,7 @@ _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n", sas_expander->handle, (unsigned long long) sas_expander->sas_address, - sas_expander->port->port_id); + port_id); spin_lock_irqsave(&ioc->sas_node_lock, flags); list_del(&sas_expander->list); -- cgit From ebfe3e0c5e805da3dd692bb120cd6269b7c19b80 Mon Sep 17 00:00:00 2001 From: Wenchao Hao Date: Thu, 17 Mar 2022 11:01:16 -0400 Subject: scsi: libiscsi: Remove unnecessary memset() in iscsi_conn_setup() iscsi_cls_conn is alloced by kzalloc(), the whole iscsi_cls_conn is zero filled already including the dd_data. So it is unnecessary to call memset again. Link: https://lore.kernel.org/r/20220317150116.194140-1-haowenchao@huawei.com Reviewed-by: Wu Bo Reviewed-by: Lee Duncan Reviewed-by: Mike Christie Signed-off-by: Wenchao Hao Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index d09926e6c8a8..cf4211c6500d 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -3045,7 +3045,6 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, if (!cls_conn) return NULL; conn = cls_conn->dd_data; - memset(conn, 0, sizeof(*conn) + dd_size); conn->dd_data = cls_conn->dd_data + sizeof(*conn); conn->session = session; -- cgit From a6968f7a367f128d120447360734344d5a3d5336 Mon Sep 17 00:00:00 2001 From: Xiaoguang Wang Date: Fri, 11 Mar 2022 21:22:05 +0800 Subject: scsi: target: tcmu: Fix possible page UAF tcmu_try_get_data_page() looks up pages under cmdr_lock, but it does not take refcount properly and just returns page pointer. When tcmu_try_get_data_page() returns, the returned page may have been freed by tcmu_blocks_release(). We need to get_page() under cmdr_lock to avoid concurrent tcmu_blocks_release(). Link: https://lore.kernel.org/r/20220311132206.24515-1-xiaoguang.wang@linux.alibaba.com Reviewed-by: Bodo Stroesser Signed-off-by: Xiaoguang Wang Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 95d4ca50a605..fd7267baa707 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1821,6 +1821,7 @@ static struct page *tcmu_try_get_data_page(struct tcmu_dev *udev, uint32_t dpi) mutex_lock(&udev->cmdr_lock); page = xa_load(&udev->data_pages, dpi); if (likely(page)) { + get_page(page); mutex_unlock(&udev->cmdr_lock); return page; } @@ -1877,6 +1878,7 @@ static vm_fault_t tcmu_vma_fault(struct vm_fault *vmf) /* For the vmalloc()ed cmd area pages */ addr = (void *)(unsigned long)info->mem[mi].addr + offset; page = vmalloc_to_page(addr); + get_page(page); } else { uint32_t dpi; @@ -1887,7 +1889,6 @@ static vm_fault_t tcmu_vma_fault(struct vm_fault *vmf) return VM_FAULT_SIGBUS; } - get_page(page); vmf->page = page; return 0; } -- cgit From 35ed9613d83f3c1f011877d591fd7d36f2666106 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Mar 2022 20:27:34 -0700 Subject: scsi: lpfc: Improve PCI EEH Error and Recovery Handling Following EEH errors, the driver can crash or hang when deleting the localport or when attempting to unload. The EEH handlers in the driver did not notify the NVMe-FC transport before tearing the driver down. This was delayed until the resume steps. This worked for SCSI because lpfc_block_scsi() would notify the scsi_fc_transport that the target was not available but it would not clean up all the references to the ndlp. The SLI3 prep for dev reset handler did the lpfc_offline_prep() and lpfc_offline() calls to get the port stopped before restarting. The SLI4 version of the prep for dev reset just destroyed the queues and did not stop NVMe from continuing. Also because the port was not really stopped the localport destroy would hang because the transport was still waiting for I/O. Additionally, a devloss tmo can fire and post events to a stopped worker thread creating another hang condition. lpfc_sli4_prep_dev_for_reset() is modified to call lpfc_offline_prep() and lpfc_offline() rather than just lpfc_scsi_dev_block() to ensure both SCSI and NVMe transports are notified to block I/O to the driver. Logic is added to devloss handler and worker thread to clean up ndlp references and quiesce appropriately. Link: https://lore.kernel.org/r/20220317032737.45308-2-jsmart2021@gmail.com Co-developed-by: Justin Tee Signed-off-by: Justin Tee Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 7 ++- drivers/scsi/lpfc/lpfc_crtn.h | 3 + drivers/scsi/lpfc/lpfc_hbadisc.c | 119 +++++++++++++++++++++++++++++++-------- drivers/scsi/lpfc/lpfc_init.c | 60 ++++++++++++-------- drivers/scsi/lpfc/lpfc_nvme.c | 11 ++-- drivers/scsi/lpfc/lpfc_sli.c | 15 +++-- 6 files changed, 157 insertions(+), 58 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 86653aa9b389..8405fd0bbc59 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -896,6 +896,11 @@ enum lpfc_irq_chann_mode { NHT_MODE, }; +enum lpfc_hba_bit_flags { + FABRIC_COMANDS_BLOCKED, + HBA_PCI_ERR, +}; + struct lpfc_hba { /* SCSI interface function jump table entries */ struct lpfc_io_buf * (*lpfc_get_scsi_buf) @@ -1042,7 +1047,6 @@ struct lpfc_hba { * Firmware supports Forced Link Speed * capability */ -#define HBA_PCI_ERR 0x80000 /* The PCI slot is offline */ #define HBA_FLOGI_ISSUED 0x100000 /* FLOGI was issued */ #define HBA_SHORT_CMF 0x200000 /* shorter CMF timer routine */ #define HBA_CGN_DAY_WRAP 0x400000 /* HBA Congestion info day wraps */ @@ -1349,7 +1353,6 @@ struct lpfc_hba { atomic_t fabric_iocb_count; struct timer_list fabric_block_timer; unsigned long bit_flags; -#define FABRIC_COMANDS_BLOCKED 0 atomic_t num_rsrc_err; atomic_t num_cmd_success; unsigned long last_rsrc_error_time; diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 96408cd6c4c8..9897a1aa387b 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -670,3 +670,6 @@ struct lpfc_vmid *lpfc_get_vmid_from_hashtable(struct lpfc_vport *vport, uint32_t hash, uint8_t *buf); void lpfc_vmid_vport_cleanup(struct lpfc_vport *vport); int lpfc_issue_els_qfpa(struct lpfc_vport *vport); + +void lpfc_sli_rpi_release(struct lpfc_vport *vport, + struct lpfc_nodelist *ndlp); diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 0144da30e3db..6983c70f2fc6 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -109,8 +109,8 @@ lpfc_rport_invalid(struct fc_rport *rport) ndlp = rdata->pnode; if (!rdata->pnode) { - pr_err("**** %s: NULL ndlp on rport x%px SID x%x\n", - __func__, rport, rport->scsi_target_id); + pr_info("**** %s: NULL ndlp on rport x%px SID x%x\n", + __func__, rport, rport->scsi_target_id); return -EINVAL; } @@ -169,9 +169,10 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, "3181 dev_loss_callbk x%06x, rport x%px flg x%x " - "load_flag x%x refcnt %d\n", + "load_flag x%x refcnt %d state %d xpt x%x\n", ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag, - vport->load_flag, kref_read(&ndlp->kref)); + vport->load_flag, kref_read(&ndlp->kref), + ndlp->nlp_state, ndlp->fc4_xpt_flags); /* Don't schedule a worker thread event if the vport is going down. * The teardown process cleans up the node via lpfc_drop_node. @@ -181,6 +182,11 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) ndlp->rport = NULL; ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD; + /* clear the NLP_XPT_REGD if the node is not registered + * with nvme-fc + */ + if (ndlp->fc4_xpt_flags == NLP_XPT_REGD) + ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD; /* Remove the node reference from remote_port_add now. * The driver will not call remote_port_delete. @@ -225,18 +231,36 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) ndlp->rport = NULL; spin_unlock_irqrestore(&ndlp->lock, iflags); - /* We need to hold the node by incrementing the reference - * count until this queued work is done - */ - evtp->evt_arg1 = lpfc_nlp_get(ndlp); + if (phba->worker_thread) { + /* We need to hold the node by incrementing the reference + * count until this queued work is done + */ + evtp->evt_arg1 = lpfc_nlp_get(ndlp); + + spin_lock_irqsave(&phba->hbalock, iflags); + if (evtp->evt_arg1) { + evtp->evt = LPFC_EVT_DEV_LOSS; + list_add_tail(&evtp->evt_listp, &phba->work_list); + lpfc_worker_wake_up(phba); + } + spin_unlock_irqrestore(&phba->hbalock, iflags); + } else { + lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, + "3188 worker thread is stopped %s x%06x, " + " rport x%px flg x%x load_flag x%x refcnt " + "%d\n", __func__, ndlp->nlp_DID, + ndlp->rport, ndlp->nlp_flag, + vport->load_flag, kref_read(&ndlp->kref)); + if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD)) { + spin_lock_irqsave(&ndlp->lock, iflags); + /* Node is in dev loss. No further transaction. */ + ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS; + spin_unlock_irqrestore(&ndlp->lock, iflags); + lpfc_disc_state_machine(vport, ndlp, NULL, + NLP_EVT_DEVICE_RM); + } - spin_lock_irqsave(&phba->hbalock, iflags); - if (evtp->evt_arg1) { - evtp->evt = LPFC_EVT_DEV_LOSS; - list_add_tail(&evtp->evt_listp, &phba->work_list); - lpfc_worker_wake_up(phba); } - spin_unlock_irqrestore(&phba->hbalock, iflags); return; } @@ -503,11 +527,12 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "0203 Devloss timeout on " "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x " - "NPort x%06x Data: x%x x%x x%x\n", + "NPort x%06x Data: x%x x%x x%x refcnt %d\n", *name, *(name+1), *(name+2), *(name+3), *(name+4), *(name+5), *(name+6), *(name+7), ndlp->nlp_DID, ndlp->nlp_flag, - ndlp->nlp_state, ndlp->nlp_rpi); + ndlp->nlp_state, ndlp->nlp_rpi, + kref_read(&ndlp->kref)); } else { lpfc_printf_vlog(vport, KERN_INFO, LOG_TRACE_EVENT, "0204 Devloss timeout on " @@ -755,18 +780,22 @@ lpfc_work_list_done(struct lpfc_hba *phba) int free_evt; int fcf_inuse; uint32_t nlp_did; + bool hba_pci_err; spin_lock_irq(&phba->hbalock); while (!list_empty(&phba->work_list)) { list_remove_head((&phba->work_list), evtp, typeof(*evtp), evt_listp); spin_unlock_irq(&phba->hbalock); + hba_pci_err = test_bit(HBA_PCI_ERR, &phba->bit_flags); free_evt = 1; switch (evtp->evt) { case LPFC_EVT_ELS_RETRY: ndlp = (struct lpfc_nodelist *) (evtp->evt_arg1); - lpfc_els_retry_delay_handler(ndlp); - free_evt = 0; /* evt is part of ndlp */ + if (!hba_pci_err) { + lpfc_els_retry_delay_handler(ndlp); + free_evt = 0; /* evt is part of ndlp */ + } /* decrement the node reference count held * for this queued work */ @@ -788,8 +817,10 @@ lpfc_work_list_done(struct lpfc_hba *phba) break; case LPFC_EVT_RECOVER_PORT: ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); - lpfc_sli_abts_recover_port(ndlp->vport, ndlp); - free_evt = 0; + if (!hba_pci_err) { + lpfc_sli_abts_recover_port(ndlp->vport, ndlp); + free_evt = 0; + } /* decrement the node reference count held for * this queued work */ @@ -859,14 +890,18 @@ lpfc_work_done(struct lpfc_hba *phba) struct lpfc_vport **vports; struct lpfc_vport *vport; int i; + bool hba_pci_err; + hba_pci_err = test_bit(HBA_PCI_ERR, &phba->bit_flags); spin_lock_irq(&phba->hbalock); ha_copy = phba->work_ha; phba->work_ha = 0; spin_unlock_irq(&phba->hbalock); + if (hba_pci_err) + ha_copy = 0; /* First, try to post the next mailbox command to SLI4 device */ - if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) + if (phba->pci_dev_grp == LPFC_PCI_DEV_OC && !hba_pci_err) lpfc_sli4_post_async_mbox(phba); if (ha_copy & HA_ERATT) { @@ -886,7 +921,7 @@ lpfc_work_done(struct lpfc_hba *phba) lpfc_handle_latt(phba); /* Handle VMID Events */ - if (lpfc_is_vmid_enabled(phba)) { + if (lpfc_is_vmid_enabled(phba) && !hba_pci_err) { if (phba->pport->work_port_events & WORKER_CHECK_VMID_ISSUE_QFPA) { lpfc_check_vmid_qfpa_issue(phba); @@ -936,6 +971,8 @@ lpfc_work_done(struct lpfc_hba *phba) work_port_events = vport->work_port_events; vport->work_port_events &= ~work_port_events; spin_unlock_irq(&vport->work_port_lock); + if (hba_pci_err) + continue; if (work_port_events & WORKER_DISC_TMO) lpfc_disc_timeout_handler(vport); if (work_port_events & WORKER_ELS_TMO) @@ -1173,12 +1210,14 @@ lpfc_linkdown(struct lpfc_hba *phba) struct lpfc_vport **vports; LPFC_MBOXQ_t *mb; int i; + int offline; if (phba->link_state == LPFC_LINK_DOWN) return 0; /* Block all SCSI stack I/Os */ lpfc_scsi_dev_block(phba); + offline = pci_channel_offline(phba->pcidev); phba->defer_flogi_acc_flag = false; @@ -1219,7 +1258,7 @@ lpfc_linkdown(struct lpfc_hba *phba) lpfc_destroy_vport_work_array(phba, vports); /* Clean up any SLI3 firmware default rpi's */ - if (phba->sli_rev > LPFC_SLI_REV3) + if (phba->sli_rev > LPFC_SLI_REV3 || offline) goto skip_unreg_did; mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -4712,6 +4751,11 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) spin_lock_irqsave(&ndlp->lock, iflags); if (!(ndlp->fc4_xpt_flags & NLP_XPT_REGD)) { spin_unlock_irqrestore(&ndlp->lock, iflags); + lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI, + "0999 %s Not regd: ndlp x%px rport x%px DID " + "x%x FLG x%x XPT x%x\n", + __func__, ndlp, ndlp->rport, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->fc4_xpt_flags); return; } @@ -4722,6 +4766,13 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) ndlp->fc4_xpt_flags & SCSI_XPT_REGD) { vport->phba->nport_event_cnt++; lpfc_unregister_remote_port(ndlp); + } else if (!ndlp->rport) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI, + "1999 %s NDLP in devloss x%px DID x%x FLG x%x" + " XPT x%x refcnt %d\n", + __func__, ndlp, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->fc4_xpt_flags, + kref_read(&ndlp->kref)); } if (ndlp->fc4_xpt_flags & NVME_XPT_REGD) { @@ -6097,12 +6148,34 @@ lpfc_disc_flush_list(struct lpfc_vport *vport) } } +/* + * lpfc_notify_xport_npr - notifies xport of node disappearance + * @vport: Pointer to Virtual Port object. + * + * Transitions all ndlps to NPR state. When lpfc_nlp_set_state + * calls lpfc_nlp_state_cleanup, the ndlp->rport is unregistered + * and transport notified that the node is gone. + * Return Code: + * none + */ +static void +lpfc_notify_xport_npr(struct lpfc_vport *vport) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, + nlp_listp) { + lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + } +} void lpfc_cleanup_discovery_resources(struct lpfc_vport *vport) { lpfc_els_flush_rscn(vport); lpfc_els_flush_cmd(vport); lpfc_disc_flush_list(vport); + if (pci_channel_offline(vport->phba->pcidev)) + lpfc_notify_xport_npr(vport); } /*****************************************************************************/ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index eed6464bd880..b8ab6dcbadc5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1642,7 +1642,7 @@ lpfc_sli4_offline_eratt(struct lpfc_hba *phba) { spin_lock_irq(&phba->hbalock); if (phba->link_state == LPFC_HBA_ERROR && - phba->hba_flag & HBA_PCI_ERR) { + test_bit(HBA_PCI_ERR, &phba->bit_flags)) { spin_unlock_irq(&phba->hbalock); return; } @@ -3682,7 +3682,8 @@ lpfc_offline_prep(struct lpfc_hba *phba, int mbx_action) struct lpfc_vport **vports; struct Scsi_Host *shost; int i; - int offline = 0; + int offline; + bool hba_pci_err; if (vport->fc_flag & FC_OFFLINE_MODE) return; @@ -3692,6 +3693,7 @@ lpfc_offline_prep(struct lpfc_hba *phba, int mbx_action) lpfc_linkdown(phba); offline = pci_channel_offline(phba->pcidev); + hba_pci_err = test_bit(HBA_PCI_ERR, &phba->bit_flags); /* Issue an unreg_login to all nodes on all vports */ vports = lpfc_create_vport_work_array(phba); @@ -3715,11 +3717,14 @@ lpfc_offline_prep(struct lpfc_hba *phba, int mbx_action) ndlp->nlp_flag &= ~NLP_NPR_ADISC; spin_unlock_irq(&ndlp->lock); - if (offline) { + if (offline || hba_pci_err) { spin_lock_irq(&ndlp->lock); ndlp->nlp_flag &= ~(NLP_UNREG_INP | NLP_RPI_REGISTERED); spin_unlock_irq(&ndlp->lock); + if (phba->sli_rev == LPFC_SLI_REV4) + lpfc_sli_rpi_release(vports[i], + ndlp); } else { lpfc_unreg_rpi(vports[i], ndlp); } @@ -13374,15 +13379,12 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) /* Disable FW logging to host memory */ lpfc_ras_stop_fwlog(phba); - /* Unset the queues shared with the hardware then release all - * allocated resources. - */ - lpfc_sli4_queue_unset(phba); - lpfc_sli4_queue_destroy(phba); - /* Reset SLI4 HBA FCoE function */ lpfc_pci_function_reset(phba); + /* release all queue allocated resources. */ + lpfc_sli4_queue_destroy(phba); + /* Free RAS DMA memory */ if (phba->ras_fwlog.ras_enabled) lpfc_sli4_ras_dma_free(phba); @@ -15057,24 +15059,28 @@ lpfc_sli4_prep_dev_for_recover(struct lpfc_hba *phba) static void lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba) { - lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, - "2826 PCI channel disable preparing for reset\n"); + int offline = pci_channel_offline(phba->pcidev); + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2826 PCI channel disable preparing for reset offline" + " %d\n", offline); /* Block any management I/Os to the device */ lpfc_block_mgmt_io(phba, LPFC_MBX_NO_WAIT); - /* Block all SCSI devices' I/Os on the host */ - lpfc_scsi_dev_block(phba); + /* HBA_PCI_ERR was set in io_error_detect */ + lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT); /* Flush all driver's outstanding I/Os as we are to reset */ lpfc_sli_flush_io_rings(phba); + lpfc_offline(phba); /* stop all timers */ lpfc_stop_hba_timers(phba); + lpfc_sli4_queue_destroy(phba); /* Disable interrupt and pci device */ lpfc_sli4_disable_intr(phba); - lpfc_sli4_queue_destroy(phba); pci_disable_device(phba->pcidev); } @@ -15123,6 +15129,7 @@ lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; + bool hba_pci_err; switch (state) { case pci_channel_io_normal: @@ -15130,17 +15137,24 @@ lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state) lpfc_sli4_prep_dev_for_recover(phba); return PCI_ERS_RESULT_CAN_RECOVER; case pci_channel_io_frozen: - phba->hba_flag |= HBA_PCI_ERR; + hba_pci_err = test_and_set_bit(HBA_PCI_ERR, &phba->bit_flags); /* Fatal error, prepare for slot reset */ - lpfc_sli4_prep_dev_for_reset(phba); + if (!hba_pci_err) + lpfc_sli4_prep_dev_for_reset(phba); + else + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2832 Already handling PCI error " + "state: x%x\n", state); return PCI_ERS_RESULT_NEED_RESET; case pci_channel_io_perm_failure: - phba->hba_flag |= HBA_PCI_ERR; + set_bit(HBA_PCI_ERR, &phba->bit_flags); /* Permanent failure, prepare for device down */ lpfc_sli4_prep_dev_for_perm_failure(phba); return PCI_ERS_RESULT_DISCONNECT; default: - phba->hba_flag |= HBA_PCI_ERR; + hba_pci_err = test_and_set_bit(HBA_PCI_ERR, &phba->bit_flags); + if (!hba_pci_err) + lpfc_sli4_prep_dev_for_reset(phba); /* Unknown state, prepare and request slot reset */ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "2825 Unknown PCI error state: x%x\n", state); @@ -15174,17 +15188,21 @@ lpfc_io_slot_reset_s4(struct pci_dev *pdev) struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; struct lpfc_sli *psli = &phba->sli; uint32_t intr_mode; + bool hba_pci_err; dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n"); if (pci_enable_device_mem(pdev)) { printk(KERN_ERR "lpfc: Cannot re-enable " - "PCI device after reset.\n"); + "PCI device after reset.\n"); return PCI_ERS_RESULT_DISCONNECT; } pci_restore_state(pdev); - phba->hba_flag &= ~HBA_PCI_ERR; + hba_pci_err = test_and_clear_bit(HBA_PCI_ERR, &phba->bit_flags); + if (!hba_pci_err) + dev_info(&pdev->dev, + "hba_pci_err was not set, recovering slot reset.\n"); /* * As the new kernel behavior of pci_restore_state() API call clears * device saved_state flag, need to save the restored state again. @@ -15239,8 +15257,6 @@ lpfc_io_resume_s4(struct pci_dev *pdev) */ if (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE)) { /* Perform device reset */ - lpfc_offline_prep(phba, LPFC_MBX_WAIT); - lpfc_offline(phba); lpfc_sli_brdrestart(phba); /* Bring the device back online */ lpfc_online(phba); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 1213a299f9aa..e47205e0d3e2 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -2169,8 +2169,7 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, abts_nvme = 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { qp = &phba->sli4_hba.hdwq[i]; - if (!vport || !vport->localport || - !qp || !qp->io_wq) + if (!vport->localport || !qp || !qp->io_wq) return; pring = qp->io_wq->pring; @@ -2180,8 +2179,9 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, abts_scsi += qp->abts_scsi_io_bufs; abts_nvme += qp->abts_nvme_io_bufs; } - if (!vport || !vport->localport || - vport->phba->hba_flag & HBA_PCI_ERR) + if (!vport->localport || + test_bit(HBA_PCI_ERR, &vport->phba->bit_flags) || + vport->load_flag & FC_UNLOADING) return; lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, @@ -2541,8 +2541,7 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * return values is ignored. The upcall is a courtesy to the * transport. */ - if (vport->load_flag & FC_UNLOADING || - unlikely(vport->phba->hba_flag & HBA_PCI_ERR)) + if (vport->load_flag & FC_UNLOADING) (void)nvme_fc_set_remoteport_devloss(remoteport, 0); ret = nvme_fc_unregister_remoteport(remoteport); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 20d40957a385..26f6a147b5ae 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -2828,6 +2828,12 @@ __lpfc_sli_rpi_release(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) ndlp->nlp_flag &= ~NLP_UNREG_INP; } +void +lpfc_sli_rpi_release(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) +{ + __lpfc_sli_rpi_release(vport, ndlp); +} + /** * lpfc_sli_def_mbox_cmpl - Default mailbox completion handler * @phba: Pointer to HBA context object. @@ -4624,11 +4630,6 @@ lpfc_sli_flush_io_rings(struct lpfc_hba *phba) struct lpfc_iocbq *piocb, *next_iocb; spin_lock_irq(&phba->hbalock); - if (phba->hba_flag & HBA_IOQ_FLUSH || - !phba->sli4_hba.hdwq) { - spin_unlock_irq(&phba->hbalock); - return; - } /* Indicate the I/O queues are flushed */ phba->hba_flag |= HBA_IOQ_FLUSH; spin_unlock_irq(&phba->hbalock); @@ -10997,6 +10998,10 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, unsigned long iflags; int rc; + /* If the PCI channel is in offline state, do not post iocbs. */ + if (unlikely(pci_channel_offline(phba->pcidev))) + return IOCB_ERROR; + if (phba->sli_rev == LPFC_SLI_REV4) { lpfc_sli_prep_wqe(phba, piocb); -- cgit From a4691038b4071ff0d9ae486d8822a2c0d41d5796 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Mar 2022 20:27:35 -0700 Subject: scsi: lpfc: Fix unload hang after back to back PCI EEH faults When injecting EEH errors the port is getting hung up waiting on the node list to empty, message number 0233. The driver is stuck at this point and also can't unload. The driver makes transport remoteport delete calls which try to abort I/O's, but the EEH daemon has already called the driver to detach and the detachment has set the global FC_UNLOADING flag. There are several code paths that will avoid I/O cleanup if the FC_UNLOADING flag is set, resulting in transports waiting for I/O while the driver is waiting on transports to clean up. Additionally, during study of the list, a locking issue was found in lpfc_sli_abort_iocb_ring that could corrupt the list. A special case was added to the lpfc_cleanup() routine to call lpfc_sli_flush_rings() if the driver is FC_UNLOADING and if the pci-slot is offline (e.g. EEH). The SLI4 part of lpfc_sli_abort_iocb_ring() is changed to use the ring_lock. Also added code to cancel the I/Os if the pci-slot is offline and added checks and returns for the FC_UNLOADING and HBA_IOQ_FLUSH flags to prevent trying to send an I/O that we cannot handle. Link: https://lore.kernel.org/r/20220317032737.45308-3-jsmart2021@gmail.com Co-developed-by: Justin Tee Signed-off-by: Justin Tee Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hbadisc.c | 1 + drivers/scsi/lpfc/lpfc_init.c | 26 ++++++++++++++++++--- drivers/scsi/lpfc/lpfc_nvme.c | 16 ++++++++++--- drivers/scsi/lpfc/lpfc_sli.c | 50 ++++++++++++++++++++++++++++------------ 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 6983c70f2fc6..2b877dff5ed4 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -5422,6 +5422,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) ndlp->nlp_flag &= ~NLP_UNREG_INP; mempool_free(mbox, phba->mbox_mem_pool); acc_plogi = 1; + lpfc_nlp_put(ndlp); } } else { lpfc_printf_vlog(vport, KERN_INFO, diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index b8ab6dcbadc5..cf83bc0e27c0 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -95,6 +95,7 @@ static void lpfc_sli4_oas_verify(struct lpfc_hba *phba); static uint16_t lpfc_find_cpu_handle(struct lpfc_hba *, uint16_t, int); static void lpfc_setup_bg(struct lpfc_hba *, struct Scsi_Host *); static int lpfc_sli4_cgn_parm_chg_evt(struct lpfc_hba *); +static void lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba); static struct scsi_transport_template *lpfc_transport_template = NULL; static struct scsi_transport_template *lpfc_vport_transport_template = NULL; @@ -1985,6 +1986,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba) if (pci_channel_offline(phba->pcidev)) { lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "3166 pci channel is offline\n"); + lpfc_sli_flush_io_rings(phba); return; } @@ -2973,6 +2975,22 @@ lpfc_cleanup(struct lpfc_vport *vport) NLP_EVT_DEVICE_RM); } + /* This is a special case flush to return all + * IOs before entering this loop. There are + * two points in the code where a flush is + * avoided if the FC_UNLOADING flag is set. + * one is in the multipool destroy, + * (this prevents a crash) and the other is + * in the nvme abort handler, ( also prevents + * a crash). Both of these exceptions are + * cases where the slot is still accessible. + * The flush here is only when the pci slot + * is offline. + */ + if (vport->load_flag & FC_UNLOADING && + pci_channel_offline(phba->pcidev)) + lpfc_sli_flush_io_rings(vport->phba); + /* At this point, ALL ndlp's should be gone * because of the previous NLP_EVT_DEVICE_RM. * Lets wait for this to happen, if needed. @@ -2985,7 +3003,7 @@ lpfc_cleanup(struct lpfc_vport *vport) list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { lpfc_printf_vlog(ndlp->vport, KERN_ERR, - LOG_TRACE_EVENT, + LOG_DISCOVERY, "0282 did:x%x ndlp:x%px " "refcnt:%d xflags x%x nflag x%x\n", ndlp->nlp_DID, (void *)ndlp, @@ -13359,8 +13377,9 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) /* Abort all iocbs associated with the hba */ lpfc_sli_hba_iocb_abort(phba); - /* Wait for completion of device XRI exchange busy */ - lpfc_sli4_xri_exchange_busy_wait(phba); + if (!pci_channel_offline(phba->pcidev)) + /* Wait for completion of device XRI exchange busy */ + lpfc_sli4_xri_exchange_busy_wait(phba); /* per-phba callback de-registration for hotplug event */ if (phba->pport) @@ -14264,6 +14283,7 @@ lpfc_sli_prep_dev_for_perm_failure(struct lpfc_hba *phba) "2711 PCI channel permanent disable for failure\n"); /* Block all SCSI devices' I/Os on the host */ lpfc_scsi_dev_block(phba); + lpfc_sli4_prep_dev_for_reset(phba); /* stop all timers */ lpfc_stop_hba_timers(phba); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index e47205e0d3e2..8d26f207ebd2 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -93,6 +93,11 @@ lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport, lport = (struct lpfc_nvme_lport *)pnvme_lport->private; vport = lport->vport; + + if (!vport || vport->load_flag & FC_UNLOADING || + vport->phba->hba_flag & HBA_IOQ_FLUSH) + return -ENODEV; + qhandle = kzalloc(sizeof(struct lpfc_nvme_qhandle), GFP_KERNEL); if (qhandle == NULL) return -ENOMEM; @@ -267,7 +272,8 @@ lpfc_nvme_handle_lsreq(struct lpfc_hba *phba, return -EINVAL; remoteport = lpfc_rport->remoteport; - if (!vport->localport) + if (!vport->localport || + vport->phba->hba_flag & HBA_IOQ_FLUSH) return -EINVAL; lport = vport->localport->private; @@ -559,6 +565,8 @@ __lpfc_nvme_ls_req(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_DID, ntype, nstate); return -ENODEV; } + if (vport->phba->hba_flag & HBA_IOQ_FLUSH) + return -ENODEV; if (!vport->phba->sli4_hba.nvmels_wq) return -ENOMEM; @@ -662,7 +670,8 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, return -EINVAL; vport = lport->vport; - if (vport->load_flag & FC_UNLOADING) + if (vport->load_flag & FC_UNLOADING || + vport->phba->hba_flag & HBA_IOQ_FLUSH) return -ENODEV; atomic_inc(&lport->fc4NvmeLsRequests); @@ -1516,7 +1525,8 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, phba = vport->phba; - if (unlikely(vport->load_flag & FC_UNLOADING)) { + if ((unlikely(vport->load_flag & FC_UNLOADING)) || + phba->hba_flag & HBA_IOQ_FLUSH) { lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, "6124 Fail IO, Driver unload\n"); atomic_inc(&lport->xmt_fcp_err); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 26f6a147b5ae..70c3929a9fb4 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -4542,42 +4542,62 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba, void lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) { - LIST_HEAD(completions); + LIST_HEAD(tx_completions); + LIST_HEAD(txcmplq_completions); struct lpfc_iocbq *iocb, *next_iocb; + int offline; if (pring->ringno == LPFC_ELS_RING) { lpfc_fabric_abort_hba(phba); } + offline = pci_channel_offline(phba->pcidev); /* Error everything on txq and txcmplq * First do the txq. */ if (phba->sli_rev >= LPFC_SLI_REV4) { spin_lock_irq(&pring->ring_lock); - list_splice_init(&pring->txq, &completions); + list_splice_init(&pring->txq, &tx_completions); pring->txq_cnt = 0; - spin_unlock_irq(&pring->ring_lock); - spin_lock_irq(&phba->hbalock); - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) - lpfc_sli_issue_abort_iotag(phba, pring, iocb, NULL); - spin_unlock_irq(&phba->hbalock); + if (offline) { + list_splice_init(&pring->txcmplq, + &txcmplq_completions); + } else { + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, + &pring->txcmplq, list) + lpfc_sli_issue_abort_iotag(phba, pring, + iocb, NULL); + } + spin_unlock_irq(&pring->ring_lock); } else { spin_lock_irq(&phba->hbalock); - list_splice_init(&pring->txq, &completions); + list_splice_init(&pring->txq, &tx_completions); pring->txq_cnt = 0; - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) - lpfc_sli_issue_abort_iotag(phba, pring, iocb, NULL); + if (offline) { + list_splice_init(&pring->txcmplq, &txcmplq_completions); + } else { + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, + &pring->txcmplq, list) + lpfc_sli_issue_abort_iotag(phba, pring, + iocb, NULL); + } spin_unlock_irq(&phba->hbalock); } - /* Make sure HBA is alive */ - lpfc_issue_hb_tmo(phba); + if (offline) { + /* Cancel all the IOCBs from the completions list */ + lpfc_sli_cancel_iocbs(phba, &txcmplq_completions, + IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); + } else { + /* Make sure HBA is alive */ + lpfc_issue_hb_tmo(phba); + } /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, + lpfc_sli_cancel_iocbs(phba, &tx_completions, IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); } -- cgit From df0101197c4d9596682901631f3ee193ed354873 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Mar 2022 20:27:36 -0700 Subject: scsi: lpfc: Fix queue failures when recovering from PCI parity error When recovering from a pci-parity error the driver is failing to re-create queues, causing recovery to fail. Looking deeper, it was found that the interrupt vector count allocated on the recovery was fewer than the vectors originally allocated. This disparity resulted in CPU map entries with stale information. When the driver tries to re-create the queues, it attempts to use the stale information which indicates an eq/interrupt vector that was no longer created. Fix by clearng the cpup map array before enabling and requesting the IRQs in the lpfc_sli_reset_slot_s4 routine(). Link: https://lore.kernel.org/r/20220317032737.45308-4-jsmart2021@gmail.com Co-developed-by: Justin Tee Signed-off-by: Justin Tee Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index cf83bc0e27c0..461d333b1b3a 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -15236,6 +15236,8 @@ lpfc_io_slot_reset_s4(struct pci_dev *pdev) psli->sli_flag &= ~LPFC_SLI_ACTIVE; spin_unlock_irq(&phba->hbalock); + /* Init cpu_map array */ + lpfc_cpu_map_array_init(phba); /* Configure and enable interrupt */ intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode); if (intr_mode == LPFC_INTR_ERROR) { -- cgit From 4f3beb36b1e46f1a04967a84e3a1bd55bf7a9e7a Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Mar 2022 20:27:37 -0700 Subject: scsi: lpfc: Update lpfc version to 14.2.0.1 Update lpfc version to 14.2.0.1 Link: https://lore.kernel.org/r/20220317032737.45308-5-jsmart2021@gmail.com Co-developed-by: Justin Tee Signed-off-by: Justin Tee Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index e52f37e5d896..a4d3259b8c52 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "14.2.0.0" +#define LPFC_DRIVER_VERSION "14.2.0.1" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ -- cgit From 7294a9bcaa7ee0d3b96aab1a277317315fd46f09 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 23 Mar 2022 13:55:44 -0700 Subject: scsi: lpfc: Fix broken SLI4 abort path There was a merge error in ther 14.2.0.0 patches that resulted in the SLI4 path using the SLI3 issue_abort_iotag() routine. This resulted in txcmplq corruption. Fix to use the SLI4 routine when SLI4. Link: https://lore.kernel.org/r/20220323205545.81814-2-jsmart2021@gmail.com Fixes: 31a59f75702f ("scsi: lpfc: SLI path split: Refactor Abort paths") Cc: # v5.2+ Co-developed-by: Dick Kennedy Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_scsi.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 3c132604fd91..ba9dbb51b75f 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -5929,13 +5929,15 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) } lpfc_cmd->waitq = &waitq; - if (phba->sli_rev == LPFC_SLI_REV4) + if (phba->sli_rev == LPFC_SLI_REV4) { spin_unlock(&pring_s4->ring_lock); - else + ret_val = lpfc_sli4_issue_abort_iotag(phba, iocb, + lpfc_sli_abort_fcp_cmpl); + } else { pring = &phba->sli.sli3_ring[LPFC_FCP_RING]; - - ret_val = lpfc_sli_issue_abort_iotag(phba, pring, iocb, - lpfc_sli_abort_fcp_cmpl); + ret_val = lpfc_sli_issue_abort_iotag(phba, pring, iocb, + lpfc_sli_abort_fcp_cmpl); + } /* Make sure HBA is alive */ lpfc_issue_hb_tmo(phba); -- cgit From c26bd6602e1d348bfa754dc55e5608c922dd2801 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 23 Mar 2022 13:55:45 -0700 Subject: scsi: lpfc: Fix locking for lpfc_sli_iocbq_lookup() The rules changed for lpfc_sli_iocbq_lookup() vs locking. Prior, the routine properly took out the lock. In newly refactored code, the locks must be held when calling the routine. Fix lpfc_sli_process_sol_iocb() to take the locks before calling the routine. Fix lpfc_sli_handle_fast_ring_event() to not release the locks to call the routine. Link: https://lore.kernel.org/r/20220323205545.81814-3-jsmart2021@gmail.com Fixes: 1b64aa9eae28 ("scsi: lpfc: SLI path split: Refactor fast and slow paths to native SLI4") Co-developed-by: Dick Kennedy Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_sli.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 70c3929a9fb4..bda2a7ba4e77 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -3721,7 +3721,15 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, unsigned long iflag; u32 ulp_command, ulp_status, ulp_word4, ulp_context, iotag; + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock_irqsave(&pring->ring_lock, iflag); + else + spin_lock_irqsave(&phba->hbalock, iflag); cmdiocbp = lpfc_sli_iocbq_lookup(phba, pring, saveq); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock_irqrestore(&pring->ring_lock, iflag); + else + spin_unlock_irqrestore(&phba->hbalock, iflag); ulp_command = get_job_cmnd(phba, saveq); ulp_status = get_job_ulpstatus(phba, saveq); @@ -4058,10 +4066,8 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba, break; } - spin_unlock_irqrestore(&phba->hbalock, iflag); cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring, &rspiocbq); - spin_lock_irqsave(&phba->hbalock, iflag); if (unlikely(!cmdiocbq)) break; if (cmdiocbq->cmd_flag & LPFC_DRIVER_ABORTED) -- cgit From 8ee15ea779c332f94d74d135212403f2d0defb5f Mon Sep 17 00:00:00 2001 From: Keoseong Park Date: Thu, 24 Mar 2022 16:01:46 +0900 Subject: scsi: ufs: core: Remove unused field in struct ufs_hba Remove unused fields 'rpm_lvl_attr' and 'spm_lvl_attr' in struct ufs_hba. Commit cbb6813ee771 ("scsi: ufs: sysfs: attribute group for existing sysfs entries.") removed all code using that field. Link: https://lore.kernel.org/r/413601558.101648105683746.JavaMail.epsvc@epcpadp4 Reviewed-by: Bart Van Assche Acked-by: Avri Altman Signed-off-by: Keoseong Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 88c20f3608c2..94f545be183a 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -820,8 +820,6 @@ struct ufs_hba { enum ufs_pm_level rpm_lvl; /* Desired UFS power management level during system PM */ enum ufs_pm_level spm_lvl; - struct device_attribute rpm_lvl_attr; - struct device_attribute spm_lvl_attr; int pm_op_in_progress; /* Auto-Hibernate Idle Timer register value */ -- cgit From f06aa52cb2723ec67e92df463827b800d6c477d1 Mon Sep 17 00:00:00 2001 From: Tomas Henzl Date: Thu, 24 Mar 2022 14:46:03 +0100 Subject: scsi: core: scsi_logging: Fix a BUG The request_queue may be NULL in a request, for example when it comes from scsi_ioctl_reset(). Check it before use. Fixes: f3fa33acca9f ("block: remove the ->rq_disk field in struct request") Link: https://lore.kernel.org/r/20220324134603.28463-1-thenzl@redhat.com Reported-by: Changhui Zhong Reviewed-by: Christoph Hellwig Signed-off-by: Tomas Henzl Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_logging.c b/drivers/scsi/scsi_logging.c index ff89de86545d..b02af340c2d3 100644 --- a/drivers/scsi/scsi_logging.c +++ b/drivers/scsi/scsi_logging.c @@ -30,7 +30,7 @@ static inline const char *scmd_name(const struct scsi_cmnd *scmd) { struct request *rq = scsi_cmd_to_rq((struct scsi_cmnd *)scmd); - if (!rq->q->disk) + if (!rq->q || !rq->q->disk) return NULL; return rq->q->disk->disk_name; } -- cgit From f16aa285e6185271bc6812a176f7ae3dbd7fe28d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 26 Mar 2022 13:48:15 +0100 Subject: scsi: pmcraid: Remove the PMCRAID_PASSTHROUGH_IOCTL ioctl implementation The whole passthrough ioctl path looks completely broken. For example it DMA maps the scatterlist and after that copies data to it, which is prohibited by the DMA API contract. Moreover, in pmcraid_alloc_sglist(), the pointer returned by a sgl_alloc_order() call is not recorded anywhere which is pointless. So remove the PMCRAID_PASSTHROUGH_IOCTL ioctl implementation entirely. Should it be needed, we should reimplement it using the proper block layer request mapping helpers. Link: https://lore.kernel.org/r/7f27a70bec3f3dcaf46a29b1c630edd4792e71c0.1648298857.git.christophe.jaillet@wanadoo.fr Suggested-by: Christoph Hellwig Reviewed-by: Christoph Hellwig Signed-off-by: Christophe JAILLET Signed-off-by: Martin K. Petersen --- drivers/scsi/pmcraid.c | 491 ------------------------------------------------- drivers/scsi/pmcraid.h | 33 ---- 2 files changed, 524 deletions(-) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 928532180d32..fd674ed1febe 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3181,124 +3181,6 @@ static int pmcraid_build_ioadl( return 0; } -/** - * pmcraid_free_sglist - Frees an allocated SG buffer list - * @sglist: scatter/gather list pointer - * - * Free a DMA'able memory previously allocated with pmcraid_alloc_sglist - * - * Return value: - * none - */ -static void pmcraid_free_sglist(struct pmcraid_sglist *sglist) -{ - sgl_free_order(sglist->scatterlist, sglist->order); - kfree(sglist); -} - -/** - * pmcraid_alloc_sglist - Allocates memory for a SG list - * @buflen: buffer length - * - * Allocates a DMA'able buffer in chunks and assembles a scatter/gather - * list. - * - * Return value - * pointer to sglist / NULL on failure - */ -static struct pmcraid_sglist *pmcraid_alloc_sglist(int buflen) -{ - struct pmcraid_sglist *sglist; - int sg_size; - int order; - - sg_size = buflen / (PMCRAID_MAX_IOADLS - 1); - order = (sg_size > 0) ? get_order(sg_size) : 0; - - /* Allocate a scatter/gather list for the DMA */ - sglist = kzalloc(sizeof(struct pmcraid_sglist), GFP_KERNEL); - if (sglist == NULL) - return NULL; - - sglist->order = order; - sgl_alloc_order(buflen, order, false, GFP_KERNEL | __GFP_ZERO, - &sglist->num_sg); - - return sglist; -} - -/** - * pmcraid_copy_sglist - Copy user buffer to kernel buffer's SG list - * @sglist: scatter/gather list pointer - * @buffer: buffer pointer - * @len: buffer length - * @direction: data transfer direction - * - * Copy a user buffer into a buffer allocated by pmcraid_alloc_sglist - * - * Return value: - * 0 on success / other on failure - */ -static int pmcraid_copy_sglist( - struct pmcraid_sglist *sglist, - void __user *buffer, - u32 len, - int direction -) -{ - struct scatterlist *sg; - void *kaddr; - int bsize_elem; - int i; - int rc = 0; - - /* Determine the actual number of bytes per element */ - bsize_elem = PAGE_SIZE * (1 << sglist->order); - - sg = sglist->scatterlist; - - for (i = 0; i < (len / bsize_elem); i++, sg = sg_next(sg), buffer += bsize_elem) { - struct page *page = sg_page(sg); - - kaddr = kmap(page); - if (direction == DMA_TO_DEVICE) - rc = copy_from_user(kaddr, buffer, bsize_elem); - else - rc = copy_to_user(buffer, kaddr, bsize_elem); - - kunmap(page); - - if (rc) { - pmcraid_err("failed to copy user data into sg list\n"); - return -EFAULT; - } - - sg->length = bsize_elem; - } - - if (len % bsize_elem) { - struct page *page = sg_page(sg); - - kaddr = kmap(page); - - if (direction == DMA_TO_DEVICE) - rc = copy_from_user(kaddr, buffer, len % bsize_elem); - else - rc = copy_to_user(buffer, kaddr, len % bsize_elem); - - kunmap(page); - - sg->length = len % bsize_elem; - } - - if (rc) { - pmcraid_err("failed to copy user data into sg list\n"); - rc = -EFAULT; - } - - return rc; -} - /** * pmcraid_queuecommand_lck - Queue a mid-layer request * @scsi_cmd: scsi command struct @@ -3454,365 +3336,6 @@ static int pmcraid_chr_fasync(int fd, struct file *filep, int mode) return rc; } - -/** - * pmcraid_build_passthrough_ioadls - builds SG elements for passthrough - * commands sent over IOCTL interface - * - * @cmd : pointer to struct pmcraid_cmd - * @buflen : length of the request buffer - * @direction : data transfer direction - * - * Return value - * 0 on success, non-zero error code on failure - */ -static int pmcraid_build_passthrough_ioadls( - struct pmcraid_cmd *cmd, - int buflen, - int direction -) -{ - struct pmcraid_sglist *sglist = NULL; - struct scatterlist *sg = NULL; - struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; - struct pmcraid_ioadl_desc *ioadl; - int i; - - sglist = pmcraid_alloc_sglist(buflen); - - if (!sglist) { - pmcraid_err("can't allocate memory for passthrough SGls\n"); - return -ENOMEM; - } - - sglist->num_dma_sg = dma_map_sg(&cmd->drv_inst->pdev->dev, - sglist->scatterlist, - sglist->num_sg, direction); - - if (!sglist->num_dma_sg || sglist->num_dma_sg > PMCRAID_MAX_IOADLS) { - dev_err(&cmd->drv_inst->pdev->dev, - "Failed to map passthrough buffer!\n"); - pmcraid_free_sglist(sglist); - return -EIO; - } - - cmd->sglist = sglist; - ioarcb->request_flags0 |= NO_LINK_DESCS; - - ioadl = pmcraid_init_ioadls(cmd, sglist->num_dma_sg); - - /* Initialize IOADL descriptor addresses */ - for_each_sg(sglist->scatterlist, sg, sglist->num_dma_sg, i) { - ioadl[i].data_len = cpu_to_le32(sg_dma_len(sg)); - ioadl[i].address = cpu_to_le64(sg_dma_address(sg)); - ioadl[i].flags = 0; - } - - /* setup the last descriptor */ - ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC; - - return 0; -} - - -/** - * pmcraid_release_passthrough_ioadls - release passthrough ioadls - * - * @cmd: pointer to struct pmcraid_cmd for which ioadls were allocated - * @buflen: size of the request buffer - * @direction: data transfer direction - * - * Return value - * 0 on success, non-zero error code on failure - */ -static void pmcraid_release_passthrough_ioadls( - struct pmcraid_cmd *cmd, - int buflen, - int direction -) -{ - struct pmcraid_sglist *sglist = cmd->sglist; - - if (buflen > 0) { - dma_unmap_sg(&cmd->drv_inst->pdev->dev, - sglist->scatterlist, - sglist->num_sg, - direction); - pmcraid_free_sglist(sglist); - cmd->sglist = NULL; - } -} - -/** - * pmcraid_ioctl_passthrough - handling passthrough IOCTL commands - * - * @pinstance: pointer to adapter instance structure - * @ioctl_cmd: ioctl code - * @buflen: unused - * @arg: pointer to pmcraid_passthrough_buffer user buffer - * - * Return value - * 0 on success, non-zero error code on failure - */ -static long pmcraid_ioctl_passthrough( - struct pmcraid_instance *pinstance, - unsigned int ioctl_cmd, - unsigned int buflen, - void __user *arg -) -{ - struct pmcraid_passthrough_ioctl_buffer *buffer; - struct pmcraid_ioarcb *ioarcb; - struct pmcraid_cmd *cmd; - struct pmcraid_cmd *cancel_cmd; - void __user *request_buffer; - unsigned long request_offset; - unsigned long lock_flags; - void __user *ioasa; - u32 ioasc; - int request_size; - int buffer_size; - u8 direction; - int rc = 0; - - /* If IOA reset is in progress, wait 10 secs for reset to complete */ - if (pinstance->ioa_reset_in_progress) { - rc = wait_event_interruptible_timeout( - pinstance->reset_wait_q, - !pinstance->ioa_reset_in_progress, - msecs_to_jiffies(10000)); - - if (!rc) - return -ETIMEDOUT; - else if (rc < 0) - return -ERESTARTSYS; - } - - /* If adapter is not in operational state, return error */ - if (pinstance->ioa_state != IOA_STATE_OPERATIONAL) { - pmcraid_err("IOA is not operational\n"); - return -ENOTTY; - } - - buffer_size = sizeof(struct pmcraid_passthrough_ioctl_buffer); - buffer = kmalloc(buffer_size, GFP_KERNEL); - - if (!buffer) { - pmcraid_err("no memory for passthrough buffer\n"); - return -ENOMEM; - } - - request_offset = - offsetof(struct pmcraid_passthrough_ioctl_buffer, request_buffer); - - request_buffer = arg + request_offset; - - rc = copy_from_user(buffer, arg, - sizeof(struct pmcraid_passthrough_ioctl_buffer)); - - ioasa = arg + offsetof(struct pmcraid_passthrough_ioctl_buffer, ioasa); - - if (rc) { - pmcraid_err("ioctl: can't copy passthrough buffer\n"); - rc = -EFAULT; - goto out_free_buffer; - } - - request_size = le32_to_cpu(buffer->ioarcb.data_transfer_length); - - if (buffer->ioarcb.request_flags0 & TRANSFER_DIR_WRITE) { - direction = DMA_TO_DEVICE; - } else { - direction = DMA_FROM_DEVICE; - } - - if (request_size < 0) { - rc = -EINVAL; - goto out_free_buffer; - } - - /* check if we have any additional command parameters */ - if (le16_to_cpu(buffer->ioarcb.add_cmd_param_length) - > PMCRAID_ADD_CMD_PARAM_LEN) { - rc = -EINVAL; - goto out_free_buffer; - } - - cmd = pmcraid_get_free_cmd(pinstance); - - if (!cmd) { - pmcraid_err("free command block is not available\n"); - rc = -ENOMEM; - goto out_free_buffer; - } - - cmd->scsi_cmd = NULL; - ioarcb = &(cmd->ioa_cb->ioarcb); - - /* Copy the user-provided IOARCB stuff field by field */ - ioarcb->resource_handle = buffer->ioarcb.resource_handle; - ioarcb->data_transfer_length = buffer->ioarcb.data_transfer_length; - ioarcb->cmd_timeout = buffer->ioarcb.cmd_timeout; - ioarcb->request_type = buffer->ioarcb.request_type; - ioarcb->request_flags0 = buffer->ioarcb.request_flags0; - ioarcb->request_flags1 = buffer->ioarcb.request_flags1; - memcpy(ioarcb->cdb, buffer->ioarcb.cdb, PMCRAID_MAX_CDB_LEN); - - if (buffer->ioarcb.add_cmd_param_length) { - ioarcb->add_cmd_param_length = - buffer->ioarcb.add_cmd_param_length; - ioarcb->add_cmd_param_offset = - buffer->ioarcb.add_cmd_param_offset; - memcpy(ioarcb->add_data.u.add_cmd_params, - buffer->ioarcb.add_data.u.add_cmd_params, - le16_to_cpu(buffer->ioarcb.add_cmd_param_length)); - } - - /* set hrrq number where the IOA should respond to. Note that all cmds - * generated internally uses hrrq_id 0, exception to this is the cmd - * block of scsi_cmd which is re-used (e.g. cancel/abort), which uses - * hrrq_id assigned here in queuecommand - */ - ioarcb->hrrq_id = atomic_add_return(1, &(pinstance->last_message_id)) % - pinstance->num_hrrq; - - if (request_size) { - rc = pmcraid_build_passthrough_ioadls(cmd, - request_size, - direction); - if (rc) { - pmcraid_err("couldn't build passthrough ioadls\n"); - goto out_free_cmd; - } - } - - /* If data is being written into the device, copy the data from user - * buffers - */ - if (direction == DMA_TO_DEVICE && request_size > 0) { - rc = pmcraid_copy_sglist(cmd->sglist, - request_buffer, - request_size, - direction); - if (rc) { - pmcraid_err("failed to copy user buffer\n"); - goto out_free_sglist; - } - } - - /* passthrough ioctl is a blocking command so, put the user to sleep - * until timeout. Note that a timeout value of 0 means, do timeout. - */ - cmd->cmd_done = pmcraid_internal_done; - init_completion(&cmd->wait_for_completion); - cmd->completion_req = 1; - - pmcraid_info("command(%d) (CDB[0] = %x) for %x\n", - le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2, - cmd->ioa_cb->ioarcb.cdb[0], - le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle)); - - spin_lock_irqsave(pinstance->host->host_lock, lock_flags); - _pmcraid_fire_command(cmd); - spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); - - /* NOTE ! Remove the below line once abort_task is implemented - * in firmware. This line disables ioctl command timeout handling logic - * similar to IO command timeout handling, making ioctl commands to wait - * until the command completion regardless of timeout value specified in - * ioarcb - */ - buffer->ioarcb.cmd_timeout = 0; - - /* If command timeout is specified put caller to wait till that time, - * otherwise it would be blocking wait. If command gets timed out, it - * will be aborted. - */ - if (buffer->ioarcb.cmd_timeout == 0) { - wait_for_completion(&cmd->wait_for_completion); - } else if (!wait_for_completion_timeout( - &cmd->wait_for_completion, - msecs_to_jiffies(le16_to_cpu(buffer->ioarcb.cmd_timeout) * 1000))) { - - pmcraid_info("aborting cmd %d (CDB[0] = %x) due to timeout\n", - le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2, - cmd->ioa_cb->ioarcb.cdb[0]); - - spin_lock_irqsave(pinstance->host->host_lock, lock_flags); - cancel_cmd = pmcraid_abort_cmd(cmd); - spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); - - if (cancel_cmd) { - wait_for_completion(&cancel_cmd->wait_for_completion); - ioasc = le32_to_cpu(cancel_cmd->ioa_cb->ioasa.ioasc); - pmcraid_return_cmd(cancel_cmd); - - /* if abort task couldn't find the command i.e it got - * completed prior to aborting, return good completion. - * if command got aborted successfully or there was IOA - * reset due to abort task itself getting timedout then - * return -ETIMEDOUT - */ - if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET || - PMCRAID_IOASC_SENSE_KEY(ioasc) == 0x00) { - if (ioasc != PMCRAID_IOASC_GC_IOARCB_NOTFOUND) - rc = -ETIMEDOUT; - goto out_handle_response; - } - } - - /* no command block for abort task or abort task failed to abort - * the IOARCB, then wait for 150 more seconds and initiate reset - * sequence after timeout - */ - if (!wait_for_completion_timeout( - &cmd->wait_for_completion, - msecs_to_jiffies(150 * 1000))) { - pmcraid_reset_bringup(cmd->drv_inst); - rc = -ETIMEDOUT; - } - } - -out_handle_response: - /* copy entire IOASA buffer and return IOCTL success. - * If copying IOASA to user-buffer fails, return - * EFAULT - */ - if (copy_to_user(ioasa, &cmd->ioa_cb->ioasa, - sizeof(struct pmcraid_ioasa))) { - pmcraid_err("failed to copy ioasa buffer to user\n"); - rc = -EFAULT; - } - - /* If the data transfer was from device, copy the data onto user - * buffers - */ - else if (direction == DMA_FROM_DEVICE && request_size > 0) { - rc = pmcraid_copy_sglist(cmd->sglist, - request_buffer, - request_size, - direction); - if (rc) { - pmcraid_err("failed to copy user buffer\n"); - rc = -EFAULT; - } - } - -out_free_sglist: - pmcraid_release_passthrough_ioadls(cmd, request_size, direction); - -out_free_cmd: - pmcraid_return_cmd(cmd); - -out_free_buffer: - kfree(buffer); - - return rc; -} - - - - /** * pmcraid_ioctl_driver - ioctl handler for commands handled by driver itself * @@ -3922,20 +3445,6 @@ static long pmcraid_chr_ioctl( switch (_IOC_TYPE(cmd)) { - case PMCRAID_PASSTHROUGH_IOCTL: - /* If ioctl code is to download microcode, we need to block - * mid-layer requests. - */ - if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE) - scsi_block_requests(pinstance->host); - - retval = pmcraid_ioctl_passthrough(pinstance, cmd, - hdr->buffer_length, argp); - - if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE) - scsi_unblock_requests(pinstance->host); - break; - case PMCRAID_DRIVER_IOCTL: arg += sizeof(struct pmcraid_ioctl_header); retval = pmcraid_ioctl_driver(pinstance, cmd, diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index bbb75318f1e7..9f59930e8b4f 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -1022,41 +1022,16 @@ struct pmcraid_ioctl_header { #define PMCRAID_IOCTL_SIGNATURE "PMCRAID" -/* - * pmcraid_passthrough_ioctl_buffer - structure given as argument to - * passthrough(or firmware handled) IOCTL commands. Note that ioarcb requires - * 32-byte alignment so, it is necessary to pack this structure to avoid any - * holes between ioctl_header and passthrough buffer - * - * .ioactl_header : ioctl header - * .ioarcb : filled-up ioarcb buffer, driver always reads this buffer - * .ioasa : buffer for ioasa, driver fills this with IOASA from firmware - * .request_buffer: The I/O buffer (flat), driver reads/writes to this based on - * the transfer directions passed in ioarcb.flags0. Contents - * of this buffer are valid only when ioarcb.data_transfer_len - * is not zero. - */ -struct pmcraid_passthrough_ioctl_buffer { - struct pmcraid_ioctl_header ioctl_header; - struct pmcraid_ioarcb ioarcb; - struct pmcraid_ioasa ioasa; - u8 request_buffer[]; -} __attribute__ ((packed, aligned(PMCRAID_IOARCB_ALIGNMENT))); - /* * keys to differentiate between driver handled IOCTLs and passthrough * IOCTLs passed to IOA. driver determines the ioctl type using macro * _IOC_TYPE */ #define PMCRAID_DRIVER_IOCTL 'D' -#define PMCRAID_PASSTHROUGH_IOCTL 'F' #define DRV_IOCTL(n, size) \ _IOC(_IOC_READ|_IOC_WRITE, PMCRAID_DRIVER_IOCTL, (n), (size)) -#define FMW_IOCTL(n, size) \ - _IOC(_IOC_READ|_IOC_WRITE, PMCRAID_PASSTHROUGH_IOCTL, (n), (size)) - /* * _ARGSIZE: macro that gives size of the argument type passed to an IOCTL cmd. * This is to facilitate applications avoiding un-necessary memory allocations. @@ -1069,12 +1044,4 @@ struct pmcraid_passthrough_ioctl_buffer { #define PMCRAID_IOCTL_RESET_ADAPTER \ DRV_IOCTL(5, sizeof(struct pmcraid_ioctl_header)) -/* passthrough/firmware handled commands */ -#define PMCRAID_IOCTL_PASSTHROUGH_COMMAND \ - FMW_IOCTL(1, sizeof(struct pmcraid_passthrough_ioctl_buffer)) - -#define PMCRAID_IOCTL_DOWNLOAD_MICROCODE \ - FMW_IOCTL(2, sizeof(struct pmcraid_passthrough_ioctl_buffer)) - - #endif /* _PMCRAID_H */ -- cgit From bc5519c18a32ce855bb51b9f5eceb77a9489d080 Mon Sep 17 00:00:00 2001 From: Kevin Groeneveld Date: Tue, 22 Mar 2022 20:22:42 -0400 Subject: scsi: sr: Fix typo in CDROM(CLOSETRAY|EJECT) handling Commit 2e27f576abc6 ("scsi: scsi_ioctl: Call scsi_cmd_ioctl() from scsi_ioctl()") seems to have a typo as it is checking ret instead of cmd in the if statement checking for CDROMCLOSETRAY and CDROMEJECT. This changes the behaviour of these ioctls as the cdrom_ioctl handling of these is more restrictive than the scsi_ioctl version. Link: https://lore.kernel.org/r/20220323002242.21157-1-kgroeneveld@lenbrook.com Fixes: 2e27f576abc6 ("scsi: scsi_ioctl: Call scsi_cmd_ioctl() from scsi_ioctl()") Reviewed-by: Christoph Hellwig Signed-off-by: Kevin Groeneveld Signed-off-by: Martin K. Petersen --- drivers/scsi/sr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index aaa54ad5f035..f232514f5a2b 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -578,7 +578,7 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, scsi_autopm_get_device(sdev); - if (ret != CDROMCLOSETRAY && ret != CDROMEJECT) { + if (cmd != CDROMCLOSETRAY && cmd != CDROMEJECT) { ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, arg); if (ret != -ENOSYS) goto put; -- cgit From 0bade8e53279157c7cc9dd95d573b7e82223d78a Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Tue, 22 Mar 2022 12:44:43 -0700 Subject: scsi: ibmvscsis: Increase INITIAL_SRP_LIMIT to 1024 The adapter request_limit is hardcoded to be INITIAL_SRP_LIMIT which is currently an arbitrary value of 800. Increase this value to 1024 which better matches the characteristics of the typical IBMi Initiator that supports 32 LUNs and a queue depth of 32. This change also has the secondary benefit of being a power of two as required by the kfifo API. Since, Commit ab9bb6318b09 ("Partially revert "kfifo: fix kfifo_alloc() and kfifo_init()"") the size of IU pool for each target has been rounded down to 512 when attempting to kfifo_init() those pools with the current request_limit size of 800. Link: https://lore.kernel.org/r/20220322194443.678433-1-tyreld@linux.ibm.com Signed-off-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index 80238e6a3c98..eee1a24f7e15 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -36,7 +36,7 @@ #define IBMVSCSIS_VERSION "v0.2" -#define INITIAL_SRP_LIMIT 800 +#define INITIAL_SRP_LIMIT 1024 #define DEFAULT_MAX_SECTORS 256 #define MAX_TXU 1024 * 1024 -- cgit From 63221571ef77c71a37d3c604d604168ce1de5cab Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Thu, 24 Mar 2022 08:29:37 +1100 Subject: scsi: aha152x: Stop using struct scsi_pointer Remove aha152x_cmd_priv.scsi_pointer by moving the necessary members into aha152x_cmd_priv proper. Tested with an Adaptec SlimSCSI APA-1460A card. Link: https://lore.kernel.org/r/bdc1264b6dd331150bffb737958cab8c9c068fa1.1648070977.git.fthain@linux-m68k.org Cc: Christoph Hellwig Suggested-by: Christoph Hellwig Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Finn Thain Signed-off-by: Martin K. Petersen --- drivers/scsi/aha152x.c | 235 ++++++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 122 deletions(-) diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index 5f554a3a0f62..caeebfb67149 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -317,14 +317,18 @@ enum { }; struct aha152x_cmd_priv { - struct scsi_pointer scsi_pointer; + char *ptr; + int this_residual; + struct scatterlist *buffer; + int status; + int message; + int sent_command; + int phase; }; -static struct scsi_pointer *aha152x_scsi_pointer(struct scsi_cmnd *cmd) +static struct aha152x_cmd_priv *aha152x_priv(struct scsi_cmnd *cmd) { - struct aha152x_cmd_priv *acmd = scsi_cmd_priv(cmd); - - return &acmd->scsi_pointer; + return scsi_cmd_priv(cmd); } MODULE_AUTHOR("Jürgen Fischer"); @@ -890,17 +894,16 @@ void aha152x_release(struct Scsi_Host *shpnt) static int setup_expected_interrupts(struct Scsi_Host *shpnt) { if(CURRENT_SC) { - struct scsi_pointer *scsi_pointer = - aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); - scsi_pointer->phase |= 1 << 16; + acp->phase |= 1 << 16; - if (scsi_pointer->phase & selecting) { + if (acp->phase & selecting) { SETPORT(SSTAT1, SELTO); SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0)); SETPORT(SIMODE1, ENSELTIMO); } else { - SETPORT(SIMODE0, (scsi_pointer->phase & spiordy) ? ENSPIORDY : 0); + SETPORT(SIMODE0, (acp->phase & spiordy) ? ENSPIORDY : 0); SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); } } else if(STATE==seldi) { @@ -924,17 +927,16 @@ static int setup_expected_interrupts(struct Scsi_Host *shpnt) static int aha152x_internal_queue(struct scsi_cmnd *SCpnt, struct completion *complete, int phase) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(SCpnt); + struct aha152x_cmd_priv *acp = aha152x_priv(SCpnt); struct Scsi_Host *shpnt = SCpnt->device->host; unsigned long flags; - scsi_pointer->phase = not_issued | phase; - scsi_pointer->Status = 0x1; /* Ilegal status by SCSI standard */ - scsi_pointer->Message = 0; - scsi_pointer->have_data_in = 0; - scsi_pointer->sent_command = 0; + acp->phase = not_issued | phase; + acp->status = 0x1; /* Illegal status by SCSI standard */ + acp->message = 0; + acp->sent_command = 0; - if (scsi_pointer->phase & (resetting | check_condition)) { + if (acp->phase & (resetting | check_condition)) { if (!SCpnt->host_scribble || SCSEM(SCpnt) || SCNEXT(SCpnt)) { scmd_printk(KERN_ERR, SCpnt, "cannot reuse command\n"); return FAILED; @@ -957,15 +959,15 @@ static int aha152x_internal_queue(struct scsi_cmnd *SCpnt, SCp.phase : current state of the command */ if ((phase & resetting) || !scsi_sglist(SCpnt)) { - scsi_pointer->ptr = NULL; - scsi_pointer->this_residual = 0; + acp->ptr = NULL; + acp->this_residual = 0; scsi_set_resid(SCpnt, 0); - scsi_pointer->buffer = NULL; + acp->buffer = NULL; } else { scsi_set_resid(SCpnt, scsi_bufflen(SCpnt)); - scsi_pointer->buffer = scsi_sglist(SCpnt); - scsi_pointer->ptr = SG_ADDRESS(scsi_pointer->buffer); - scsi_pointer->this_residual = scsi_pointer->buffer->length; + acp->buffer = scsi_sglist(SCpnt); + acp->ptr = SG_ADDRESS(acp->buffer); + acp->this_residual = acp->buffer->length; } DO_LOCK(flags); @@ -1015,7 +1017,7 @@ static void reset_done(struct scsi_cmnd *SCpnt) static void aha152x_scsi_done(struct scsi_cmnd *SCpnt) { - if (aha152x_scsi_pointer(SCpnt)->phase & resetting) + if (aha152x_priv(SCpnt)->phase & resetting) reset_done(SCpnt); else scsi_done(SCpnt); @@ -1101,7 +1103,7 @@ static int aha152x_device_reset(struct scsi_cmnd * SCpnt) DO_LOCK(flags); - if (aha152x_scsi_pointer(SCpnt)->phase & resetted) { + if (aha152x_priv(SCpnt)->phase & resetted) { HOSTDATA(shpnt)->commands--; if (!HOSTDATA(shpnt)->commands) SETPORT(PORTA, 0); @@ -1395,31 +1397,30 @@ static void busfree_run(struct Scsi_Host *shpnt) SETPORT(SSTAT1, CLRBUSFREE); if(CURRENT_SC) { - struct scsi_pointer *scsi_pointer = - aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); #if defined(AHA152X_STAT) action++; #endif - scsi_pointer->phase &= ~syncneg; + acp->phase &= ~syncneg; - if (scsi_pointer->phase & completed) { + if (acp->phase & completed) { /* target sent COMMAND COMPLETE */ - done(shpnt, scsi_pointer->Status, DID_OK); + done(shpnt, acp->status, DID_OK); - } else if (scsi_pointer->phase & aborted) { - done(shpnt, scsi_pointer->Status, DID_ABORT); + } else if (acp->phase & aborted) { + done(shpnt, acp->status, DID_ABORT); - } else if (scsi_pointer->phase & resetted) { - done(shpnt, scsi_pointer->Status, DID_RESET); + } else if (acp->phase & resetted) { + done(shpnt, acp->status, DID_RESET); - } else if (scsi_pointer->phase & disconnected) { + } else if (acp->phase & disconnected) { /* target sent DISCONNECT */ #if defined(AHA152X_STAT) HOSTDATA(shpnt)->disconnections++; #endif append_SC(&DISCONNECTED_SC, CURRENT_SC); - scsi_pointer->phase |= 1 << 16; + acp->phase |= 1 << 16; CURRENT_SC = NULL; } else { @@ -1438,24 +1439,23 @@ static void busfree_run(struct Scsi_Host *shpnt) action++; #endif - if (aha152x_scsi_pointer(DONE_SC)->phase & check_condition) { + if (aha152x_priv(DONE_SC)->phase & check_condition) { struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC; struct aha152x_scdata *sc = SCDATA(cmd); scsi_eh_restore_cmnd(cmd, &sc->ses); - aha152x_scsi_pointer(cmd)->Status = SAM_STAT_CHECK_CONDITION; + aha152x_priv(cmd)->status = SAM_STAT_CHECK_CONDITION; HOSTDATA(shpnt)->commands--; if (!HOSTDATA(shpnt)->commands) SETPORT(PORTA, 0); /* turn led off */ - } else if (aha152x_scsi_pointer(DONE_SC)->Status == - SAM_STAT_CHECK_CONDITION) { + } else if (aha152x_priv(DONE_SC)->status == SAM_STAT_CHECK_CONDITION) { #if defined(AHA152X_STAT) HOSTDATA(shpnt)->busfree_with_check_condition++; #endif - if(!(aha152x_scsi_pointer(DONE_SC)->phase & not_issued)) { + if (!(aha152x_priv(DONE_SC)->phase & not_issued)) { struct aha152x_scdata *sc; struct scsi_cmnd *ptr = DONE_SC; DONE_SC=NULL; @@ -1480,7 +1480,7 @@ static void busfree_run(struct Scsi_Host *shpnt) if (!HOSTDATA(shpnt)->commands) SETPORT(PORTA, 0); /* turn led off */ - if (!(aha152x_scsi_pointer(ptr)->phase & resetting)) { + if (!(aha152x_priv(ptr)->phase & resetting)) { kfree(ptr->host_scribble); ptr->host_scribble=NULL; } @@ -1503,13 +1503,12 @@ static void busfree_run(struct Scsi_Host *shpnt) DO_UNLOCK(flags); if(CURRENT_SC) { - struct scsi_pointer *scsi_pointer = - aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); #if defined(AHA152X_STAT) action++; #endif - scsi_pointer->phase |= selecting; + acp->phase |= selecting; /* clear selection timeout */ SETPORT(SSTAT1, SELTO); @@ -1537,13 +1536,13 @@ static void busfree_run(struct Scsi_Host *shpnt) */ static void seldo_run(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); SETPORT(SCSISIG, 0); SETPORT(SSTAT1, CLRBUSFREE); SETPORT(SSTAT1, CLRPHASECHG); - scsi_pointer->phase &= ~(selecting | not_issued); + acp->phase &= ~(selecting | not_issued); SETPORT(SCSISEQ, 0); @@ -1558,12 +1557,12 @@ static void seldo_run(struct Scsi_Host *shpnt) ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun)); - if (scsi_pointer->phase & aborting) { + if (acp->phase & aborting) { ADDMSGO(ABORT); - } else if (scsi_pointer->phase & resetting) { + } else if (acp->phase & resetting) { ADDMSGO(BUS_DEVICE_RESET); } else if (SYNCNEG==0 && SYNCHRONOUS) { - scsi_pointer->phase |= syncneg; + acp->phase |= syncneg; MSGOLEN += spi_populate_sync_msg(&MSGO(MSGOLEN), 50, 8); SYNCNEG=1; /* negotiation in progress */ } @@ -1578,7 +1577,7 @@ static void seldo_run(struct Scsi_Host *shpnt) */ static void selto_run(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp; SETPORT(SCSISEQ, 0); SETPORT(SSTAT1, CLRSELTIMO); @@ -1586,9 +1585,10 @@ static void selto_run(struct Scsi_Host *shpnt) if (!CURRENT_SC) return; - scsi_pointer->phase &= ~selecting; + acp = aha152x_priv(CURRENT_SC); + acp->phase &= ~selecting; - if (scsi_pointer->phase & aborted) + if (acp->phase & aborted) done(shpnt, SAM_STAT_GOOD, DID_ABORT); else if (TESTLO(SSTAT0, SELINGO)) done(shpnt, SAM_STAT_GOOD, DID_BUS_BUSY); @@ -1616,10 +1616,9 @@ static void seldi_run(struct Scsi_Host *shpnt) SETPORT(SSTAT1, CLRPHASECHG); if(CURRENT_SC) { - struct scsi_pointer *scsi_pointer = - aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); - if (!(scsi_pointer->phase & not_issued)) + if (!(acp->phase & not_issued)) scmd_printk(KERN_ERR, CURRENT_SC, "command should not have been issued yet\n"); @@ -1676,7 +1675,7 @@ static void seldi_run(struct Scsi_Host *shpnt) static void msgi_run(struct Scsi_Host *shpnt) { for(;;) { - struct scsi_pointer *scsi_pointer; + struct aha152x_cmd_priv *acp; int sstat1 = GETPORT(SSTAT1); if(sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT)) @@ -1714,9 +1713,9 @@ static void msgi_run(struct Scsi_Host *shpnt) continue; } - scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); - scsi_pointer->Message = MSGI(0); - scsi_pointer->phase &= ~disconnected; + acp = aha152x_priv(CURRENT_SC); + acp->message = MSGI(0); + acp->phase &= ~disconnected; MSGILEN=0; @@ -1724,8 +1723,8 @@ static void msgi_run(struct Scsi_Host *shpnt) continue; } - scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); - scsi_pointer->Message = MSGI(0); + acp = aha152x_priv(CURRENT_SC); + acp->message = MSGI(0); switch (MSGI(0)) { case DISCONNECT: @@ -1733,11 +1732,11 @@ static void msgi_run(struct Scsi_Host *shpnt) scmd_printk(KERN_WARNING, CURRENT_SC, "target was not allowed to disconnect\n"); - scsi_pointer->phase |= disconnected; + acp->phase |= disconnected; break; case COMMAND_COMPLETE: - scsi_pointer->phase |= completed; + acp->phase |= completed; break; case MESSAGE_REJECT: @@ -1867,11 +1866,9 @@ static void msgi_end(struct Scsi_Host *shpnt) */ static void msgo_init(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); - if(MSGOLEN==0) { - if ((scsi_pointer->phase & syncneg) && SYNCNEG==2 && - SYNCRATE==0) { + if ((aha152x_priv(CURRENT_SC)->phase & syncneg) && + SYNCNEG == 2 && SYNCRATE == 0) { ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun)); } else { scmd_printk(KERN_INFO, CURRENT_SC, @@ -1888,7 +1885,7 @@ static void msgo_init(struct Scsi_Host *shpnt) */ static void msgo_run(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); while(MSGO_Iphase |= identified; + acp->phase |= identified; if (MSGO(MSGO_I)==ABORT) - scsi_pointer->phase |= aborted; + acp->phase |= aborted; if (MSGO(MSGO_I)==BUS_DEVICE_RESET) - scsi_pointer->phase |= resetted; + acp->phase |= resetted; SETPORT(SCSIDAT, MSGO(MSGO_I++)); } @@ -1936,7 +1933,7 @@ static void msgo_end(struct Scsi_Host *shpnt) */ static void cmd_init(struct Scsi_Host *shpnt) { - if (aha152x_scsi_pointer(CURRENT_SC)->sent_command) { + if (aha152x_priv(CURRENT_SC)->sent_command) { scmd_printk(KERN_ERR, CURRENT_SC, "command already sent\n"); done(shpnt, SAM_STAT_GOOD, DID_ERROR); @@ -1967,7 +1964,7 @@ static void cmd_end(struct Scsi_Host *shpnt) "command sent incompletely (%d/%d)\n", CMD_I, CURRENT_SC->cmd_len); else - aha152x_scsi_pointer(CURRENT_SC)->sent_command++; + aha152x_priv(CURRENT_SC)->sent_command++; } /* @@ -1979,7 +1976,7 @@ static void status_run(struct Scsi_Host *shpnt) if (TESTLO(SSTAT0, SPIORDY)) return; - aha152x_scsi_pointer(CURRENT_SC)->Status = GETPORT(SCSIDAT); + aha152x_priv(CURRENT_SC)->status = GETPORT(SCSIDAT); } @@ -2003,7 +2000,7 @@ static void datai_init(struct Scsi_Host *shpnt) static void datai_run(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer; + struct aha152x_cmd_priv *acp; unsigned long the_time; int fifodata, data_count; @@ -2041,36 +2038,35 @@ static void datai_run(struct Scsi_Host *shpnt) fifodata = GETPORT(FIFOSTAT); } - scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); - if (scsi_pointer->this_residual > 0) { - while (fifodata > 0 && scsi_pointer->this_residual > 0) { - data_count = fifodata > scsi_pointer->this_residual ? - scsi_pointer->this_residual : - fifodata; + acp = aha152x_priv(CURRENT_SC); + if (acp->this_residual > 0) { + while (fifodata > 0 && acp->this_residual > 0) { + data_count = fifodata > acp->this_residual ? + acp->this_residual : fifodata; fifodata -= data_count; if (data_count & 1) { SETPORT(DMACNTRL0, ENDMA|_8BIT); - *scsi_pointer->ptr++ = GETPORT(DATAPORT); - scsi_pointer->this_residual--; + *acp->ptr++ = GETPORT(DATAPORT); + acp->this_residual--; DATA_LEN++; SETPORT(DMACNTRL0, ENDMA); } if (data_count > 1) { data_count >>= 1; - insw(DATAPORT, scsi_pointer->ptr, data_count); - scsi_pointer->ptr += 2 * data_count; - scsi_pointer->this_residual -= 2 * data_count; + insw(DATAPORT, acp->ptr, data_count); + acp->ptr += 2 * data_count; + acp->this_residual -= 2 * data_count; DATA_LEN += 2 * data_count; } - if (scsi_pointer->this_residual == 0 && - !sg_is_last(scsi_pointer->buffer)) { + if (acp->this_residual == 0 && + !sg_is_last(acp->buffer)) { /* advance to next buffer */ - scsi_pointer->buffer = sg_next(scsi_pointer->buffer); - scsi_pointer->ptr = SG_ADDRESS(scsi_pointer->buffer); - scsi_pointer->this_residual = scsi_pointer->buffer->length; + acp->buffer = sg_next(acp->buffer); + acp->ptr = SG_ADDRESS(acp->buffer); + acp->this_residual = acp->buffer->length; } } } else if (fifodata > 0) { @@ -2138,15 +2134,15 @@ static void datao_init(struct Scsi_Host *shpnt) static void datao_run(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); unsigned long the_time; int data_count; /* until phase changes or all data sent */ - while (TESTLO(DMASTAT, INTSTAT) && scsi_pointer->this_residual > 0) { + while (TESTLO(DMASTAT, INTSTAT) && acp->this_residual > 0) { data_count = 128; - if (data_count > scsi_pointer->this_residual) - data_count = scsi_pointer->this_residual; + if (data_count > acp->this_residual) + data_count = acp->this_residual; if(TESTLO(DMASTAT, DFIFOEMP)) { scmd_printk(KERN_ERR, CURRENT_SC, @@ -2157,26 +2153,25 @@ static void datao_run(struct Scsi_Host *shpnt) if(data_count & 1) { SETPORT(DMACNTRL0,WRITE_READ|ENDMA|_8BIT); - SETPORT(DATAPORT, *scsi_pointer->ptr++); - scsi_pointer->this_residual--; + SETPORT(DATAPORT, *acp->ptr++); + acp->this_residual--; CMD_INC_RESID(CURRENT_SC, -1); SETPORT(DMACNTRL0,WRITE_READ|ENDMA); } if(data_count > 1) { data_count >>= 1; - outsw(DATAPORT, scsi_pointer->ptr, data_count); - scsi_pointer->ptr += 2 * data_count; - scsi_pointer->this_residual -= 2 * data_count; + outsw(DATAPORT, acp->ptr, data_count); + acp->ptr += 2 * data_count; + acp->this_residual -= 2 * data_count; CMD_INC_RESID(CURRENT_SC, -2 * data_count); } - if (scsi_pointer->this_residual == 0 && - !sg_is_last(scsi_pointer->buffer)) { + if (acp->this_residual == 0 && !sg_is_last(acp->buffer)) { /* advance to next buffer */ - scsi_pointer->buffer = sg_next(scsi_pointer->buffer); - scsi_pointer->ptr = SG_ADDRESS(scsi_pointer->buffer); - scsi_pointer->this_residual = scsi_pointer->buffer->length; + acp->buffer = sg_next(acp->buffer); + acp->ptr = SG_ADDRESS(acp->buffer); + acp->this_residual = acp->buffer->length; } the_time=jiffies + 100*HZ; @@ -2192,7 +2187,7 @@ static void datao_run(struct Scsi_Host *shpnt) static void datao_end(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); + struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC); if(TESTLO(DMASTAT, DFIFOEMP)) { u32 datao_cnt = GETSTCNT(); @@ -2211,10 +2206,9 @@ static void datao_end(struct Scsi_Host *shpnt) sg = sg_next(sg); } - scsi_pointer->buffer = sg; - scsi_pointer->ptr = SG_ADDRESS(scsi_pointer->buffer) + done; - scsi_pointer->this_residual = scsi_pointer->buffer->length - - done; + acp->buffer = sg; + acp->ptr = SG_ADDRESS(acp->buffer) + done; + acp->this_residual = acp->buffer->length - done; } SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT); @@ -2229,7 +2223,6 @@ static void datao_end(struct Scsi_Host *shpnt) */ static int update_state(struct Scsi_Host *shpnt) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(CURRENT_SC); int dataphase=0; unsigned int stat0 = GETPORT(SSTAT0); unsigned int stat1 = GETPORT(SSTAT1); @@ -2244,7 +2237,7 @@ static int update_state(struct Scsi_Host *shpnt) } else if (stat0 & SELDI && PREVSTATE == busfree) { STATE=seldi; } else if (stat0 & SELDO && CURRENT_SC && - (scsi_pointer->phase & selecting)) { + (aha152x_priv(CURRENT_SC)->phase & selecting)) { STATE=seldo; } else if(stat1 & SELTO) { STATE=selto; @@ -2376,8 +2369,7 @@ static void is_complete(struct Scsi_Host *shpnt) SETPORT(SXFRCTL0, CH1); SETPORT(DMACNTRL0, 0); if(CURRENT_SC) - aha152x_scsi_pointer(CURRENT_SC)->phase &= - ~spiordy; + aha152x_priv(CURRENT_SC)->phase &= ~spiordy; } /* @@ -2399,8 +2391,7 @@ static void is_complete(struct Scsi_Host *shpnt) SETPORT(DMACNTRL0, 0); SETPORT(SXFRCTL0, CH1|SPIOEN); if(CURRENT_SC) - aha152x_scsi_pointer(CURRENT_SC)->phase |= - spiordy; + aha152x_priv(CURRENT_SC)->phase |= spiordy; } /* @@ -2490,7 +2481,7 @@ static void disp_enintr(struct Scsi_Host *shpnt) */ static void show_command(struct scsi_cmnd *ptr) { - const int phase = aha152x_scsi_pointer(ptr)->phase; + const int phase = aha152x_priv(ptr)->phase; scsi_print_command(ptr); scmd_printk(KERN_DEBUG, ptr, @@ -2538,8 +2529,8 @@ static void show_queues(struct Scsi_Host *shpnt) static void get_command(struct seq_file *m, struct scsi_cmnd * ptr) { - struct scsi_pointer *scsi_pointer = aha152x_scsi_pointer(ptr); - const int phase = scsi_pointer->phase; + struct aha152x_cmd_priv *acp = aha152x_priv(ptr); + const int phase = acp->phase; int i; seq_printf(m, "%p: target=%d; lun=%d; cmnd=( ", @@ -2549,8 +2540,8 @@ static void get_command(struct seq_file *m, struct scsi_cmnd * ptr) seq_printf(m, "0x%02x ", ptr->cmnd[i]); seq_printf(m, "); resid=%d; residual=%d; buffers=%d; phase |", - scsi_get_resid(ptr), scsi_pointer->this_residual, - sg_nents(scsi_pointer->buffer) - 1); + scsi_get_resid(ptr), acp->this_residual, + sg_nents(acp->buffer) - 1); if (phase & not_issued) seq_puts(m, "not issued|"); -- cgit From eaba83b5b8506bbc9ee7ca2f10aeab3fff3719e7 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 16 Mar 2022 17:44:30 +0800 Subject: scsi: core: Fix sbitmap depth in scsi_realloc_sdev_budget_map() In commit edb854a3680b ("scsi: core: Reallocate device's budget map on queue depth change"), the sbitmap for the device budget map may be reallocated after the slave device depth is configured. When the sbitmap is reallocated we use the result from scsi_device_max_queue_depth() for the sbitmap size, but don't resize to match the actual device queue depth. Fix by resizing the sbitmap after reallocating the budget sbitmap. We do this instead of init'ing the sbitmap to the device queue depth as the user may want to change the queue depth later via sysfs or other. Link: https://lore.kernel.org/r/1647423870-143867-1-git-send-email-john.garry@huawei.com Fixes: edb854a3680b ("scsi: core: Reallocate device's budget map on queue depth change") Tested-by: Damien Le Moal Reviewed-by: Ming Lei Reviewed-by: Bart Van Assche Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index f4e6c68ac99e..2ef78083f1ef 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -223,6 +223,8 @@ static int scsi_realloc_sdev_budget_map(struct scsi_device *sdev, int ret; struct sbitmap sb_backup; + depth = min_t(unsigned int, depth, scsi_device_max_queue_depth(sdev)); + /* * realloc if new shift is calculated, which is caused by setting * up one new default queue depth after calling ->slave_configure @@ -245,6 +247,9 @@ static int scsi_realloc_sdev_budget_map(struct scsi_device *sdev, scsi_device_max_queue_depth(sdev), new_shift, GFP_KERNEL, sdev->request_queue->node, false, true); + if (!ret) + sbitmap_resize(&sdev->budget_map, depth); + if (need_free) { if (ret) sdev->budget_map = sb_backup; -- cgit From fac952bb546a9f103a769d7105194175e11abc99 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 16 Mar 2022 23:56:15 +0000 Subject: scsi: isci: Fix spelling mistake "doesnt" -> "doesn't" There are a few spelling mistakes in dev_warn and dev_err messages. Fix these. Link: https://lore.kernel.org/r/20220316235615.56683-1-colin.i.king@gmail.com Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/isci/host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index d690d9cf7eb1..35589b6af90d 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -413,7 +413,7 @@ static void sci_controller_event_completion(struct isci_host *ihost, u32 ent) dev_warn(&ihost->pdev->dev, "%s: SCIC Controller 0x%p received " "event 0x%x for io request object " - "that doesnt exist.\n", + "that doesn't exist.\n", __func__, ihost, ent); @@ -428,7 +428,7 @@ static void sci_controller_event_completion(struct isci_host *ihost, u32 ent) dev_warn(&ihost->pdev->dev, "%s: SCIC Controller 0x%p received " "event 0x%x for remote device object " - "that doesnt exist.\n", + "that doesn't exist.\n", __func__, ihost, ent); @@ -462,7 +462,7 @@ static void sci_controller_event_completion(struct isci_host *ihost, u32 ent) } else dev_err(&ihost->pdev->dev, "%s: SCIC Controller 0x%p received event 0x%x " - "for remote device object 0x%0x that doesnt " + "for remote device object 0x%0x that doesn't " "exist.\n", __func__, ihost, -- cgit From 41b8c2a31472a97349fe54c3a6b3176d9cdc31be Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 16 Mar 2022 12:20:06 -0700 Subject: scsi: virtio-scsi: Eliminate anonymous module_init & module_exit Eliminate anonymous module_init() and module_exit(), which can lead to confusion or ambiguity when reading System.map, crashes/oops/bugs, or an initcall_debug log. Give each of these init and exit functions unique driver-specific names to eliminate the anonymous names. Example 1: (System.map) ffffffff832fc78c t init ffffffff832fc79e t init ffffffff832fc8f8 t init Example 2: (initcall_debug log) calling init+0x0/0x12 @ 1 initcall init+0x0/0x12 returned 0 after 15 usecs calling init+0x0/0x60 @ 1 initcall init+0x0/0x60 returned 0 after 2 usecs calling init+0x0/0x9a @ 1 initcall init+0x0/0x9a returned 0 after 74 usecs Link: https://lore.kernel.org/r/20220316192010.19001-6-rdunlap@infradead.org Fixes: 4fe74b1cb051 ("[SCSI] virtio-scsi: SCSI driver for QEMU based virtual machines") Cc: "Michael S. Tsirkin" Cc: Jason Wang Cc: Paolo Bonzini Cc: Stefan Hajnoczi Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Cc: virtualization@lists.linux-foundation.org Reviewed-by: Stefan Hajnoczi Reviewed-by: Ira Weiny Acked-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: Randy Dunlap Signed-off-by: Martin K. Petersen --- drivers/scsi/virtio_scsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 0e6110da69e7..578c4b6d0f7d 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -988,7 +988,7 @@ static struct virtio_driver virtio_scsi_driver = { .remove = virtscsi_remove, }; -static int __init init(void) +static int __init virtio_scsi_init(void) { int ret = -ENOMEM; @@ -1020,14 +1020,14 @@ error: return ret; } -static void __exit fini(void) +static void __exit virtio_scsi_fini(void) { unregister_virtio_driver(&virtio_scsi_driver); mempool_destroy(virtscsi_cmd_pool); kmem_cache_destroy(virtscsi_cmd_cache); } -module_init(init); -module_exit(fini); +module_init(virtio_scsi_init); +module_exit(virtio_scsi_fini); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio SCSI HBA driver"); -- cgit From 066f4c31945ce2cb30e840794ee01713dec73b74 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 17 Mar 2022 10:52:14 +0300 Subject: scsi: hisi_sas: Remove stray fallthrough annotation This case statement doesn't fall through any more so remove the fallthrough annotation. Link: https://lore.kernel.org/r/20220317075214.GC25237@kili Acked-by: John Garry Signed-off-by: Dan Carpenter Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 461ef8a76c4c..4bda2f6cb352 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -442,7 +442,6 @@ void hisi_sas_task_deliver(struct hisi_hba *hisi_hba, case SAS_PROTOCOL_INTERNAL_ABORT: hisi_sas_task_prep_abort(hisi_hba, slot); break; - fallthrough; default: return; } -- cgit From 99241e119f4a2077383f994a174b38ced21d6fca Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Tue, 29 Mar 2022 10:12:51 +0800 Subject: scsi: core: sysfs: Remove comments that conflict with the actual logic Christoph Hellwig Says: ======================= I think we should just handle the error properly and remove the comment. There's no good reason to ignore bsg registration errors. In fact, after commit 92c4b58b15c5 ("scsi: core: Register sysfs attributes earlier"), we are already forced to return errno. We discuss this issue in [1]. [1] https://lore.kernel.org/all/20211022010201.426746-1-liu.yun@linux.dev/ Link: https://lore.kernel.org/r/20220329021251.123805-1-liu.yun@linux.dev Suggested-by: Christoph Hellwig Reviewed-by: Guenter Roeck Reviewed-by: Christoph Hellwig Signed-off-by: Jackie Liu Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_sysfs.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 226a50944c00..dc6872e352bd 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1384,10 +1384,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) if (IS_ENABLED(CONFIG_BLK_DEV_BSG)) { sdev->bsg_dev = scsi_bsg_register_queue(sdev); if (IS_ERR(sdev->bsg_dev)) { - /* - * We're treating error on bsg register as non-fatal, so - * pretend nothing went wrong. - */ error = PTR_ERR(sdev->bsg_dev); sdev_printk(KERN_INFO, sdev, "Failed to register bsg queue, errno=%d\n", -- cgit From 5ca0faf9c292029ec6f9edbfe7b42e97dcb87dca Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 21 Mar 2022 16:18:53 +0100 Subject: scsi: ufs: qcom: Drop custom Android boot parameters The QCOM UFS driver requires an androidboot.bootdevice command line argument matching the UFS device name. If the name is different, it refuses to probe. This androidboot.bootdevice is provided by stock/vendor (from an Android-based device) bootloader. This does not make sense from Linux point of view. Driver should be able to boot regardless of bootloader. Driver should not depend on some Android custom environment data. Link: https://lore.kernel.org/r/20220321151853.24138-1-krzk@kernel.org Tested-by: Amit Pundir Tested-by: Luca Weiss Reviewed-by: Brian Masney Reviewed-by: Bjorn Andersson Reviewed-by: Alim Akhtar Signed-off-by: Krzysztof Kozlowski Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-qcom.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 0d2e950d0865..586c0e567ff9 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -957,18 +957,6 @@ static const struct reset_control_ops ufs_qcom_reset_ops = { .deassert = ufs_qcom_reset_deassert, }; -#define ANDROID_BOOT_DEV_MAX 30 -static char android_boot_dev[ANDROID_BOOT_DEV_MAX]; - -#ifndef MODULE -static int __init get_android_boot_dev(char *str) -{ - strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX); - return 1; -} -__setup("androidboot.bootdevice=", get_android_boot_dev); -#endif - /** * ufs_qcom_init - bind phy with controller * @hba: host controller instance @@ -988,9 +976,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) struct resource *res; struct ufs_clk_info *clki; - if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) - return -ENODEV; - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); if (!host) { err = -ENOMEM; -- cgit From 37a9bd7090cdf6657ced3f693897819a66ee8d52 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 22 Mar 2022 07:46:48 -0700 Subject: scsi: aic7xxx: Use standard PCI subsystem, subdevice defines Common defines should be used over custom defines. Change and remove these defines: - PCIR_SUBVEND_0 to PCI_SUBSYSTEM_VENDOR_ID - PCIR_SUBDEV_0 to PCI_SUBSYSTEM_ID Link: https://lore.kernel.org/r/20220322144648.2467777-1-trix@redhat.com Signed-off-by: Tom Rix Signed-off-by: Martin K. Petersen --- drivers/scsi/aic7xxx/aic79xx_osm.h | 2 -- drivers/scsi/aic7xxx/aic79xx_pci.c | 6 +++--- drivers/scsi/aic7xxx/aic7xxx_osm.h | 2 -- drivers/scsi/aic7xxx/aic7xxx_pci.c | 4 ++-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h index 679a4fd13874..793fe19993a9 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.h +++ b/drivers/scsi/aic7xxx/aic79xx_osm.h @@ -420,8 +420,6 @@ ahd_unlock(struct ahd_softc *ahd, unsigned long *flags) /* config registers for header type 0 devices */ #define PCIR_MAPS 0x10 -#define PCIR_SUBVEND_0 0x2c -#define PCIR_SUBDEV_0 0x2e /****************************** PCI-X definitions *****************************/ #define PCIXR_COMMAND 0x96 diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c index 2f0bdb9225a4..5fad41b1ab58 100644 --- a/drivers/scsi/aic7xxx/aic79xx_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_pci.c @@ -260,8 +260,8 @@ ahd_find_pci_device(ahd_dev_softc_t pci) vendor = ahd_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2); device = ahd_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2); - subvendor = ahd_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2); - subdevice = ahd_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2); + subvendor = ahd_pci_read_config(pci, PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2); + subdevice = ahd_pci_read_config(pci, PCI_SUBSYSTEM_ID, /*bytes*/2); full_id = ahd_compose_id(device, vendor, subdevice, @@ -298,7 +298,7 @@ ahd_pci_config(struct ahd_softc *ahd, const struct ahd_pci_identity *entry) * Record if this is an HP board. */ subvendor = ahd_pci_read_config(ahd->dev_softc, - PCIR_SUBVEND_0, /*bytes*/2); + PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2); if (subvendor == SUBID_HP) ahd->flags |= AHD_HP_BOARD; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h index 4782a304e93c..51d9f4de0734 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.h +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h @@ -433,8 +433,6 @@ ahc_unlock(struct ahc_softc *ahc, unsigned long *flags) /* config registers for header type 0 devices */ #define PCIR_MAPS 0x10 -#define PCIR_SUBVEND_0 0x2c -#define PCIR_SUBDEV_0 0x2e typedef enum { diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c index dab3a6d12c4d..2d4c85426dc3 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c @@ -673,8 +673,8 @@ ahc_find_pci_device(ahc_dev_softc_t pci) vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2); device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2); - subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2); - subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2); + subvendor = ahc_pci_read_config(pci, PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2); + subdevice = ahc_pci_read_config(pci, PCI_SUBSYSTEM_ID, /*bytes*/2); full_id = ahc_compose_id(device, vendor, subdevice, subvendor); /* -- cgit From 16ed828b872d12ccba8f07bcc446ae89ba662f9c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 19 Mar 2022 08:01:24 +0100 Subject: scsi: zorro7xx: Fix a resource leak in zorro7xx_remove_one() The error handling path of the probe releases a resource that is not freed in the remove function. In some cases, a ioremap() must be undone. Add the missing iounmap() call in the remove function. Link: https://lore.kernel.org/r/247066a3104d25f9a05de8b3270fc3c848763bcc.1647673264.git.christophe.jaillet@wanadoo.fr Fixes: 45804fbb00ee ("[SCSI] 53c700: Amiga Zorro NCR53c710 SCSI") Reviewed-by: Geert Uytterhoeven Signed-off-by: Christophe JAILLET Signed-off-by: Martin K. Petersen --- drivers/scsi/zorro7xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/zorro7xx.c b/drivers/scsi/zorro7xx.c index 27b9e2baab1a..7acf9193a9e8 100644 --- a/drivers/scsi/zorro7xx.c +++ b/drivers/scsi/zorro7xx.c @@ -159,6 +159,8 @@ static void zorro7xx_remove_one(struct zorro_dev *z) scsi_remove_host(host); NCR_700_release(host); + if (host->base > 0x01000000) + iounmap(hostdata->base); kfree(hostdata); free_irq(host->irq, host); zorro_release_device(z); -- cgit From 7ff897b2a59558d919b72f1aef58b1aa63d19c61 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 19 Mar 2022 23:11:22 +0000 Subject: scsi: bnx2fc: Fix spelling mistake "mis-match" -> "mismatch" There are a few spelling mistakes in some error messages. Fix them. Link: https://lore.kernel.org/r/20220319231122.21476-1-colin.i.king@gmail.com Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/bnx2fc/bnx2fc_hwi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 0103f811cc25..776544385598 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -1169,7 +1169,7 @@ static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba, ofld_kcqe->fcoe_conn_context_id); interface = tgt->port->priv; if (hba != interface->hba) { - printk(KERN_ERR PFX "ERROR:ofld_cmpl: HBA mis-match\n"); + printk(KERN_ERR PFX "ERROR:ofld_cmpl: HBA mismatch\n"); goto ofld_cmpl_err; } /* @@ -1226,12 +1226,12 @@ static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba, * and enable */ if (tgt->context_id != context_id) { - printk(KERN_ERR PFX "context id mis-match\n"); + printk(KERN_ERR PFX "context id mismatch\n"); return; } interface = tgt->port->priv; if (hba != interface->hba) { - printk(KERN_ERR PFX "bnx2fc-enbl_cmpl: HBA mis-match\n"); + printk(KERN_ERR PFX "bnx2fc-enbl_cmpl: HBA mismatch\n"); goto enbl_cmpl_err; } if (!ofld_kcqe->completion_status) -- cgit From a6b758b0420bda28995ea6939ca0808fcec68be4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 19 Mar 2022 23:14:45 +0000 Subject: scsi: bnx2i: Fix spelling mistake "mis-match" -> "mismatch" There are a few spelling mistakes in some error messages. Fix them. Link: https://lore.kernel.org/r/20220319231445.21696-1-colin.i.king@gmail.com Acked-by: Manish Rangankar Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/bnx2i/bnx2i_hwi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 5521469ce678..7fe7f53a41c0 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -2398,7 +2398,7 @@ static void bnx2i_process_conn_destroy_cmpl(struct bnx2i_hba *hba, } if (hba != ep->hba) { - printk(KERN_ALERT "conn destroy- error hba mis-match\n"); + printk(KERN_ALERT "conn destroy- error hba mismatch\n"); return; } @@ -2432,7 +2432,7 @@ static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba, } if (hba != ep->hba) { - printk(KERN_ALERT "ofld_cmpl: error hba mis-match\n"); + printk(KERN_ALERT "ofld_cmpl: error hba mismatch\n"); return; } -- cgit From 67bae5f28c895f8737a1974c3f31cf12b9170b14 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Tue, 29 Mar 2022 15:27:32 +0200 Subject: drm: of: Properly try all possible cases for bridge/panel detection While bridge/panel detection was initially relying on the usual port/ports-based of graph detection, it was recently changed to perform the lookup on any child node that is not port/ports instead when such a node is available, with no fallback on the usual way. This results in breaking detection when a child node is present but does not contain any panel or bridge node, even when the usual port/ports-based of graph is there. In order to support both situations properly, this commit reworks the logic to try both options and not just one of the two: it will only return -EPROBE_DEFER when both have failed. Signed-off-by: Paul Kocialkowski Fixes: 80253168dbfd ("drm: of: Lookup if child node has panel or bridge") Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220329132732.628474-1-paul.kocialkowski@bootlin.com --- drivers/gpu/drm/drm_of.c | 99 ++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 9d90cd75c457..8716da6369a6 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -219,6 +219,29 @@ int drm_of_encoder_active_endpoint(struct device_node *node, } EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); +static int find_panel_or_bridge(struct device_node *node, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + if (panel) { + *panel = of_drm_find_panel(node); + if (!IS_ERR(*panel)) + return 0; + + /* Clear the panel pointer in case of error. */ + *panel = NULL; + } + + /* No panel found yet, check for a bridge next. */ + if (bridge) { + *bridge = of_drm_find_bridge(node); + if (*bridge) + return 0; + } + + return -EPROBE_DEFER; +} + /** * drm_of_find_panel_or_bridge - return connected panel or bridge device * @np: device tree node containing encoder output ports @@ -241,66 +264,44 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, struct drm_panel **panel, struct drm_bridge **bridge) { - int ret = -EPROBE_DEFER; - struct device_node *remote; + struct device_node *node; + int ret; if (!panel && !bridge) return -EINVAL; + if (panel) *panel = NULL; - - /** - * Devices can also be child nodes when we also control that device - * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device). - * - * Lookup for a child node of the given parent that isn't either port - * or ports. - */ - for_each_available_child_of_node(np, remote) { - if (of_node_name_eq(remote, "port") || - of_node_name_eq(remote, "ports")) - continue; - - goto of_find_panel_or_bridge; + if (bridge) + *bridge = NULL; + + /* Check for a graph on the device node first. */ + if (of_graph_is_present(np)) { + node = of_graph_get_remote_node(np, port, endpoint); + if (node) { + ret = find_panel_or_bridge(node, panel, bridge); + of_node_put(node); + + if (!ret) + return 0; + } } - /* - * of_graph_get_remote_node() produces a noisy error message if port - * node isn't found and the absence of the port is a legit case here, - * so at first we silently check whether graph presents in the - * device-tree node. - */ - if (!of_graph_is_present(np)) - return -ENODEV; - - remote = of_graph_get_remote_node(np, port, endpoint); - -of_find_panel_or_bridge: - if (!remote) - return -ENODEV; + /* Otherwise check for any child node other than port/ports. */ + for_each_available_child_of_node(np, node) { + if (of_node_name_eq(node, "port") || + of_node_name_eq(node, "ports")) + continue; - if (panel) { - *panel = of_drm_find_panel(remote); - if (!IS_ERR(*panel)) - ret = 0; - else - *panel = NULL; - } - - /* No panel found yet, check for a bridge next. */ - if (bridge) { - if (ret) { - *bridge = of_drm_find_bridge(remote); - if (*bridge) - ret = 0; - } else { - *bridge = NULL; - } + ret = find_panel_or_bridge(node, panel, bridge); + of_node_put(node); + /* Stop at the first found occurrence. */ + if (!ret) + return 0; } - of_node_put(remote); - return ret; + return -EPROBE_DEFER; } EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); -- cgit From 7414539c5f2e43bad67ae88a3612455d01583429 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 30 Mar 2022 02:19:16 -0400 Subject: Revert "virtio: use virtio_device_ready() in virtio_device_restore()" This reverts commit 8d65bc9a5be3f23c5e2ab36b6b8ef40095165b18. We reverted the problematic changes, no more need for work arounds on restore. Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang --- drivers/virtio/virtio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 75c8d560bbd3..22f15f444f75 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -526,9 +526,8 @@ int virtio_device_restore(struct virtio_device *dev) goto err; } - /* If restore didn't do it, mark device DRIVER_OK ourselves. */ - if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) - virtio_device_ready(dev); + /* Finally, tell the device we're all set */ + virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); virtio_config_enable(dev); -- cgit From c18c86808b78c4c2dc69f27f37c57abab14ee387 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 30 Mar 2022 02:22:17 -0400 Subject: Revert "virtio_config: introduce a new .enable_cbs method" This reverts commit d50497eb4e554e1f0351e1836ee7241c059592e6. The new callback ended up not being used, and it's asymmetrical: just enable, no disable. Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang --- include/linux/virtio_config.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index dafdc7f48c01..b341dd62aa4d 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -23,8 +23,6 @@ struct virtio_shm_region { * any of @get/@set, @get_status/@set_status, or @get_features/ * @finalize_features are NOT safe to be called from an atomic * context. - * @enable_cbs: enable the callbacks - * vdev: the virtio_device * @get: read the value of a configuration field * vdev: the virtio_device * offset: the offset of the configuration field @@ -78,7 +76,6 @@ struct virtio_shm_region { */ typedef void vq_callback_t(struct virtqueue *); struct virtio_config_ops { - void (*enable_cbs)(struct virtio_device *vdev); void (*get)(struct virtio_device *vdev, unsigned offset, void *buf, unsigned len); void (*set)(struct virtio_device *vdev, unsigned offset, @@ -233,9 +230,6 @@ void virtio_device_ready(struct virtio_device *dev) { unsigned status = dev->config->get_status(dev); - if (dev->config->enable_cbs) - dev->config->enable_cbs(dev); - BUG_ON(status & VIRTIO_CONFIG_S_DRIVER_OK); dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK); } -- cgit From 55ebf0d60e3cc6c9e8593399e185842c00e12f36 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 29 Mar 2022 12:21:07 +0800 Subject: vdpa: mlx5: prevent cvq work from hogging CPU A userspace triggerable infinite loop could happen in mlx5_cvq_kick_handler() if userspace keeps sending a huge amount of cvq requests. Fixing this by introducing a quota and re-queue the work if we're out of the budget (currently the implicit budget is one) . While at it, using a per device work struct to avoid on demand memory allocation for cvq. Fixes: 5262912ef3cfc ("vdpa/mlx5: Add support for control VQ and MAC setting") Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20220329042109.4029-1-jasowang@redhat.com Signed-off-by: Michael S. Tsirkin Acked-by: Eli Cohen --- drivers/vdpa/mlx5/net/mlx5_vnet.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index 2f4fb09f1e89..da21e5430434 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -163,6 +163,7 @@ struct mlx5_vdpa_net { u32 cur_num_vqs; struct notifier_block nb; struct vdpa_callback config_cb; + struct mlx5_vdpa_wq_ent cvq_ent; }; static void free_resources(struct mlx5_vdpa_net *ndev); @@ -1659,10 +1660,10 @@ static void mlx5_cvq_kick_handler(struct work_struct *work) ndev = to_mlx5_vdpa_ndev(mvdev); cvq = &mvdev->cvq; if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) - goto out; + return; if (!cvq->ready) - goto out; + return; while (true) { err = vringh_getdesc_iotlb(&cvq->vring, &cvq->riov, &cvq->wiov, &cvq->head, @@ -1696,9 +1697,10 @@ static void mlx5_cvq_kick_handler(struct work_struct *work) if (vringh_need_notify_iotlb(&cvq->vring)) vringh_notify(&cvq->vring); + + queue_work(mvdev->wq, &wqent->work); + break; } -out: - kfree(wqent); } static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) @@ -1706,7 +1708,6 @@ static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); struct mlx5_vdpa_virtqueue *mvq; - struct mlx5_vdpa_wq_ent *wqent; if (!is_index_valid(mvdev, idx)) return; @@ -1715,13 +1716,7 @@ static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) if (!mvdev->wq || !mvdev->cvq.ready) return; - wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC); - if (!wqent) - return; - - wqent->mvdev = mvdev; - INIT_WORK(&wqent->work, mlx5_cvq_kick_handler); - queue_work(mvdev->wq, &wqent->work); + queue_work(mvdev->wq, &ndev->cvq_ent.work); return; } @@ -2740,6 +2735,8 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, if (err) goto err_mr; + ndev->cvq_ent.mvdev = mvdev; + INIT_WORK(&ndev->cvq_ent.work, mlx5_cvq_kick_handler); mvdev->wq = create_singlethread_workqueue("mlx5_vdpa_wq"); if (!mvdev->wq) { err = -ENOMEM; -- cgit From 1c80cf031e0204fde471558ee40183695773ce13 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 29 Mar 2022 12:21:08 +0800 Subject: vdpa: mlx5: synchronize driver status with CVQ Currently, CVQ doesn't have any synchronization with the driver status. Then CVQ emulation code run in the middle of: 1) device reset 2) device status changed 3) map updating The will lead several unexpected issue like trying to execute CVQ command after the driver has been teared down. Fixing this by using reslock to synchronize CVQ emulation code with the driver status changing: - protect the whole device reset, status changing and set_map() updating with reslock - protect the CVQ handler with the reslock and check VIRTIO_CONFIG_S_DRIVER_OK in the CVQ handler This will guarantee that: 1) CVQ handler won't work if VIRTIO_CONFIG_S_DRIVER_OK is not set 2) CVQ handler will see a consistent state of the driver instead of the partial one when it is running in the middle of the teardown_driver() or setup_driver(). Cc: 5262912ef3cfc ("vdpa/mlx5: Add support for control VQ and MAC setting") Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20220329042109.4029-2-jasowang@redhat.com Signed-off-by: Michael S. Tsirkin Acked-by: Eli Cohen --- drivers/vdpa/mlx5/net/mlx5_vnet.c | 51 ++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index da21e5430434..79001301b383 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -1659,11 +1659,17 @@ static void mlx5_cvq_kick_handler(struct work_struct *work) mvdev = wqent->mvdev; ndev = to_mlx5_vdpa_ndev(mvdev); cvq = &mvdev->cvq; + + mutex_lock(&ndev->reslock); + + if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) + goto out; + if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) - return; + goto out; if (!cvq->ready) - return; + goto out; while (true) { err = vringh_getdesc_iotlb(&cvq->vring, &cvq->riov, &cvq->wiov, &cvq->head, @@ -1701,6 +1707,9 @@ static void mlx5_cvq_kick_handler(struct work_struct *work) queue_work(mvdev->wq, &wqent->work); break; } + +out: + mutex_unlock(&ndev->reslock); } static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx) @@ -2175,7 +2184,7 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb goto err_mr; if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) - return 0; + goto err_mr; restore_channels_info(ndev); err = setup_driver(mvdev); @@ -2190,12 +2199,14 @@ err_mr: return err; } +/* reslock must be held for this function */ static int setup_driver(struct mlx5_vdpa_dev *mvdev) { struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); int err; - mutex_lock(&ndev->reslock); + WARN_ON(!mutex_is_locked(&ndev->reslock)); + if (ndev->setup) { mlx5_vdpa_warn(mvdev, "setup driver called for already setup driver\n"); err = 0; @@ -2225,7 +2236,6 @@ static int setup_driver(struct mlx5_vdpa_dev *mvdev) goto err_fwd; } ndev->setup = true; - mutex_unlock(&ndev->reslock); return 0; @@ -2236,23 +2246,23 @@ err_tir: err_rqt: teardown_virtqueues(ndev); out: - mutex_unlock(&ndev->reslock); return err; } +/* reslock must be held for this function */ static void teardown_driver(struct mlx5_vdpa_net *ndev) { - mutex_lock(&ndev->reslock); + + WARN_ON(!mutex_is_locked(&ndev->reslock)); + if (!ndev->setup) - goto out; + return; remove_fwd_to_tir(ndev); destroy_tir(ndev); destroy_rqt(ndev); teardown_virtqueues(ndev); ndev->setup = false; -out: - mutex_unlock(&ndev->reslock); } static void clear_vqs_ready(struct mlx5_vdpa_net *ndev) @@ -2273,6 +2283,8 @@ static void mlx5_vdpa_set_status(struct vdpa_device *vdev, u8 status) print_status(mvdev, status, true); + mutex_lock(&ndev->reslock); + if ((status ^ ndev->mvdev.status) & VIRTIO_CONFIG_S_DRIVER_OK) { if (status & VIRTIO_CONFIG_S_DRIVER_OK) { err = setup_driver(mvdev); @@ -2282,16 +2294,19 @@ static void mlx5_vdpa_set_status(struct vdpa_device *vdev, u8 status) } } else { mlx5_vdpa_warn(mvdev, "did not expect DRIVER_OK to be cleared\n"); - return; + goto err_clear; } } ndev->mvdev.status = status; + mutex_unlock(&ndev->reslock); return; err_setup: mlx5_vdpa_destroy_mr(&ndev->mvdev); ndev->mvdev.status |= VIRTIO_CONFIG_S_FAILED; +err_clear: + mutex_unlock(&ndev->reslock); } static int mlx5_vdpa_reset(struct vdpa_device *vdev) @@ -2301,6 +2316,8 @@ static int mlx5_vdpa_reset(struct vdpa_device *vdev) print_status(mvdev, 0, true); mlx5_vdpa_info(mvdev, "performing device reset\n"); + + mutex_lock(&ndev->reslock); teardown_driver(ndev); clear_vqs_ready(ndev); mlx5_vdpa_destroy_mr(&ndev->mvdev); @@ -2313,6 +2330,7 @@ static int mlx5_vdpa_reset(struct vdpa_device *vdev) if (mlx5_vdpa_create_mr(mvdev, NULL)) mlx5_vdpa_warn(mvdev, "create MR failed\n"); } + mutex_unlock(&ndev->reslock); return 0; } @@ -2348,19 +2366,24 @@ static u32 mlx5_vdpa_get_generation(struct vdpa_device *vdev) static int mlx5_vdpa_set_map(struct vdpa_device *vdev, struct vhost_iotlb *iotlb) { struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); bool change_map; int err; + mutex_lock(&ndev->reslock); + err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map); if (err) { mlx5_vdpa_warn(mvdev, "set map failed(%d)\n", err); - return err; + goto err; } if (change_map) - return mlx5_vdpa_change_map(mvdev, iotlb); + err = mlx5_vdpa_change_map(mvdev, iotlb); - return 0; +err: + mutex_unlock(&ndev->reslock); + return err; } static void mlx5_vdpa_free(struct vdpa_device *vdev) -- cgit From 522574fd7864e091d473765102e866414979b2ab Mon Sep 17 00:00:00 2001 From: Delyan Kratunov Date: Mon, 21 Mar 2022 23:29:18 +0000 Subject: bpftool: Explicit errno handling in skeletons Andrii noticed that since f97b8b9bd630 ("bpftool: Fix a bug in subskeleton code generation") the subskeleton code allows bpf_object__destroy_subskeleton to overwrite the errno that subskeleton__open would return with. While this is not currently an issue, let's make it future-proof. This patch explicitly tracks err in subskeleton__open and skeleton__create (i.e. calloc failure is explicitly ENOMEM) and ensures that errno is -err on the error return path. The skeleton code had to be changed since maps and progs codegen is shared with subskeletons. Fixes: f97b8b9bd630 ("bpftool: Fix a bug in subskeleton code generation") Signed-off-by: Delyan Kratunov Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/3b6bfbb770c79ae64d8de26c1c1bd9d53a4b85f8.camel@fb.com --- tools/bpf/bpftool/gen.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 91af2850b505..7678af364793 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -828,8 +828,10 @@ codegen_maps_skeleton(struct bpf_object *obj, size_t map_cnt, bool mmaped) s->map_cnt = %zu; \n\ s->map_skel_sz = sizeof(*s->maps); \n\ s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz);\n\ - if (!s->maps) \n\ + if (!s->maps) { \n\ + err = -ENOMEM; \n\ goto err; \n\ + } \n\ ", map_cnt ); @@ -870,8 +872,10 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li s->prog_cnt = %zu; \n\ s->prog_skel_sz = sizeof(*s->progs); \n\ s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz);\n\ - if (!s->progs) \n\ + if (!s->progs) { \n\ + err = -ENOMEM; \n\ goto err; \n\ + } \n\ ", prog_cnt ); @@ -1182,10 +1186,13 @@ static int do_skeleton(int argc, char **argv) %1$s__create_skeleton(struct %1$s *obj) \n\ { \n\ struct bpf_object_skeleton *s; \n\ + int err; \n\ \n\ s = (struct bpf_object_skeleton *)calloc(1, sizeof(*s));\n\ - if (!s) \n\ + if (!s) { \n\ + err = -ENOMEM; \n\ goto err; \n\ + } \n\ \n\ s->sz = sizeof(*s); \n\ s->name = \"%1$s\"; \n\ @@ -1206,7 +1213,7 @@ static int do_skeleton(int argc, char **argv) return 0; \n\ err: \n\ bpf_object__destroy_skeleton(s); \n\ - return -ENOMEM; \n\ + return err; \n\ } \n\ \n\ static inline const void *%2$s__elf_bytes(size_t *sz) \n\ @@ -1466,12 +1473,12 @@ static int do_subskeleton(int argc, char **argv) \n\ obj = (struct %1$s *)calloc(1, sizeof(*obj)); \n\ if (!obj) { \n\ - errno = ENOMEM; \n\ + err = -ENOMEM; \n\ goto err; \n\ } \n\ s = (struct bpf_object_subskeleton *)calloc(1, sizeof(*s));\n\ if (!s) { \n\ - errno = ENOMEM; \n\ + err = -ENOMEM; \n\ goto err; \n\ } \n\ s->sz = sizeof(*s); \n\ @@ -1483,7 +1490,7 @@ static int do_subskeleton(int argc, char **argv) s->var_cnt = %2$d; \n\ s->vars = (struct bpf_var_skeleton *)calloc(%2$d, sizeof(*s->vars));\n\ if (!s->vars) { \n\ - errno = ENOMEM; \n\ + err = -ENOMEM; \n\ goto err; \n\ } \n\ ", @@ -1538,6 +1545,7 @@ static int do_subskeleton(int argc, char **argv) return obj; \n\ err: \n\ %1$s__destroy(obj); \n\ + errno = -err; \n\ return NULL; \n\ } \n\ \n\ -- cgit From d31e0386a2f122b40b605eb0120a2fbcfca77868 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 30 Mar 2022 13:05:10 +0200 Subject: bpf: Fix sparse warnings in kprobe_multi_resolve_syms Adding missing __user tags to fix sparse warnings: kernel/trace/bpf_trace.c:2370:34: warning: incorrect type in argument 2 (different address spaces) kernel/trace/bpf_trace.c:2370:34: expected void const [noderef] __user *from kernel/trace/bpf_trace.c:2370:34: got void const *usyms kernel/trace/bpf_trace.c:2376:51: warning: incorrect type in argument 2 (different address spaces) kernel/trace/bpf_trace.c:2376:51: expected char const [noderef] __user *src kernel/trace/bpf_trace.c:2376:51: got char const * kernel/trace/bpf_trace.c:2443:49: warning: incorrect type in argument 1 (different address spaces) kernel/trace/bpf_trace.c:2443:49: expected void const *usyms kernel/trace/bpf_trace.c:2443:49: got void [noderef] __user *[assigned] usyms Fixes: 0dcac2725406 ("bpf: Add multi kprobe link") Reported-by: Alexei Starovoitov Reported-by: Jakub Kicinski Signed-off-by: Jiri Olsa Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20220330110510.398558-1-jolsa@kernel.org --- kernel/trace/bpf_trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 7fa2ebc07f60..d8553f46caa2 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2349,11 +2349,11 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long entry_ip, } static int -kprobe_multi_resolve_syms(const void *usyms, u32 cnt, +kprobe_multi_resolve_syms(const void __user *usyms, u32 cnt, unsigned long *addrs) { unsigned long addr, size; - const char **syms; + const char __user **syms; int err = -ENOMEM; unsigned int i; char *func; -- cgit From 2609f635a20d3691e7b5725edc3bdadb7bedf8fb Mon Sep 17 00:00:00 2001 From: Haowen Bai Date: Wed, 30 Mar 2022 09:59:48 +0800 Subject: selftests/bpf: Fix warning comparing pointer to 0 Avoid pointer type value compared with 0 to make code clear. Reported by coccicheck: tools/testing/selftests/bpf/progs/map_ptr_kern.c:370:21-22: WARNING comparing pointer to 0 tools/testing/selftests/bpf/progs/map_ptr_kern.c:397:21-22: WARNING comparing pointer to 0 Signed-off-by: Haowen Bai Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/1648605588-19269-1-git-send-email-baihaowen@meizu.com --- tools/testing/selftests/bpf/progs/map_ptr_kern.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/map_ptr_kern.c b/tools/testing/selftests/bpf/progs/map_ptr_kern.c index b64df94ec476..db388f593d0a 100644 --- a/tools/testing/selftests/bpf/progs/map_ptr_kern.c +++ b/tools/testing/selftests/bpf/progs/map_ptr_kern.c @@ -367,7 +367,7 @@ static inline int check_array_of_maps(void) VERIFY(check_default(&array_of_maps->map, map)); inner_map = bpf_map_lookup_elem(array_of_maps, &key); - VERIFY(inner_map != 0); + VERIFY(inner_map != NULL); VERIFY(inner_map->map.max_entries == INNER_MAX_ENTRIES); return 1; @@ -394,7 +394,7 @@ static inline int check_hash_of_maps(void) VERIFY(check_default(&hash_of_maps->map, map)); inner_map = bpf_map_lookup_elem(hash_of_maps, &key); - VERIFY(inner_map != 0); + VERIFY(inner_map != NULL); VERIFY(inner_map->map.max_entries == INNER_MAX_ENTRIES); return 1; -- cgit From d049a24b15d8c1c90461933f0526b582a8cdf9b4 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 30 Mar 2022 09:04:44 -0700 Subject: drm/panel-edp: Fix AUO B133UAN01 panel id Two digits are swapped in the AUO B133UAN01 panel id (0x8495 instead of 0x8594). This went initially unnoticed because the panel is still detected, though it is set up with a conservative default timing. Fix the digit swap. Fixes: ec57376fba5a ("drm/panel-edp: Add AUO B133UAN01") Signed-off-by: Matthias Kaehlcke Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220330090435.1.Id1522250dd07a6b574c1cc7826023fc6acd141b4@changeid --- drivers/gpu/drm/panel/panel-edp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index db4eea1d7f67..1732b4f56e38 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1847,7 +1847,7 @@ static const struct panel_delay delay_100_500_e200 = { static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"), - EDP_PANEL_ENTRY('A', 'U', 'O', 0x8495, &delay_200_500_e50, "B133UAN01.0"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0786, &delay_200_500_p2e80, "NV116WHM-T01"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"), -- cgit From a2fb49833cad07a53651c23dce508127085fae2d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 31 Mar 2022 10:11:17 +0900 Subject: rethook: Fix to use WRITE_ONCE() for rethook:: Handler Since the function pointered by rethook::handler never be removed when the rethook is alive, it doesn't need to use rcu_assign_pointer() to update it. Just use WRITE_ONCE(). Reported-by: Alexei Starovoitov Signed-off-by: Masami Hiramatsu Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/164868907688.21983.1606862921419988152.stgit@devnote2 --- kernel/trace/rethook.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/rethook.c b/kernel/trace/rethook.c index ab463a4d2b23..b56833700d23 100644 --- a/kernel/trace/rethook.c +++ b/kernel/trace/rethook.c @@ -65,7 +65,7 @@ static void rethook_free_rcu(struct rcu_head *head) */ void rethook_free(struct rethook *rh) { - rcu_assign_pointer(rh->handler, NULL); + WRITE_ONCE(rh->handler, NULL); call_rcu(&rh->rcu, rethook_free_rcu); } -- cgit From 4a9c7bbe2ed4d2b240674b1fb606c41d3940c412 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 29 Mar 2022 18:14:56 -0700 Subject: bpf: Resolve to prog->aux->dst_prog->type only for BPF_PROG_TYPE_EXT The commit 7e40781cc8b7 ("bpf: verifier: Use target program's type for access verifications") fixes the verifier checking for BPF_PROG_TYPE_EXT (extension) prog such that the verifier looks for things based on the target prog type that it is extending instead of the BPF_PROG_TYPE_EXT itself. The current resolve_prog_type() returns the target prog type. It checks for nullness on prog->aux->dst_prog. However, when loading a BPF_PROG_TYPE_TRACING prog and it is tracing another bpf prog instead of a kernel function, prog->aux->dst_prog is not NULL also. In this case, the verifier should still verify as the BPF_PROG_TYPE_TRACING type instead of the traced prog type in prog->aux->dst_prog->type. An oops has been reported when tracing a struct_ops prog. A NULL dereference happened in check_return_code() when accessing the prog->aux->attach_func_proto->type and prog->aux->attach_func_proto is NULL here because the traced struct_ops prog has the "unreliable" set. This patch is to change the resolve_prog_type() to only return the target prog type if the prog being verified is BPF_PROG_TYPE_EXT. Fixes: 7e40781cc8b7 ("bpf: verifier: Use target program's type for access verifications") Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20220330011456.2984509-1-kafai@fb.com --- include/linux/bpf_verifier.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index c1fc4af47f69..3a9d2d7cc6b7 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -570,9 +570,11 @@ static inline u32 type_flag(u32 type) return type & ~BPF_BASE_TYPE_MASK; } +/* only use after check_attach_btf_id() */ static inline enum bpf_prog_type resolve_prog_type(struct bpf_prog *prog) { - return prog->aux->dst_prog ? prog->aux->dst_prog->type : prog->type; + return prog->type == BPF_PROG_TYPE_EXT ? + prog->aux->dst_prog->type : prog->type; } #endif /* _LINUX_BPF_VERIFIER_H */ -- cgit From 0a210af6d0a0595fef566e7eeb072f10f37774be Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 29 Mar 2022 18:15:02 -0700 Subject: bpf: selftests: Test fentry tracing a struct_ops program This patch tests attaching an fentry prog to a struct_ops prog. Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20220330011502.2985292-1-kafai@fb.com --- .../selftests/bpf/prog_tests/dummy_st_ops.c | 23 ++++++++++++++++++++++ .../selftests/bpf/progs/trace_dummy_st_ops.c | 21 ++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/trace_dummy_st_ops.c diff --git a/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c b/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c index 5aa52cc31dc2..c11832657d2b 100644 --- a/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c +++ b/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c @@ -2,6 +2,7 @@ /* Copyright (C) 2021. Huawei Technologies Co., Ltd */ #include #include "dummy_st_ops.skel.h" +#include "trace_dummy_st_ops.skel.h" /* Need to keep consistent with definition in include/linux/bpf.h */ struct bpf_dummy_ops_state { @@ -56,6 +57,7 @@ static void test_dummy_init_ptr_arg(void) .ctx_in = args, .ctx_size_in = sizeof(args), ); + struct trace_dummy_st_ops *trace_skel; struct dummy_st_ops *skel; int fd, err; @@ -64,12 +66,33 @@ static void test_dummy_init_ptr_arg(void) return; fd = bpf_program__fd(skel->progs.test_1); + + trace_skel = trace_dummy_st_ops__open(); + if (!ASSERT_OK_PTR(trace_skel, "trace_dummy_st_ops__open")) + goto done; + + err = bpf_program__set_attach_target(trace_skel->progs.fentry_test_1, + fd, "test_1"); + if (!ASSERT_OK(err, "set_attach_target(fentry_test_1)")) + goto done; + + err = trace_dummy_st_ops__load(trace_skel); + if (!ASSERT_OK(err, "load(trace_skel)")) + goto done; + + err = trace_dummy_st_ops__attach(trace_skel); + if (!ASSERT_OK(err, "attach(trace_skel)")) + goto done; + err = bpf_prog_test_run_opts(fd, &attr); ASSERT_OK(err, "test_run"); ASSERT_EQ(in_state.val, 0x5a, "test_ptr_ret"); ASSERT_EQ(attr.retval, exp_retval, "test_ret"); + ASSERT_EQ(trace_skel->bss->val, exp_retval, "fentry_val"); +done: dummy_st_ops__destroy(skel); + trace_dummy_st_ops__destroy(trace_skel); } static void test_dummy_multiple_args(void) diff --git a/tools/testing/selftests/bpf/progs/trace_dummy_st_ops.c b/tools/testing/selftests/bpf/progs/trace_dummy_st_ops.c new file mode 100644 index 000000000000..00a4be9d3074 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/trace_dummy_st_ops.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +int val = 0; + +SEC("fentry/test_1") +int BPF_PROG(fentry_test_1, __u64 *st_ops_ctx) +{ + __u64 state; + + /* Read the traced st_ops arg1 which is a pointer */ + bpf_probe_read_kernel(&state, sizeof(__u64), (void *)st_ops_ctx); + /* Read state->val */ + bpf_probe_read_kernel(&val, sizeof(__u32), (void *)state); + + return 0; +} + +char _license[] SEC("license") = "GPL"; -- cgit From 018ad18f6ab062a198627ec1bd5fd9d797c90238 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 28 Mar 2022 14:43:01 +0200 Subject: drm/atomic: Print the state every non-blocking commit The DRM_UT_STATE controls whether we're calling drm_atomic_print_new_state() whenever a new state is committed. However, that call is made in the drm_mode_atomic_ioctl(), whereas we have multiple users of the drm_atomic_commit() function in the kernel (framebuffer emulation, drm_atomic_helper_dirtyfb, etc.). This leads to multiple states being committed but never actually displayed even though we asked to have verbose atomic state debugging. Let's move the call to drm_atomic_print_new_state() to drm_atomic_commit() to make sure we don't miss any. Non-blocking commits were never logged though, and it would create too much churn in the logs to do so, so leave them out for now. Signed-off-by: Maxime Ripard Reviewed-by: Daniel Vetter Link: https://lore.kernel.org/r/20220328124304.2309418-2-maxime@cerno.tech --- drivers/gpu/drm/drm_atomic.c | 4 ++++ drivers/gpu/drm/drm_atomic_uapi.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 88cd992df356..637df126c2d9 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1423,8 +1423,12 @@ EXPORT_SYMBOL(drm_atomic_check_only); int drm_atomic_commit(struct drm_atomic_state *state) { struct drm_mode_config *config = &state->dev->mode_config; + struct drm_printer p = drm_info_printer(state->dev->dev); int ret; + if (drm_debug_enabled(DRM_UT_STATE)) + drm_atomic_print_new_state(state, &p); + ret = drm_atomic_check_only(state); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 9781722519c3..45e6d3c62a9a 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -1326,7 +1326,6 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, struct drm_out_fence_state *fence_state; int ret = 0; unsigned int i, j, num_fences; - struct drm_printer p = drm_info_printer(dev->dev); /* disallow for drivers not supporting atomic: */ if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) @@ -1458,9 +1457,6 @@ retry: } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { ret = drm_atomic_nonblocking_commit(state); } else { - if (drm_debug_enabled(DRM_UT_STATE)) - drm_atomic_print_new_state(state, &p); - ret = drm_atomic_commit(state); } -- cgit From 97a1f01b3f2f734bd3982aa5639b2b54632f8f7d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 28 Mar 2022 14:43:02 +0200 Subject: drm/atomic: Add atomic_print_state to private objects A number of drivers (amdgpu, komeda, vc4, etc.) leverage the drm_private_state structure, but we don't have any infrastructure to provide debugging like we do for the other components state. Let's add an atomic_print_state hook to be consistent. Reviewed-by: Daniel Vetter Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220328124304.2309418-3-maxime@cerno.tech --- drivers/gpu/drm/drm_atomic.c | 16 ++++++++++++++++ include/drm/drm_atomic.h | 27 +++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 637df126c2d9..58c0283fb6b0 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -789,6 +789,8 @@ drm_atomic_private_obj_init(struct drm_device *dev, obj->state = state; obj->funcs = funcs; list_add_tail(&obj->head, &dev->mode_config.privobj_list); + + state->obj = obj; } EXPORT_SYMBOL(drm_atomic_private_obj_init); @@ -1636,6 +1638,15 @@ commit: } EXPORT_SYMBOL(__drm_atomic_helper_set_config); +static void drm_atomic_private_obj_print_state(struct drm_printer *p, + const struct drm_private_state *state) +{ + struct drm_private_obj *obj = state->obj; + + if (obj->funcs->atomic_print_state) + obj->funcs->atomic_print_state(p, state); +} + /** * drm_atomic_print_new_state - prints drm atomic state * @state: atomic configuration to check @@ -1656,6 +1667,8 @@ void drm_atomic_print_new_state(const struct drm_atomic_state *state, struct drm_crtc_state *crtc_state; struct drm_connector *connector; struct drm_connector_state *connector_state; + struct drm_private_obj *obj; + struct drm_private_state *obj_state; int i; if (!p) { @@ -1673,6 +1686,9 @@ void drm_atomic_print_new_state(const struct drm_atomic_state *state, for_each_new_connector_in_state(state, connector, connector_state, i) drm_atomic_connector_print_state(p, connector_state); + + for_each_new_private_obj_in_state(state, obj, obj_state, i) + drm_atomic_private_obj_print_state(p, obj_state); } EXPORT_SYMBOL(drm_atomic_print_new_state); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 1701c2128a5c..0777725085df 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -227,6 +227,18 @@ struct drm_private_state_funcs { */ void (*atomic_destroy_state)(struct drm_private_obj *obj, struct drm_private_state *state); + + /** + * @atomic_print_state: + * + * If driver subclasses &struct drm_private_state, it should implement + * this optional hook for printing additional driver specific state. + * + * Do not call this directly, use drm_atomic_private_obj_print_state() + * instead. + */ + void (*atomic_print_state)(struct drm_printer *p, + const struct drm_private_state *state); }; /** @@ -311,14 +323,21 @@ struct drm_private_obj { /** * struct drm_private_state - base struct for driver private object state - * @state: backpointer to global drm_atomic_state * - * Currently only contains a backpointer to the overall atomic update, but in - * the future also might hold synchronization information similar to e.g. - * &drm_crtc.commit. + * Currently only contains a backpointer to the overall atomic update, + * and the relevant private object but in the future also might hold + * synchronization information similar to e.g. &drm_crtc.commit. */ struct drm_private_state { + /** + * @state: backpointer to global drm_atomic_state + */ struct drm_atomic_state *state; + + /** + * @obj: backpointer to the private object + */ + struct drm_private_obj *obj; }; struct __drm_private_objs_state { -- cgit From 220f125cd5153f570022b11a89d2f96b0e0979cf Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 28 Mar 2022 14:43:03 +0200 Subject: drm/vc4: Constify private state accessors None of those helpers modify the pointed data, let's make them const. Acked-by: Daniel Vetter Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220328124304.2309418-4-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_kms.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 24de29bc1cda..26b771c919b1 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -32,7 +32,8 @@ struct vc4_ctm_state { int fifo; }; -static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv) +static struct vc4_ctm_state * +to_vc4_ctm_state(const struct drm_private_state *priv) { return container_of(priv, struct vc4_ctm_state, base); } @@ -49,7 +50,7 @@ struct vc4_hvs_state { }; static struct vc4_hvs_state * -to_vc4_hvs_state(struct drm_private_state *priv) +to_vc4_hvs_state(const struct drm_private_state *priv) { return container_of(priv, struct vc4_hvs_state, base); } @@ -61,7 +62,7 @@ struct vc4_load_tracker_state { }; static struct vc4_load_tracker_state * -to_vc4_load_tracker_state(struct drm_private_state *priv) +to_vc4_load_tracker_state(const struct drm_private_state *priv) { return container_of(priv, struct vc4_load_tracker_state, base); } -- cgit From 66bfe59dd307b0b601ef721d348d850c8bf2396e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 28 Mar 2022 14:43:04 +0200 Subject: drm/vc4: Implement atomic_print_state for HVS channel state The HVS state configuration is useful when debugging what's going on in the vc4 hardware pipeline. Add an implementation of .atomic_print_state. Acked-by: Daniel Vetter Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220328124304.2309418-5-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_kms.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 26b771c919b1..bffd81d4bfcf 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -701,9 +701,26 @@ static void vc4_hvs_channels_destroy_state(struct drm_private_obj *obj, kfree(hvs_state); } +static void vc4_hvs_channels_print_state(struct drm_printer *p, + const struct drm_private_state *state) +{ + struct vc4_hvs_state *hvs_state = to_vc4_hvs_state(state); + unsigned int i; + + drm_printf(p, "HVS State\n"); + drm_printf(p, "\tCore Clock Rate: %lu\n", hvs_state->core_clock_rate); + + for (i = 0; i < HVS_NUM_CHANNELS; i++) { + drm_printf(p, "\tChannel %d\n", i); + drm_printf(p, "\t\tin use=%d\n", hvs_state->fifo_state[i].in_use); + drm_printf(p, "\t\tload=%lu\n", hvs_state->fifo_state[i].fifo_load); + } +} + static const struct drm_private_state_funcs vc4_hvs_state_funcs = { .atomic_duplicate_state = vc4_hvs_channels_duplicate_state, .atomic_destroy_state = vc4_hvs_channels_destroy_state, + .atomic_print_state = vc4_hvs_channels_print_state, }; static void vc4_hvs_channels_obj_fini(struct drm_device *dev, void *unused) -- cgit From 3aefc722ff52076407203b6af9713de567993adf Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 30 Mar 2022 20:04:26 +0300 Subject: drm/edid: fix invalid EDID extension block filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The invalid EDID block filtering uses the number of valid EDID extensions instead of all EDID extensions for looping the extensions in the copy. This is fine, by coincidence, if all the invalid blocks are at the end of the EDID. However, it's completely broken if there are invalid extensions in the middle; the invalid blocks are included and valid blocks are excluded. Fix it by modifying the base block after, not before, the copy. Fixes: 14544d0937bf ("drm/edid: Only print the bad edid when aborting") Reported-by: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20220330170426.349248-1-jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 359c52e5318a..18e1b88cd894 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2031,9 +2031,6 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, connector_bad_edid(connector, edid, edid[0x7e] + 1); - edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; - edid[0x7e] = valid_extensions; - new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, GFP_KERNEL); if (!new) @@ -2050,6 +2047,9 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, base += EDID_LENGTH; } + new[EDID_LENGTH - 1] += new[0x7e] - valid_extensions; + new[0x7e] = valid_extensions; + kfree(edid); edid = new; } -- cgit From 6f5efd118efafa22139e8670a4e4b506ba757dfd Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 26 Mar 2022 15:33:26 +0800 Subject: drm/bridge: anx7625: add missing destroy_workqueue() in anx7625_i2c_probe() Add the missing destroy_workqueue() before return from anx7625_i2c_probe() in the error handling case. Fixes: adca62ec370c ("drm/bridge: anx7625: Support reading edid through aux channel") Signed-off-by: Yang Yingliang Reviewed-by: Hsin-Yi Wang Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220326073326.3389347-1-yangyingliang@huawei.com --- drivers/gpu/drm/bridge/analogix/anx7625.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index dcf3275a00fe..6516f9570b86 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2660,7 +2660,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (ret) { if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); - return ret; + goto free_wq; } if (anx7625_register_i2c_dummy_clients(platform, client) != 0) { @@ -2675,7 +2675,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, pm_suspend_ignore_children(dev, true); ret = devm_add_action_or_reset(dev, anx7625_runtime_disable, dev); if (ret) - return ret; + goto free_wq; if (!platform->pdata.low_power_mode) { anx7625_disable_pd_protocol(platform); -- cgit From 7ed2b0dabf7a22874cb30f8878df239ef638eb53 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Mon, 21 Mar 2022 11:47:05 +0100 Subject: drm/bridge: adv7511: clean up CEC adapter when probe fails When the probe routine fails we also need to clean up the CEC adapter registered in adv7511_cec_init(). Fixes: 3b1b975003e4 ("drm: adv7511/33: add HDMI CEC support") Signed-off-by: Lucas Stach Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220321104705.2804423-1-l.stach@pengutronix.de --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 005bf18682ff..668dcefbae17 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1313,6 +1313,7 @@ err_unregister_audio: adv7511_audio_exit(adv7511); drm_bridge_remove(&adv7511->bridge); err_unregister_cec: + cec_unregister_adapter(adv7511->cec_adap); i2c_unregister_device(adv7511->i2c_cec); clk_disable_unprepare(adv7511->cec_clk); err_i2c_unregister_packet: -- cgit From e4ff77598a109bd36789ad5e80aba66fc53d0ffb Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 30 Mar 2022 19:21:23 +0530 Subject: powerpc/numa: Handle partially initialized numa nodes With commit 09f49dca570a ("mm: handle uninitialized numa nodes gracefully") NODE_DATA for even a memoryless/cpuless node is partially initialized at boot time. Before onlining the node, current Powerpc code checks for NODE_DATA to be NULL. However since NODE_DATA is partially initialized, this check will end up always being false. This causes hotplugging a CPU to a memoryless/cpuless node to fail. Before adding CPUs: $ numactl -H available: 1 nodes (4) node 4 cpus: 0 1 2 3 4 5 6 7 node 4 size: 97372 MB node 4 free: 95545 MB node distances: node 4 4: 10 $ lparstat System Configuration type=Dedicated mode=Capped smt=8 lcpu=1 mem=99709440 kB cpus=0 ent=1.00 %user %sys %wait %idle physc %entc lbusy app vcsw phint ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- 2.66 2.67 0.16 94.51 0.00 0.00 5.33 0.00 67749 0 After hotplugging 32 cores: $ numactl -H node 4 cpus: 0 1 2 3 4 5 6 7 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 node 4 size: 97372 MB node 4 free: 93636 MB node distances: node 4 4: 10 $ lparstat System Configuration type=Dedicated mode=Capped smt=8 lcpu=33 mem=99709440 kB cpus=0 ent=33.00 %user %sys %wait %idle physc %entc lbusy app vcsw phint ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- 0.04 0.02 0.00 99.94 0.00 0.00 0.06 0.00 1128751 3 As we can see numactl is listing only 8 cores while lparstat is showing 33 cores. Also dmesg is showing messages like: [ 2261.318350 ] BUG: arch topology borken [ 2261.318357 ] the DIE domain not a subset of the NODE domain Fixes: 09f49dca570a ("mm: handle uninitialized numa nodes gracefully") Reported-by: Geetika Moolchandani Signed-off-by: Srikar Dronamraju Acked-by: Michal Hocko Acked-by: Oscar Salvador Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20220330135123.1868197-1-srikar@linux.vnet.ibm.com --- arch/powerpc/mm/numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index b9b7fefbb64b..13022d734951 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1436,7 +1436,7 @@ int find_and_online_cpu_nid(int cpu) if (new_nid < 0 || !node_possible(new_nid)) new_nid = first_online_node; - if (NODE_DATA(new_nid) == NULL) { + if (!node_online(new_nid)) { #ifdef CONFIG_MEMORY_HOTPLUG /* * Need to ensure that NODE_DATA is initialized for a node from -- cgit From dd550c7cdbe3087adfd51360931938db4de8e832 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:05 +0200 Subject: dt-bindings: display: bridge: tc358867: Document DPI output support The TC358767/TC358867/TC9595 are all capable of operating in multiple modes, DPI-to-(e)DP, DSI-to-(e)DP, DSI-to-DPI. Document support for the DPI output port, which can now be connected both as input and output. Acked-by: Rob Herring Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Rob Herring Cc: Sam Ravnborg Cc: devicetree@vger.kernel.org To: dri-devel@lists.freedesktop.org Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-2-marex@denx.de --- .../devicetree/bindings/display/bridge/toshiba,tc358767.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml index f1541cc05297..5cfda6f2ba69 100644 --- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml @@ -61,8 +61,8 @@ properties: port@1: $ref: /schemas/graph.yaml#/properties/port description: | - DPI input port. The remote endpoint phandle should be a - reference to a valid DPI output endpoint node + DPI input/output port. The remote endpoint phandle should be a + reference to a valid DPI output or input endpoint node. port@2: $ref: /schemas/graph.yaml#/properties/port -- cgit From 9ba328035e68a2eae973f0c9fbb5df15f160612c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:06 +0200 Subject: dt-bindings: display: bridge: tc358867: Document DSI data-lanes property It is necessary to specify the number of connected/used DSI data lanes when using the DSI input port of this bridge. Document the 'data-lanes' property of the DSI input port. Reviewed-by: Rob Herring Acked-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Rob Herring Cc: Sam Ravnborg Cc: devicetree@vger.kernel.org To: dri-devel@lists.freedesktop.org Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-3-marex@denx.de --- .../bindings/display/bridge/toshiba,tc358767.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml index 5cfda6f2ba69..ed280053ec62 100644 --- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml @@ -53,11 +53,27 @@ properties: properties: port@0: - $ref: /schemas/graph.yaml#/properties/port + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: | DSI input port. The remote endpoint phandle should be a reference to a valid DSI output endpoint node + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: array of physical DSI data lane indexes. + minItems: 1 + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + port@1: $ref: /schemas/graph.yaml#/properties/port description: | -- cgit From a219062b9a87fd0a1591266413e62596d9bd8316 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:07 +0200 Subject: drm/bridge: tc358767: Change tc_ prefix to tc_edp_ for (e)DP specific functions These functions are specific to (e)DP output initialization and operation, add specific tc_edp_ prefix to those functions to discern them from DPI output functions that will be added later in this series. No functional change. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-4-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 7f9574b17caa..79691cd3b18c 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1164,7 +1164,7 @@ static int tc_main_link_disable(struct tc_data *tc) return regmap_write(tc->regmap, DP0CTL, 0); } -static int tc_stream_enable(struct tc_data *tc) +static int tc_edp_stream_enable(struct tc_data *tc) { int ret; u32 value; @@ -1219,7 +1219,7 @@ static int tc_stream_enable(struct tc_data *tc) return 0; } -static int tc_stream_disable(struct tc_data *tc) +static int tc_edp_stream_disable(struct tc_data *tc) { int ret; @@ -1234,7 +1234,7 @@ static int tc_stream_disable(struct tc_data *tc) return 0; } -static void tc_bridge_enable(struct drm_bridge *bridge) +static void tc_edp_bridge_enable(struct drm_bridge *bridge) { struct tc_data *tc = bridge_to_tc(bridge); int ret; @@ -1251,7 +1251,7 @@ static void tc_bridge_enable(struct drm_bridge *bridge) return; } - ret = tc_stream_enable(tc); + ret = tc_edp_stream_enable(tc); if (ret < 0) { dev_err(tc->dev, "main link stream start error: %d\n", ret); tc_main_link_disable(tc); @@ -1259,12 +1259,12 @@ static void tc_bridge_enable(struct drm_bridge *bridge) } } -static void tc_bridge_disable(struct drm_bridge *bridge) +static void tc_edp_bridge_disable(struct drm_bridge *bridge) { struct tc_data *tc = bridge_to_tc(bridge); int ret; - ret = tc_stream_disable(tc); + ret = tc_edp_stream_disable(tc); if (ret < 0) dev_err(tc->dev, "main link stream stop error: %d\n", ret); @@ -1285,9 +1285,10 @@ static bool tc_bridge_mode_fixup(struct drm_bridge *bridge, return true; } -static enum drm_mode_status tc_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) +static enum drm_mode_status +tc_edp_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { struct tc_data *tc = bridge_to_tc(bridge); u32 req, avail; @@ -1395,8 +1396,8 @@ static const struct drm_connector_funcs tc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int tc_bridge_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) +static int tc_edp_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; struct tc_data *tc = bridge_to_tc(bridge); @@ -1448,18 +1449,18 @@ aux_unregister: return ret; } -static void tc_bridge_detach(struct drm_bridge *bridge) +static void tc_edp_bridge_detach(struct drm_bridge *bridge) { drm_dp_aux_unregister(&bridge_to_tc(bridge)->aux); } -static const struct drm_bridge_funcs tc_bridge_funcs = { - .attach = tc_bridge_attach, - .detach = tc_bridge_detach, - .mode_valid = tc_mode_valid, +static const struct drm_bridge_funcs tc_edp_bridge_funcs = { + .attach = tc_edp_bridge_attach, + .detach = tc_edp_bridge_detach, + .mode_valid = tc_edp_mode_valid, .mode_set = tc_bridge_mode_set, - .enable = tc_bridge_enable, - .disable = tc_bridge_disable, + .enable = tc_edp_bridge_enable, + .disable = tc_edp_bridge_disable, .mode_fixup = tc_bridge_mode_fixup, .detect = tc_bridge_detect, .get_edid = tc_get_edid, @@ -1696,7 +1697,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) tc->aux.transfer = tc_aux_transfer; drm_dp_aux_init(&tc->aux); - tc->bridge.funcs = &tc_bridge_funcs; + tc->bridge.funcs = &tc_edp_bridge_funcs; if (tc->hpd_pin >= 0) tc->bridge.ops |= DRM_BRIDGE_OP_DETECT; tc->bridge.ops |= DRM_BRIDGE_OP_EDID; -- cgit From f5be62394dec1d05ba662c2068b0e512c29e1c36 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:08 +0200 Subject: drm/bridge: tc358767: Convert to atomic ops Use the atomic version of the enable/disable operations to continue the transition to the atomic API. This will be needed to access the mode from the atomic state. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-5-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 79691cd3b18c..780fb94609f1 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1234,7 +1234,9 @@ static int tc_edp_stream_disable(struct tc_data *tc) return 0; } -static void tc_edp_bridge_enable(struct drm_bridge *bridge) +static void +tc_edp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct tc_data *tc = bridge_to_tc(bridge); int ret; @@ -1259,7 +1261,9 @@ static void tc_edp_bridge_enable(struct drm_bridge *bridge) } } -static void tc_edp_bridge_disable(struct drm_bridge *bridge) +static void +tc_edp_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct tc_data *tc = bridge_to_tc(bridge); int ret; @@ -1459,11 +1463,14 @@ static const struct drm_bridge_funcs tc_edp_bridge_funcs = { .detach = tc_edp_bridge_detach, .mode_valid = tc_edp_mode_valid, .mode_set = tc_bridge_mode_set, - .enable = tc_edp_bridge_enable, - .disable = tc_edp_bridge_disable, + .atomic_enable = tc_edp_bridge_atomic_enable, + .atomic_disable = tc_edp_bridge_atomic_disable, .mode_fixup = tc_bridge_mode_fixup, .detect = tc_bridge_detect, .get_edid = tc_get_edid, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, }; static bool tc_readable_reg(struct device *dev, unsigned int reg) -- cgit From 65fdbb714d7f5764b6571b86f0617782cbe28ab1 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:09 +0200 Subject: drm/bridge: tc358767: Implement atomic_check callback Implement .atomic_check callback which prevents user space from setting unsupported mode. The tc_edp_common_atomic_check() variant is already prepared for DSI-to-DPI mode addition, which has different frequency limits. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-6-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 780fb94609f1..59cba91554b4 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1289,6 +1289,31 @@ static bool tc_bridge_mode_fixup(struct drm_bridge *bridge, return true; } +static int tc_common_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + const unsigned int max_khz) +{ + tc_bridge_mode_fixup(bridge, &crtc_state->mode, + &crtc_state->adjusted_mode); + + if (crtc_state->adjusted_mode.clock > max_khz) + return -EINVAL; + + return 0; +} + +static int tc_edp_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + /* DPI->(e)DP interface clock limitation: upto 154 MHz */ + return tc_common_atomic_check(bridge, bridge_state, crtc_state, + conn_state, 154000); +} + static enum drm_mode_status tc_edp_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -1463,6 +1488,7 @@ static const struct drm_bridge_funcs tc_edp_bridge_funcs = { .detach = tc_edp_bridge_detach, .mode_valid = tc_edp_mode_valid, .mode_set = tc_bridge_mode_set, + .atomic_check = tc_edp_atomic_check, .atomic_enable = tc_edp_bridge_atomic_enable, .atomic_disable = tc_edp_bridge_atomic_disable, .mode_fixup = tc_bridge_mode_fixup, -- cgit From 8478095a8c4bcea3c83b0767d6c9127434160761 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:10 +0200 Subject: drm/bridge: tc358767: Move (e)DP bridge endpoint parsing into dedicated function The TC358767/TC358867/TC9595 are all capable of operating in multiple modes, DPI-to-(e)DP, DSI-to-(e)DP, DSI-to-DPI. Only the first mode is currently supported. In order to support the rest of the modes without making the tc_probe() overly long, split the bridge endpoint parsing into dedicated function, where the necessary logic to detect the bridge mode based on which endpoints are connected, can be implemented. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-7-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 59cba91554b4..1a7755396282 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1583,19 +1583,12 @@ static irqreturn_t tc_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) { - struct device *dev = &client->dev; + struct device *dev = tc->dev; struct drm_panel *panel; - struct tc_data *tc; int ret; - tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); - if (!tc) - return -ENOMEM; - - tc->dev = dev; - /* port@2 is the output port */ ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL); if (ret && ret != -ENODEV) @@ -1614,6 +1607,25 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; } + return ret; +} + +static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tc_data *tc; + int ret; + + tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); + if (!tc) + return -ENOMEM; + + tc->dev = dev; + + ret = tc_probe_edp_bridge_endpoint(tc); + if (ret) + return ret; + /* Shut down GPIO is optional */ tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tc->sd_gpio)) -- cgit From 824c7bb46c9c2b5fbdc910425357fe863db93c26 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:11 +0200 Subject: drm/bridge: tc358767: Wrap (e)DP aux I2C registration into tc_aux_link_setup() This bit of code is (e)DP and aux I2C link specific, move it into tc_aux_link_setup() to permit cleaner addition of DSI-to-DPI mode. No functional change. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-8-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 1a7755396282..1f4b962502c7 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -656,6 +656,12 @@ static int tc_aux_link_setup(struct tc_data *tc) if (ret) goto err; + /* Register DP AUX channel */ + tc->aux.name = "TC358767 AUX i2c adapter"; + tc->aux.dev = tc->dev; + tc->aux.transfer = tc_aux_transfer; + drm_dp_aux_init(&tc->aux); + return 0; err: dev_err(tc->dev, "tc_aux_link_setup failed: %d\n", ret); @@ -1736,12 +1742,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) return ret; - /* Register DP AUX channel */ - tc->aux.name = "TC358767 AUX i2c adapter"; - tc->aux.dev = tc->dev; - tc->aux.transfer = tc_aux_transfer; - drm_dp_aux_init(&tc->aux); - tc->bridge.funcs = &tc_edp_bridge_funcs; if (tc->hpd_pin >= 0) tc->bridge.ops |= DRM_BRIDGE_OP_DETECT; -- cgit From dd1fd5ab7cd6dca15af6553b936a08cfc354e406 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:12 +0200 Subject: drm/bridge: tc358767: Move bridge ops setup into tc_probe_edp_bridge_endpoint() The bridge ops are specific to the bridge configuration, move them into tc_probe_edp_bridge_endpoint() to permit cleaner addition of DSI-to-DPI mode. No functional change. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-9-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 1f4b962502c7..470f3e9c76fc 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1613,6 +1613,11 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; } + tc->bridge.funcs = &tc_edp_bridge_funcs; + if (tc->hpd_pin >= 0) + tc->bridge.ops |= DRM_BRIDGE_OP_DETECT; + tc->bridge.ops |= DRM_BRIDGE_OP_EDID; + return ret; } @@ -1742,11 +1747,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) return ret; - tc->bridge.funcs = &tc_edp_bridge_funcs; - if (tc->hpd_pin >= 0) - tc->bridge.ops |= DRM_BRIDGE_OP_DETECT; - tc->bridge.ops |= DRM_BRIDGE_OP_EDID; - tc->bridge.of_node = dev->of_node; drm_bridge_add(&tc->bridge); -- cgit From 71f7d9c03118fc98c487e7ecd3a039da9c1cc513 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:13 +0200 Subject: drm/bridge: tc358767: Detect bridge mode from connected endpoints in DT The TC358767/TC358867/TC9595 are all capable of operating in multiple modes, DPI-to-(e)DP, DSI-to-(e)DP, DSI-to-DPI. Only the first mode is currently supported. It is possible to find out the mode in which the bridge should be operated by testing connected endpoints in DT. Port allocation: port@0 - DSI input port@1 - DPI input/output port@2 - eDP output Possible connections: DPI -> port@1 -> port@2 -> eDP :: [port@0 is not connected] DSI -> port@0 -> port@2 -> eDP :: [port@1 is not connected] DSI -> port@0 -> port@1 -> DPI :: [port@2 is not connected] Add function to determine the bridge mode based on connected endpoints. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-10-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 470f3e9c76fc..074efe24ac61 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1621,6 +1621,50 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) return ret; } +static int tc_probe_bridge_endpoint(struct tc_data *tc) +{ + struct device *dev = tc->dev; + struct of_endpoint endpoint; + struct device_node *node = NULL; + const u8 mode_dpi_to_edp = BIT(1) | BIT(2); + const u8 mode_dsi_to_edp = BIT(0) | BIT(2); + const u8 mode_dsi_to_dpi = BIT(0) | BIT(1); + u8 mode = 0; + + /* + * Determine bridge configuration. + * + * Port allocation: + * port@0 - DSI input + * port@1 - DPI input/output + * port@2 - eDP output + * + * Possible connections: + * DPI -> port@1 -> port@2 -> eDP :: [port@0 is not connected] + * DSI -> port@0 -> port@2 -> eDP :: [port@1 is not connected] + * DSI -> port@0 -> port@1 -> DPI :: [port@2 is not connected] + */ + + for_each_endpoint_of_node(dev->of_node, node) { + of_graph_parse_endpoint(node, &endpoint); + if (endpoint.port > 2) + return -EINVAL; + + mode |= BIT(endpoint.port); + } + + if (mode == mode_dpi_to_edp) + return tc_probe_edp_bridge_endpoint(tc); + else if (mode == mode_dsi_to_dpi) + dev_warn(dev, "The mode DSI-to-DPI is not supported!\n"); + else if (mode == mode_dsi_to_edp) + dev_warn(dev, "The mode DSI-to-(e)DP is not supported!\n"); + else + dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); + + return -EINVAL; +} + static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; @@ -1633,7 +1677,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) tc->dev = dev; - ret = tc_probe_edp_bridge_endpoint(tc); + ret = tc_probe_bridge_endpoint(tc); if (ret) return ret; -- cgit From aebe58a7af42d2126dc8ca39825cb5a624649115 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:14 +0200 Subject: drm/bridge: tc358767: Split tc_set_video_mode() into common and (e)DP part The tc_set_video_mode() sets up both common and (e)DP video mode settings of the bridge chip. Split the function into tc_set_common_video_mode() to set the common settings and tc_set_edp_video_mode() to set the (e)DP specific settings. No functional change. Reviewed-by: Lucas Stach Tested-by: Lucas Stach # In both DPI to eDP and DSI to DPI mode. Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-11-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 65 +++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 074efe24ac61..ba4e75c22030 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -734,33 +734,16 @@ err_dpcd_read: return ret; } -static int tc_set_video_mode(struct tc_data *tc, - const struct drm_display_mode *mode) +static int tc_set_common_video_mode(struct tc_data *tc, + const struct drm_display_mode *mode) { - int ret; - int vid_sync_dly; - int max_tu_symbol; - int left_margin = mode->htotal - mode->hsync_end; int right_margin = mode->hsync_start - mode->hdisplay; int hsync_len = mode->hsync_end - mode->hsync_start; int upper_margin = mode->vtotal - mode->vsync_end; int lower_margin = mode->vsync_start - mode->vdisplay; int vsync_len = mode->vsync_end - mode->vsync_start; - u32 dp0_syncval; - u32 bits_per_pixel = 24; - u32 in_bw, out_bw; - - /* - * Recommended maximum number of symbols transferred in a transfer unit: - * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size, - * (output active video bandwidth in bytes)) - * Must be less than tu_size. - */ - - in_bw = mode->clock * bits_per_pixel / 8; - out_bw = tc->link.num_lanes * tc->link.rate; - max_tu_symbol = DIV_ROUND_UP(in_bw * TU_SIZE_RECOMMENDED, out_bw); + int ret; dev_dbg(tc->dev, "set mode %dx%d\n", mode->hdisplay, mode->vdisplay); @@ -818,8 +801,35 @@ static int tc_set_video_mode(struct tc_data *tc, FIELD_PREP(COLOR_B, 99) | ENI2CFILTER | FIELD_PREP(COLOR_BAR_MODE, COLOR_BAR_MODE_BARS)); - if (ret) - return ret; + + return ret; +} + +static int tc_set_edp_video_mode(struct tc_data *tc, + const struct drm_display_mode *mode) +{ + int ret; + int vid_sync_dly; + int max_tu_symbol; + + int left_margin = mode->htotal - mode->hsync_end; + int hsync_len = mode->hsync_end - mode->hsync_start; + int upper_margin = mode->vtotal - mode->vsync_end; + int vsync_len = mode->vsync_end - mode->vsync_start; + u32 dp0_syncval; + u32 bits_per_pixel = 24; + u32 in_bw, out_bw; + + /* + * Recommended maximum number of symbols transferred in a transfer unit: + * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size, + * (output active video bandwidth in bytes)) + * Must be less than tu_size. + */ + + in_bw = mode->clock * bits_per_pixel / 8; + out_bw = tc->link.num_lanes * tc->link.rate; + max_tu_symbol = DIV_ROUND_UP(in_bw * TU_SIZE_RECOMMENDED, out_bw); /* DP Main Stream Attributes */ vid_sync_dly = hsync_len + left_margin + mode->hdisplay; @@ -869,10 +879,7 @@ static int tc_set_video_mode(struct tc_data *tc, FIELD_PREP(MAX_TU_SYMBOL, max_tu_symbol) | FIELD_PREP(TU_SIZE, TU_SIZE_RECOMMENDED) | BPC_8); - if (ret) - return ret; - - return 0; + return ret; } static int tc_wait_link_training(struct tc_data *tc) @@ -1185,7 +1192,11 @@ static int tc_edp_stream_enable(struct tc_data *tc) return ret; } - ret = tc_set_video_mode(tc, &tc->mode); + ret = tc_set_common_video_mode(tc, &tc->mode); + if (ret) + return ret; + + ret = tc_set_edp_video_mode(tc, &tc->mode); if (ret) return ret; -- cgit From bbfd3190b65629117ae1535499e3de9791499ddc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Mar 2022 10:50:15 +0200 Subject: drm/bridge: tc358767: Add DSI-to-DPI mode support The TC358767/TC358867/TC9595 are all capable of operating in multiple modes, DPI-to-(e)DP, DSI-to-(e)DP, DSI-to-DPI. Add support for the DSI-to-DPI mode. This requires skipping most of the (e)DP initialization code, which is currently a large part of this driver, hence it is better to have far simpler separate tc_dpi_bridge_funcs and their implementation. The configuration of DPI output is also much simpler. The configuration of the DSI input is rather similar to the other TC bridge chips. The Pixel PLL in DPI output mode does not have the 65..150 MHz limitation imposed on the (e)DP output mode, so this limitation is skipped to permit operating panels with far slower pixel clock, even below 9 MHz. This mode of operation of the PLL is valid and tested. The detection of bridge mode is now added into tc_probe_bridge_mode(), where in case a DPI panel is found on port@1 endpoint@1, the mode is assumed to be DSI-to-DPI. If (e)DP is detected on port@2, the mode is assumed to be DPI-to-(e)DP. The DSI-to-(e)DP mode is not supported due to lack of proper hardware, but this would be some sort of mix between the two aforementioned modes. Reviewed-by: Lucas Stach Signed-off-by: Marek Vasut Cc: Jonas Karlman Cc: Laurent Pinchart Cc: Maxime Ripard Cc: Neil Armstrong Cc: Sam Ravnborg Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220329085015.39159-12-marex@denx.de --- drivers/gpu/drm/bridge/Kconfig | 1 + drivers/gpu/drm/bridge/tc358767.c | 358 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 348 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 1b8721a7a6df..e4cbf31797c5 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -267,6 +267,7 @@ config DRM_TOSHIBA_TC358767 select DRM_DP_HELPER select DRM_KMS_HELPER select REGMAP_I2C + select DRM_MIPI_DSI select DRM_PANEL help Toshiba TC358767 eDP bridge chip driver. diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index ba4e75c22030..12355daf45ef 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1,6 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * tc358767 eDP bridge driver + * TC358767/TC358867/TC9595 DSI/DPI-to-DPI/(e)DP bridge driver + * + * The TC358767/TC358867/TC9595 can operate in multiple modes. + * The following modes are supported: + * DPI->(e)DP -- supported + * DSI->DPI .... supported + * DSI->(e)DP .. NOT supported * * Copyright (C) 2016 CogentEmbedded Inc * Author: Andrey Gusakov @@ -29,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +43,35 @@ /* Registers */ -/* Display Parallel Interface */ +/* PPI layer registers */ +#define PPI_STARTPPI 0x0104 /* START control bit */ +#define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */ +#define LPX_PERIOD 3 +#define PPI_LANEENABLE 0x0134 +#define PPI_TX_RX_TA 0x013c +#define TTA_GET 0x40000 +#define TTA_SURE 6 +#define PPI_D0S_ATMR 0x0144 +#define PPI_D1S_ATMR 0x0148 +#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */ +#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */ +#define PPI_D2S_CLRSIPOCOUNT 0x016c /* Assertion timer for Lane 2 */ +#define PPI_D3S_CLRSIPOCOUNT 0x0170 /* Assertion timer for Lane 3 */ +#define PPI_START_FUNCTION BIT(0) + +/* DSI layer registers */ +#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */ +#define DSI_LANEENABLE 0x0210 /* Enables each lane */ +#define DSI_RX_START BIT(0) + +/* Lane enable PPI and DSI register bits */ +#define LANEENABLE_CLEN BIT(0) +#define LANEENABLE_L0EN BIT(1) +#define LANEENABLE_L1EN BIT(2) +#define LANEENABLE_L2EN BIT(1) +#define LANEENABLE_L3EN BIT(2) + +/* Display Parallel Input Interface */ #define DPIPXLFMT 0x0440 #define VS_POL_ACTIVE_LOW (1 << 10) #define HS_POL_ACTIVE_LOW (1 << 9) @@ -48,6 +83,14 @@ #define DPI_BPP_RGB666 (1 << 0) #define DPI_BPP_RGB565 (2 << 0) +/* Display Parallel Output Interface */ +#define POCTRL 0x0448 +#define POCTRL_S2P BIT(7) +#define POCTRL_PCLK_POL BIT(3) +#define POCTRL_VS_POL BIT(2) +#define POCTRL_HS_POL BIT(1) +#define POCTRL_DE_POL BIT(0) + /* Video Path */ #define VPCTRL0 0x0450 #define VSDELAY GENMASK(31, 20) @@ -247,6 +290,9 @@ struct tc_data { struct drm_bridge *panel_bridge; struct drm_connector connector; + struct mipi_dsi_device *dsi; + u8 dsi_lanes; + /* link settings */ struct tc_edp_link link; @@ -469,10 +515,24 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) int mul, best_mul = 1; int delta, best_delta; int ext_div[] = {1, 2, 3, 5, 7}; + int clk_min, clk_max; int best_pixelclock = 0; int vco_hi = 0; u32 pxl_pllparam; + /* + * refclk * mul / (ext_pre_div * pre_div) should be in range: + * - DPI ..... 0 to 100 MHz + * - (e)DP ... 150 to 650 MHz + */ + if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI) { + clk_min = 0; + clk_max = 100000000; + } else { + clk_min = 150000000; + clk_max = 650000000; + } + dev_dbg(tc->dev, "PLL: requested %d pixelclock, ref %d\n", pixelclock, refclk); best_delta = pixelclock; @@ -499,11 +559,7 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) continue; clk = (refclk / ext_div[i_pre] / div) * mul; - /* - * refclk * mul / (ext_pre_div * pre_div) - * should be in the 150 to 650 MHz range - */ - if ((clk > 650000000) || (clk < 150000000)) + if ((clk > clk_max) || (clk < clk_min)) continue; clk = clk / ext_div[i_post]; @@ -805,6 +861,20 @@ static int tc_set_common_video_mode(struct tc_data *tc, return ret; } +static int tc_set_dpi_video_mode(struct tc_data *tc, + const struct drm_display_mode *mode) +{ + u32 value = POCTRL_S2P; + + if (tc->mode.flags & DRM_MODE_FLAG_NHSYNC) + value |= POCTRL_HS_POL; + + if (tc->mode.flags & DRM_MODE_FLAG_NVSYNC) + value |= POCTRL_VS_POL; + + return regmap_write(tc->regmap, POCTRL, value); +} + static int tc_set_edp_video_mode(struct tc_data *tc, const struct drm_display_mode *mode) { @@ -1177,6 +1247,85 @@ static int tc_main_link_disable(struct tc_data *tc) return regmap_write(tc->regmap, DP0CTL, 0); } +static int tc_dpi_stream_enable(struct tc_data *tc) +{ + int ret; + u32 value; + + dev_dbg(tc->dev, "enable video stream\n"); + + /* Setup PLL */ + ret = tc_set_syspllparam(tc); + if (ret) + return ret; + + /* + * Initially PLLs are in bypass. Force PLL parameter update, + * disable PLL bypass, enable PLL + */ + ret = tc_pllupdate(tc, DP0_PLLCTRL); + if (ret) + return ret; + + ret = tc_pllupdate(tc, DP1_PLLCTRL); + if (ret) + return ret; + + /* Pixel PLL must always be enabled for DPI mode */ + ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), + 1000 * tc->mode.clock); + if (ret) + return ret; + + regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D0S_ATMR, 0); + regmap_write(tc->regmap, PPI_D1S_ATMR, 0); + regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE); + regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD); + + value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) | + LANEENABLE_CLEN; + regmap_write(tc->regmap, PPI_LANEENABLE, value); + regmap_write(tc->regmap, DSI_LANEENABLE, value); + + ret = tc_set_common_video_mode(tc, &tc->mode); + if (ret) + return ret; + + ret = tc_set_dpi_video_mode(tc, &tc->mode); + if (ret) + return ret; + + /* Set input interface */ + value = DP0_AUDSRC_NO_INPUT; + if (tc_test_pattern) + value |= DP0_VIDSRC_COLOR_BAR; + else + value |= DP0_VIDSRC_DSI_RX; + ret = regmap_write(tc->regmap, SYSCTRL, value); + if (ret) + return ret; + + usleep_range(120, 150); + + regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION); + regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START); + + return 0; +} + +static int tc_dpi_stream_disable(struct tc_data *tc) +{ + dev_dbg(tc->dev, "disable video stream\n"); + + tc_pxl_pll_dis(tc); + + return 0; +} + static int tc_edp_stream_enable(struct tc_data *tc) { int ret; @@ -1251,6 +1400,34 @@ static int tc_edp_stream_disable(struct tc_data *tc) return 0; } +static void +tc_dpi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) + +{ + struct tc_data *tc = bridge_to_tc(bridge); + int ret; + + ret = tc_dpi_stream_enable(tc); + if (ret < 0) { + dev_err(tc->dev, "main link stream start error: %d\n", ret); + tc_main_link_disable(tc); + return; + } +} + +static void +tc_dpi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct tc_data *tc = bridge_to_tc(bridge); + int ret; + + ret = tc_dpi_stream_disable(tc); + if (ret < 0) + dev_err(tc->dev, "main link stream stop error: %d\n", ret); +} + static void tc_edp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) @@ -1321,6 +1498,16 @@ static int tc_common_atomic_check(struct drm_bridge *bridge, return 0; } +static int tc_dpi_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + /* DSI->DPI interface clock limitation: upto 100 MHz */ + return tc_common_atomic_check(bridge, bridge_state, crtc_state, + conn_state, 100000); +} + static int tc_edp_atomic_check(struct drm_bridge *bridge, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, @@ -1331,6 +1518,18 @@ static int tc_edp_atomic_check(struct drm_bridge *bridge, conn_state, 154000); } +static enum drm_mode_status +tc_dpi_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + /* DPI interface clock limitation: upto 100 MHz */ + if (mode->clock > 100000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + static enum drm_mode_status tc_edp_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -1442,6 +1641,18 @@ static const struct drm_connector_funcs tc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static int tc_dpi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct tc_data *tc = bridge_to_tc(bridge); + + if (!tc->panel_bridge) + return 0; + + return drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge, + &tc->bridge, flags); +} + static int tc_edp_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { @@ -1500,6 +1711,45 @@ static void tc_edp_bridge_detach(struct drm_bridge *bridge) drm_dp_aux_unregister(&bridge_to_tc(bridge)->aux); } +#define MAX_INPUT_SEL_FORMATS 1 + +static u32 * +tc_dpi_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + /* This is the DSI-end bus format */ + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_input_fmts = 1; + + return input_fmts; +} + +static const struct drm_bridge_funcs tc_dpi_bridge_funcs = { + .attach = tc_dpi_bridge_attach, + .mode_valid = tc_dpi_mode_valid, + .mode_set = tc_bridge_mode_set, + .atomic_check = tc_dpi_atomic_check, + .atomic_enable = tc_dpi_bridge_atomic_enable, + .atomic_disable = tc_dpi_bridge_atomic_disable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_get_input_bus_fmts = tc_dpi_atomic_get_input_bus_fmts, +}; + static const struct drm_bridge_funcs tc_edp_bridge_funcs = { .attach = tc_edp_bridge_attach, .detach = tc_edp_bridge_detach, @@ -1600,6 +1850,82 @@ static irqreturn_t tc_irq_handler(int irq, void *arg) return IRQ_HANDLED; } +static int tc_mipi_dsi_host_attach(struct tc_data *tc) +{ + struct device *dev = tc->dev; + struct device_node *host_node; + struct device_node *endpoint; + struct mipi_dsi_device *dsi; + struct mipi_dsi_host *host; + const struct mipi_dsi_device_info info = { + .type = "tc358767", + .channel = 0, + .node = NULL, + }; + int dsi_lanes, ret; + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); + dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + host_node = of_graph_get_remote_port_parent(endpoint); + host = of_find_mipi_dsi_host_by_node(host_node); + of_node_put(host_node); + of_node_put(endpoint); + + if (dsi_lanes < 0 || dsi_lanes > 4) + return -EINVAL; + + if (!host) + return -EPROBE_DEFER; + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) + return dev_err_probe(dev, PTR_ERR(dsi), + "failed to create dsi device\n"); + + tc->dsi = dsi; + + tc->dsi_lanes = dsi_lanes; + dsi->lanes = tc->dsi_lanes; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "failed to attach dsi to host: %d\n", ret); + return ret; + } + + return 0; +} + +static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc) +{ + struct device *dev = tc->dev; + struct drm_panel *panel; + int ret; + + /* port@1 is the DPI input/output port */ + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL); + if (ret && ret != -ENODEV) + return ret; + + if (panel) { + struct drm_bridge *panel_bridge; + + panel_bridge = devm_drm_panel_bridge_add(dev, panel); + if (IS_ERR(panel_bridge)) + return PTR_ERR(panel_bridge); + + tc->panel_bridge = panel_bridge; + tc->bridge.type = DRM_MODE_CONNECTOR_DPI; + tc->bridge.funcs = &tc_dpi_bridge_funcs; + + return 0; + } + + return ret; +} + static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) { struct device *dev = tc->dev; @@ -1667,7 +1993,7 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) if (mode == mode_dpi_to_edp) return tc_probe_edp_bridge_endpoint(tc); else if (mode == mode_dsi_to_dpi) - dev_warn(dev, "The mode DSI-to-DPI is not supported!\n"); + return tc_probe_dpi_bridge_endpoint(tc); else if (mode == mode_dsi_to_edp) dev_warn(dev, "The mode DSI-to-(e)DP is not supported!\n"); else @@ -1798,15 +2124,25 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) } } - ret = tc_aux_link_setup(tc); - if (ret) - return ret; + if (tc->bridge.type != DRM_MODE_CONNECTOR_DPI) { /* (e)DP output */ + ret = tc_aux_link_setup(tc); + if (ret) + return ret; + } tc->bridge.of_node = dev->of_node; drm_bridge_add(&tc->bridge); i2c_set_clientdata(client, tc); + if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI) { /* DPI output */ + ret = tc_mipi_dsi_host_attach(tc); + if (ret) { + drm_bridge_remove(&tc->bridge); + return ret; + } + } + return 0; } -- cgit From b2831dd4be4e98c6f62976a821d7e852f0c9c08b Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Thu, 3 Mar 2022 22:06:49 +0530 Subject: drm: bridge: tc358764: Use drm panel_bridge API Replace the manual panel handling code by a drm panel_bridge via devm_drm_of_get_bridge(). Adding panel_bridge handling, - Drops drm_connector and related operations as drm_bridge_attach creates connector during attachment. - Drops panel pointer and panel healpers. This simplifies the driver and allows all components in the display pipeline to be treated as bridges. Signed-off-by: Jagan Teki Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220303163654.3381470-2-jagan@amarulasolutions.com --- drivers/gpu/drm/bridge/tc358764.c | 104 +++----------------------------------- 1 file changed, 6 insertions(+), 98 deletions(-) diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index c1e35bdf9232..dca41ed32f8a 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -16,14 +16,9 @@ #include