summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-09-08 16:51:58 +1000
committerDave Airlie <airlied@redhat.com>2026-09-08 21:05:35 +1000
commit483ece619b64a6596bdaf69479ea0ecbbd197a00 (patch)
tree44af1491a66bbce99c99396405552afb77f4512f
parent94376186f25bfb86842c9b7551d223415cc6e4f0 (diff)
parent99c95ce1b07081d7944d637ba7d72d835c0d520a (diff)
downloadlinux-next-483ece619b64a6596bdaf69479ea0ecbbd197a00.tar.gz
linux-next-483ece619b64a6596bdaf69479ea0ecbbd197a00.zip
Merge tag 'drm-misc-next-2026-09-03' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.4: UAPI Changes: colorop: - provide DRM_COLOR_OP_FIXED_MATRIX Cross-subsystem Changes: cgroups: - fix typos dma-buf: - fix typos sound: - adapt to changes in omapdrm Core Changes: atomic: - convert most of DRM from state reset callbacks to atomic_create_state - remove drm_simple_encoder_init(); update drivers buddy: - improve dirty-page tracking clients: - log: Improve vmap handling display: - export HDMI SCDC status data via debugfs edid: - parse AMD VSDB entries - parse ALLM/VRR capabilities log: - add drm_warn_ratelimited() sched: - add missing locking Driver Changes: amd: - display: Use AMD VSDB for FreeSync - display: Implement YUV-to-RGB with fixed-matrix colorop amdxdma: - various fixes ast: - support 256-byte EDID data bridge: - clean up redundant error reporting - ti-sn65dsi83: Simplify error condition logic hyperv_drm: - remove support for pre-Win10 hosts komeda: - fix usage of GLB_CORE_ID nouveau: - improve runtime PM on R570 GSP firmware - various fixes throughout the driver - dispnv50: Support 2.147 GHz pixel clock in GB20x omap: - report HDMI hotplug events to ASoC HDMI codec panel: - panel-edp: Support MNE007QS3-F, TM140VDXP15, and KD116N36-30NB-A001 - samsung-s6d16d0: Use mipi_dsi_*_multi() functions - support Ilitek ILI7836A OLED plus DT bindings - support Novatek NT36532 plus DT bindings - convert several drivers to managed cleanup - fix Kconfig selections panthor: - provide gpu_cache_flush tracepoint - improve dma_fence signalling latency - improve locking qaic: - reject BOs that exceed maximum page count - add missing include statements verisilicon: - fix hardware cursor offsets vkms: - implement YUV-to-RGB with fixed-matrix colorop Signed-off-by: Dave Airlie <airlied@redhat.com> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patch.msgid.link/20260903130548.GA91506@2a02-2455-9062-2500-3419-2212-e55c-8a45.dyn6.pyur.net
-rw-r--r--Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml58
-rw-r--r--Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml83
-rw-r--r--Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml2
-rw-r--r--Documentation/gpu/todo.rst15
-rw-r--r--MAINTAINERS22
-rw-r--r--drivers/accel/amdxdna/aie2_ctx.c9
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.c197
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.h5
-rw-r--r--drivers/accel/ethosu/ethosu_job.c4
-rw-r--r--drivers/accel/ivpu/ivpu_drv.h2
-rw-r--r--drivers/accel/qaic/qaic_data.c8
-rw-r--r--drivers/accel/qaic/qaic_debugfs.c1
-rw-r--r--drivers/accel/qaic/qaic_drv.c1
-rw-r--r--drivers/accel/qaic/qaic_ras.c1
-rw-r--r--drivers/accel/qaic/qaic_ssr.c1
-rw-r--r--drivers/accel/qaic/qaic_timesync.c1
-rw-r--r--drivers/accel/qaic/sahara.c1
-rw-r--r--drivers/dma-buf/dma-buf.c2
-rw-r--r--drivers/dma-buf/dma-resv.c2
-rw-r--r--drivers/gpu/buddy.c1344
-rw-r--r--drivers/gpu/drm/adp/adp_drv.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c28
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c85
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c27
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c253
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c16
-rw-r--r--drivers/gpu/drm/amd/display/dc/Makefile1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_edid_parser.c80
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_edid_parser.h44
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c121
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h10
-rw-r--r--drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h71
-rw-r--r--drivers/gpu/drm/amd/display/modules/color/color_gamma.c3
-rw-r--r--drivers/gpu/drm/arm/display/include/malidp_product.h10
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_crtc.c25
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_plane.c16
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c33
-rw-r--r--drivers/gpu/drm/arm/hdlcd_crtc.c4
-rw-r--r--drivers/gpu/drm/arm/malidp_crtc.c18
-rw-r--r--drivers/gpu/drm/arm/malidp_planes.c18
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c2
-rw-r--r--drivers/gpu/drm/ast/ast_dp.c24
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c18
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c19
-rw-r--r--drivers/gpu/drm/bridge/Kconfig7
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c4
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c1
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c8
-rw-r--r--drivers/gpu/drm/bridge/ite-it6505.c4
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c4
-rw-r--r--drivers/gpu/drm/bridge/samsung-dsim.c4
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c3
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c4
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c4
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c45
-rw-r--r--drivers/gpu/drm/clients/drm_log.c52
-rw-r--r--drivers/gpu/drm/display/drm_bridge_connector.c3
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_state_helper.c156
-rw-r--r--drivers/gpu/drm/display/drm_scdc_helper.c289
-rw-r--r--drivers/gpu/drm/drm_atomic.c4
-rw-r--r--drivers/gpu/drm/drm_atomic_uapi.c7
-rw-r--r--drivers/gpu/drm/drm_buddy.c3
-rw-r--r--drivers/gpu/drm/drm_colorop.c107
-rw-r--r--drivers/gpu/drm/drm_debugfs.c157
-rw-r--r--drivers/gpu/drm/drm_drv.c29
-rw-r--r--drivers/gpu/drm/drm_edid.c189
-rw-r--r--drivers/gpu/drm/drm_gem_atomic_helper.c80
-rw-r--r--drivers/gpu/drm/drm_of.c38
-rw-r--r--drivers/gpu/drm/drm_simple_kms_helper.c37
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c2
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c10
-rw-r--r--drivers/gpu/drm/gud/gud_drv.c2
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c2
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c9
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c2
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm.h1
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_modeset.c2
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_proto.c44
-rw-r--r--drivers/gpu/drm/imx/dc/dc-crtc.c2
-rw-r--r--drivers/gpu/drm/imx/dc/dc-kms.c8
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-crtc.c2
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-plane.c2
-rw-r--r--drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c16
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c4
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-ipu.c2
-rw-r--r--drivers/gpu/drm/kmb/kmb_crtc.c2
-rw-r--r--drivers/gpu/drm/kmb/kmb_dsi.c9
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c10
-rw-r--r--drivers/gpu/drm/meson/meson_crtc.c2
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.c11
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_dsi.c11
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.c11
-rw-r--r--drivers/gpu/drm/meson/meson_overlay.c2
-rw-r--r--drivers/gpu/drm/meson/meson_plane.c2
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.h4
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c15
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c18
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c18
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c2
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c2
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c20
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c16
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_kms.c15
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_kms.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dfp.c5
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head.c14
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/headca7d.c21
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h4
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c32
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.h10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c149
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h12
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c116
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_svm.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c35
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dsi.c63
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi.h6
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi4.c29
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi5.c37
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi_common.c14
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c15
-rw-r--r--drivers/gpu/drm/panel/Kconfig25
-rw-r--r--drivers/gpu/drm/panel/Makefile2
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c3
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83102.c24
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83112a.c24
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83112b.c23
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx8394.c26
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili7836a.c297
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9805.c21
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c12
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h1
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c16
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c6
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9881c.c15
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9882t.c24
-rw-r--r--drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c19
-rw-r--r--drivers/gpu/drm/panel/panel-jdi-lt070me05000.c32
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c20
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c20
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36532.c431
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6d16d0.c74
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c20
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c20
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c20
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c30
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c20
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c31
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c13
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c6
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.c12
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.h1
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c20
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c20
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c19
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c23
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-sofef00.c20
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c31
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c20
-rw-r--r--drivers/gpu/drm/panel/panel-sony-td4353-jdi.c20
-rw-r--r--drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c20
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-r66451.c23
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-rm69299.c21
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_devfreq.c2
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c1
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c2
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.h286
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c22
-rw-r--r--drivers/gpu/drm/panthor/panthor_gpu.c117
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c39
-rw-r--r--drivers/gpu/drm/panthor/panthor_pwr.c24
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.c496
-rw-r--r--drivers/gpu/drm/panthor/panthor_trace.h38
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c2
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c17
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c15
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c12
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c18
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.c18
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c10
-rw-r--r--drivers/gpu/drm/scheduler/tests/tests_basic.c2
-rw-r--r--drivers/gpu/drm/sitronix/st7571.c2
-rw-r--r--drivers/gpu/drm/sitronix/st7920.c12
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c22
-rw-r--r--drivers/gpu/drm/sprd/sprd_dpu.c2
-rw-r--r--drivers/gpu/drm/sti/sti_crtc.c2
-rw-r--r--drivers/gpu/drm/stm/ltdc.c4
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c1
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_layer.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_layer.c2
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_helper.h8
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_modeset.c64
-rw-r--r--drivers/gpu/drm/tegra/dc.c18
-rw-r--r--drivers/gpu/drm/tegra/dsi.c16
-rw-r--r--drivers/gpu/drm/tegra/plane.c28
-rw-r--r--drivers/gpu/drm/tegra/rgb.c15
-rw-r--r--drivers/gpu/drm/tests/drm_kunit_helpers.c2
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.c38
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.h2
-rw-r--r--drivers/gpu/drm/tidss/tidss_encoder.c10
-rw-r--r--drivers/gpu/drm/tiny/appletbdrm.c14
-rw-r--r--drivers/gpu/drm/tiny/bochs.c2
-rw-r--r--drivers/gpu/drm/tiny/cirrus-qemu.c2
-rw-r--r--drivers/gpu/drm/tiny/pixpaper.c2
-rw-r--r--drivers/gpu/drm/tiny/sharp-memory.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c3
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c2
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_mode.c2
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c14
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h2
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c15
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c2
-rw-r--r--drivers/gpu/drm/verisilicon/vs_crtc.c2
-rw-r--r--drivers/gpu/drm/verisilicon/vs_cursor_plane.c8
-rw-r--r--drivers/gpu/drm/verisilicon/vs_plane.c20
-rw-r--r--drivers/gpu/drm/verisilicon/vs_plane.h2
-rw-r--r--drivers/gpu/drm/verisilicon/vs_primary_plane.c7
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c14
-rwxr-xr-xdrivers/gpu/drm/vkms/tests/gen_yuv_conversion.py87
-rw-r--r--drivers/gpu/drm/vkms/tests/vkms_format_test.c40
-rw-r--r--drivers/gpu/drm/vkms/vkms_colorop.c38
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c6
-rw-r--r--drivers/gpu/drm/vkms/vkms_crtc.c18
-rw-r--r--drivers/gpu/drm/vkms/vkms_formats.c64
-rw-r--r--drivers/gpu/drm/vkms/vkms_formats.h2
-rw-r--r--drivers/gpu/drm/vkms/vkms_plane.c55
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c22
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.h2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c2
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_kms.c16
-rw-r--r--drivers/gpu/tests/gpu_buddy_test.c162
-rw-r--r--include/drm/display/drm_dp.h1
-rw-r--r--include/drm/display/drm_hdmi_state_helper.h3
-rw-r--r--include/drm/display/drm_scdc.h21
-rw-r--r--include/drm/display/drm_scdc_helper.h103
-rw-r--r--include/drm/drm_colorop.h127
-rw-r--r--include/drm/drm_connector.h69
-rw-r--r--include/drm/drm_gem_atomic_helper.h11
-rw-r--r--include/drm/drm_mipi_dbi.h2
-rw-r--r--include/drm/drm_print.h3
-rw-r--r--include/drm/drm_simple_kms_helper.h6
-rw-r--r--include/linux/gpu_buddy.h101
-rw-r--r--include/sound/omap-hdmi-audio.h1
-rw-r--r--include/uapi/drm/drm_mode.h12
-rw-r--r--kernel/cgroup/dmem.c4
-rw-r--r--sound/soc/ti/omap-hdmi.c52
272 files changed, 5621 insertions, 3288 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml
new file mode 100644
index 000000000000..962cf257ed8d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili7836a.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/ilitek,ili7836a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ilitek ILI7836A-based OLED panels
+
+maintainers:
+ - Neil Armstrong <neil.armstrong@linaro.org>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: retroidpocket,nova-panel
+ - const: ilitek,ili7836a
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel
+
+ vddio-supply: true
+ avdd-supply: true
+
+ backlight: true
+ port: true
+ reset-gpios: true
+ rotation: true
+
+required:
+ - compatible
+ - reg
+ - vddio-supply
+ - avdd-supply
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "retroidpocket,nova-panel", "ilitek,ili7836a";
+ reg = <0>;
+ vddio-supply = <&vdd_disp_1v8>;
+ avdd-supply = <&vdd_disp2_2v8>;
+ reset-gpios = <&tlmm 133 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml
new file mode 100644
index 000000000000..c1ab793dd718
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/novatek,nt36532.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Novatek NT36532 based DSI display Panels
+
+maintainers:
+ - Junjie Cao <junjie.cao@linux.dev>
+
+description: |
+ The NT36532 IC from Novatek is a Driver IC used to drive MIPI-DSI panels.
+
+allOf:
+ - $ref: panel-common-dual.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - csot,ppc100hb1-1
+ - const: novatek,nt36532
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+ description: phandle of gpio for reset line - This should be 8mA
+
+ vddio-supply:
+ description: regulator that supplies the I/O voltage
+
+ ports: true
+
+required:
+ - compatible
+ - reg
+ - vddio-supply
+ - reset-gpios
+ - ports
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "csot,ppc100hb1-1", "novatek,nt36532";
+ reg = <0>;
+
+ reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>;
+
+ vddio-supply = <&vreg_l12b_1p8>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ panel_in_0: endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ panel_in_1: endpoint {
+ remote-endpoint = <&dsi1_out>;
+ };
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml
index b271de575e15..f04702de1101 100644
--- a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml
+++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa5x01-ams561ra01.yaml
@@ -34,7 +34,7 @@ required:
- compatible
- reg
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 14cf37590fc7..b7351467dc74 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -29,21 +29,6 @@ refactorings already and are an expert in the specific area
Subsystem-wide refactorings
===========================
-Open-code drm_simple_encoder_init()
------------------------------------
-
-The helper drm_simple_encoder_init() was supposed to simplify encoder
-initialization. Instead it only added an intermediate layer between atomic
-modesetting and the DRM driver.
-
-The task here is to remove drm_simple_encoder_init(). Search for a driver
-that calls drm_simple_encoder_init() and inline the helper. The driver will
-also need its own instance of drm_encoder_funcs.
-
-Contact: Thomas Zimmermann, respective driver maintainer
-
-Level: Easy
-
Replace struct drm_simple_display_pipe with regular atomic helpers
------------------------------------------------------------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 248688a344f6..6abef8f1a59c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8340,6 +8340,13 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/novatek,nt36523.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt36523.c
+DRM DRIVER FOR NOVATEK NT36532 PANELS
+M: Junjie Cao <junjie.cao@linux.dev>
+S: Maintained
+T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F: Documentation/devicetree/bindings/display/panel/novatek,nt36532.yaml
+F: drivers/gpu/drm/panel/panel-novatek-nt36532.c
+
DRM DRIVER FOR NOVATEK NT36672A PANELS
M: Sumit Semwal <sumit.semwal@linaro.org>
S: Maintained
@@ -9069,6 +9076,21 @@ F: include/drm/drm_gpuvm.h
F: rust/helpers/drm_gpuvm.c
F: rust/kernel/drm/gpuvm/
+DRM GPUSVM
+M: Matthew Brost <matthew.brost@intel.com>
+R: Thomas Hellström <thomas.hellstrom@linux.intel.com>
+R: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
+R: Honglei Huang <honghuan@amd.com>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F: drivers/gpu/drm/drm_gpusvm.c
+F: drivers/gpu/drm/drm_pagemap.c
+F: drivers/gpu/drm/drm_pagemap_util.c
+F: include/drm/drm_gpusvm.h
+F: include/drm/drm_pagemap.h
+F: include/drm/drm_pagemap_util.h
+
DRM LOG
M: Jocelyn Falempe <jfalempe@redhat.com>
M: Javier Martinez Canillas <javierm@redhat.com>
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
index baf9a8b90a4d..164441e43940 100644
--- a/drivers/accel/amdxdna/aie2_ctx.c
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -799,6 +799,7 @@ free_cmd_bufs:
for (i = 0; i < ARRAY_SIZE(priv->cmd_buf); i++) {
if (!priv->cmd_buf[i])
continue;
+ amdxdna_gem_heap_free(client, priv->cmd_buf[i]);
drm_gem_object_put(to_gobj(priv->cmd_buf[i]));
}
amdxdna_gem_unpin(heap);
@@ -836,8 +837,14 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
drm_sched_fini(&hwctx->priv->sched);
aie2_ctx_syncobj_destroy(hwctx);
- for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++)
+ for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++) {
+ /*
+ * The open/close will never be called for driver allocated
+ * dev bo. Call amdxdna_gem_heap_free explicitly.
+ */
+ amdxdna_gem_heap_free(hwctx->client, hwctx->priv->cmd_buf[idx]);
drm_gem_object_put(to_gobj(hwctx->priv->cmd_buf[idx]));
+ }
amdxdna_gem_unpin(hwctx->priv->heap);
drm_gem_object_put(to_gobj(hwctx->priv->heap));
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index 4b0d58d0329b..3f05fa8811af 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.c
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -37,6 +37,7 @@ amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo)
struct amdxdna_gem_obj *heap;
u64 heap_addr, exp_heap_uva;
u32 heap_id;
+ int ret;
if (xa_empty(&client->dev_heap_xa)) {
XDNA_DBG(xdna, "Empty heap xa");
@@ -58,24 +59,32 @@ amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo)
heap = xa_load(&client->dev_heap_xa, heap_id);
exp_heap_uva = amdxdna_gem_uva(heap);
heap_addr = amdxdna_gem_dev_addr(heap);
- dev_bo->heap_start_id = heap_id;
dev_bo->mem.uva = dev_bo->mm_node.start - heap_addr + exp_heap_uva;
for (; heap_id < client->dev_heap_nid; heap_id++) {
heap = xa_load(&client->dev_heap_xa, heap_id);
if (!heap) {
XDNA_ERR(xdna, "Failed to load heap %d", heap_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto cleanup_heap_xa;
}
heap_addr = amdxdna_gem_uva(heap);
if (heap_addr == AMDXDNA_INVALID_ADDR) {
XDNA_ERR(xdna, "Heap %d is not mapped", heap_id);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto cleanup_heap_xa;
}
if (heap_addr != exp_heap_uva) {
XDNA_ERR(xdna, "Heap %d uva is not contiguous", heap_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto cleanup_heap_xa;
+ }
+
+ ret = xa_insert(&dev_bo->heap_xa, heap_id, heap, GFP_KERNEL);
+ if (ret) {
+ ret = -ENOMEM;
+ goto cleanup_heap_xa;
}
if (heap->dev_addr + heap->mem.size >=
@@ -87,12 +96,15 @@ amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo)
if (heap_id == client->dev_heap_nid) {
XDNA_DBG(xdna, "Can not find heap end");
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto cleanup_heap_xa;
}
- dev_bo->heap_end_id = heap_id;
-
return 0;
+
+cleanup_heap_xa:
+ xa_destroy(&dev_bo->heap_xa);
+ return ret;
}
static int
@@ -132,8 +144,7 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
}
client->heap_usage += mem->size;
- xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
- abo->heap_start_id, abo->heap_end_id)
+ xa_for_each(&abo->heap_xa, heap_id, heap)
drm_gem_object_get(to_gobj(heap));
unlock_out:
@@ -142,22 +153,13 @@ unlock_out:
return ret;
}
-static void
-amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo)
+void amdxdna_gem_heap_free(struct amdxdna_client *client, struct amdxdna_gem_obj *abo)
{
- struct amdxdna_client *client = abo->client;
- struct amdxdna_gem_obj *heap;
- unsigned long heap_id;
-
mutex_lock(&client->mm_lock);
drm_mm_remove_node(&abo->mm_node);
client->heap_usage -= abo->mem.size;
- xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
- abo->heap_start_id, abo->heap_end_id)
- drm_gem_object_put(to_gobj(heap));
-
mutex_unlock(&client->mm_lock);
}
@@ -180,6 +182,7 @@ amdxdna_gem_create_obj(struct drm_device *dev, size_t size)
abo->open_ref = 0;
abo->internal = false;
INIT_LIST_HEAD(&abo->mem.umap_list);
+ xa_init_flags(&abo->heap_xa, XA_FLAGS_ALLOC);
return abo;
}
@@ -253,13 +256,16 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni,
struct amdxdna_gem_obj *abo = mapp->abo;
struct amdxdna_dev *xdna;
+ if (!mmu_notifier_range_blockable(range))
+ return false;
+
+ if (mapp->unmapped)
+ return true;
+
xdna = to_xdna_dev(to_gobj(abo)->dev);
XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d",
mapp->range.start, mapp->range.end, abo->type);
- if (!mmu_notifier_range_blockable(range))
- return false;
-
down_write(&xdna->notifier_lock);
abo->mem.map_invalid = true;
mapp->invalid = true;
@@ -301,33 +307,40 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo,
down_write(&xdna->notifier_lock);
list_for_each_entry(mapp, &abo->mem.umap_list, node) {
- if (!vma || compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) {
- if (!mapp->unmapped) {
- queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work);
- mapp->unmapped = true;
- }
- if (vma)
- break;
- }
+ if (!compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end))
+ continue;
+
+ queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work);
+ mapp->unmapped = true;
}
up_write(&xdna->notifier_lock);
}
-static void amdxdna_umap_release(struct kref *ref)
+static void amdxdna_hmm_unregister_all(struct amdxdna_gem_obj *abo)
{
- struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt);
- struct amdxdna_gem_obj *abo = mapp->abo;
- struct amdxdna_dev *xdna;
-
- mmu_interval_notifier_remove(&mapp->notifier);
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ struct amdxdna_umap *mapp, *tmp;
+ LIST_HEAD(dead);
- xdna = to_xdna_dev(to_gobj(mapp->abo)->dev);
down_write(&xdna->notifier_lock);
- list_del(&mapp->node);
- if (list_empty(&abo->mem.umap_list))
- abo->mem.uva = AMDXDNA_INVALID_ADDR;
+ list_for_each_entry_safe(mapp, tmp, &abo->mem.umap_list, node) {
+ mapp->unmapped = true;
+ mapp->cleanup = true;
+ list_move(&mapp->node, &dead);
+ }
up_write(&xdna->notifier_lock);
+ list_for_each_entry_safe(mapp, tmp, &dead, node) {
+ cancel_work_sync(&mapp->hmm_unreg_work);
+ amdxdna_umap_put(mapp);
+ }
+}
+
+static void amdxdna_umap_release(struct kref *ref)
+{
+ struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt);
+
+ mmu_interval_notifier_remove(&mapp->notifier);
kvfree(mapp->range.hmm_pfns);
kfree(mapp);
}
@@ -341,6 +354,20 @@ static void amdxdna_hmm_unreg_work(struct work_struct *work)
{
struct amdxdna_umap *mapp = container_of(work, struct amdxdna_umap,
hmm_unreg_work);
+ struct amdxdna_gem_obj *abo = mapp->abo;
+ struct amdxdna_dev *xdna;
+
+ xdna = to_xdna_dev(to_gobj(mapp->abo)->dev);
+ down_write(&xdna->notifier_lock);
+ if (mapp->cleanup) {
+ up_write(&xdna->notifier_lock);
+ return;
+ }
+
+ list_del(&mapp->node);
+ if (list_empty(&abo->mem.umap_list))
+ abo->mem.uva = AMDXDNA_INVALID_ADDR;
+ up_write(&xdna->notifier_lock);
amdxdna_umap_put(mapp);
}
@@ -425,13 +452,18 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj)
{
struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, amdxdna_gem_dev_addr(abo));
if (abo->pinned)
amdxdna_gem_unpin(abo);
amdxdna_gem_vunmap(abo);
- amdxdna_gem_heap_free(abo);
+ xa_for_each(&abo->heap_xa, heap_id, heap)
+ drm_gem_object_put(to_gobj(heap));
+ xa_destroy(&abo->heap_xa);
+
drm_gem_object_release(gobj);
amdxdna_gem_destroy_obj(abo);
}
@@ -643,8 +675,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
- amdxdna_hmm_unregister(abo, NULL);
- flush_workqueue(xdna->notifier_wq);
+ amdxdna_hmm_unregister_all(abo);
if (abo->pinned)
amdxdna_gem_unpin(abo);
@@ -734,19 +765,72 @@ static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map
drm_gem_shmem_object_vunmap(obj, map);
}
+static int amdxdna_gem_dev_obj_open(struct drm_gem_object *gobj, struct drm_file *filp)
+{
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+
+ guard(mutex)(&abo->lock);
+ if (filp->driver_priv != abo->client)
+ return -EPERM;
+ abo->open_ref++;
+
+ return 0;
+}
+
+static void amdxdna_gem_dev_obj_close(struct drm_gem_object *gobj, struct drm_file *filp)
+{
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+ struct amdxdna_client *client = NULL;
+
+ mutex_lock(&abo->lock);
+ abo->open_ref--;
+
+ /*
+ * Freeing the heap allocation here, when the handle is closed, is
+ * intentional. DEV BOs are carved out of a per-client drm_mm heap;
+ * any subsequent allocation that lands on the same device address will
+ * also belong to the same client. If the user closes the handle while
+ * a job is still in flight the only consequence is self-inflicted
+ * corruption within their own context -- it cannot affect other
+ * processes. The GEM reference held by the in-flight job keeps the
+ * amdxdna_gem_obj struct alive until the job completes; it does not
+ * prevent the device address from being reclaimed by the allocator.
+ *
+ * Cross-process handle creation for DEV BOs is rejected in
+ * amdxdna_gem_dev_obj_open(), which prevents the following UAF:
+ * if a second process shared the handle via GEM flink and the
+ * original creator exited (freeing client), the importer would later
+ * reach open_ref == 0 here and call amdxdna_gem_heap_free() with a
+ * dangling abo->client pointer. Because cross-process opens are
+ * rejected, the process arriving here is always the owning client,
+ * which is still alive. abo->client is nulled out afterwards so that
+ * any code path running on a lingering GEM reference (e.g. an
+ * in-flight job) cannot silently dereference a stale pointer.
+ */
+ if (abo->open_ref == 0) {
+ client = abo->client;
+ abo->client = NULL;
+ }
+ mutex_unlock(&abo->lock);
+
+ if (client)
+ amdxdna_gem_heap_free(client, abo);
+}
+
static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
struct amdxdna_gem_obj *heap;
+ unsigned long index = 0;
void *base;
u64 offset;
- /* vmap dev bo which is across more than 1 heap is not allowed */
- if (abo->heap_start_id != abo->heap_end_id)
+ heap = xa_find(&abo->heap_xa, &index, ULONG_MAX, XA_PRESENT);
+ if (!heap)
return -ENOMEM;
- heap = xa_load(&abo->client->dev_heap_xa, abo->heap_start_id);
- if (!heap)
+ /* vmap dev bo which is across more than 1 heap is not allowed */
+ if (xa_find_after(&abo->heap_xa, &index, ULONG_MAX, XA_PRESENT))
return -ENOMEM;
base = amdxdna_gem_vmap(heap);
@@ -765,6 +849,8 @@ static struct dma_buf *amdxdna_gem_dev_obj_export(struct drm_gem_object *gobj, i
static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = {
.free = amdxdna_gem_dev_obj_free,
+ .open = amdxdna_gem_dev_obj_open,
+ .close = amdxdna_gem_dev_obj_close,
.vmap = amdxdna_gem_dev_obj_vmap,
.export = amdxdna_gem_dev_obj_export,
};
@@ -1103,6 +1189,8 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
args->handle, args->type, amdxdna_gem_uva(abo),
amdxdna_gem_dev_addr(abo), abo->mem.size);
put_obj:
+ if (ret && abo->type == AMDXDNA_BO_DEV)
+ amdxdna_gem_heap_free(client, abo);
/* Dereference object reference. Handle holds it now. */
drm_gem_object_put(to_gobj(abo));
return ret;
@@ -1136,7 +1224,6 @@ static void amdxdna_bo_unpin(struct amdxdna_gem_obj *abo)
int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
{
- struct amdxdna_client *client = abo->client;
struct amdxdna_gem_obj *heap;
unsigned long heap_id, last = ULONG_MAX;
int ret = 0;
@@ -1144,17 +1231,15 @@ int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
if (abo->type != AMDXDNA_BO_DEV)
return amdxdna_bo_pin(abo);
- xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
- abo->heap_start_id, abo->heap_end_id) {
+ xa_for_each(&abo->heap_xa, heap_id, heap) {
ret = amdxdna_bo_pin(heap);
if (ret)
break;
last = heap_id;
}
- if (ret && last <= abo->heap_end_id) {
- xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
- abo->heap_start_id, last)
+ if (ret && last != ULONG_MAX) {
+ xa_for_each_range(&abo->heap_xa, heap_id, heap, 0, last)
amdxdna_bo_unpin(heap);
}
@@ -1179,8 +1264,7 @@ void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo)
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
- xa_for_each_range(&abo->client->dev_heap_xa, heap_id, heap,
- abo->heap_start_id, abo->heap_end_id)
+ xa_for_each(&abo->heap_xa, heap_id, heap)
amdxdna_bo_unpin(heap);
} else {
amdxdna_bo_unpin(abo);
@@ -1300,8 +1384,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
u64 flush_start = bo_start + args->offset;
u64 flush_end = flush_start + args->size;
- xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
- abo->heap_start_id, abo->heap_end_id) {
+ xa_for_each(&abo->heap_xa, heap_id, heap) {
u64 heap_start = amdxdna_gem_dev_addr(heap);
u64 heap_end = heap_start + heap->mem.size;
u64 start = max(flush_start, heap_start);
diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h
index 1e90e32bf3cd..5dfefdcf1356 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.h
+++ b/drivers/accel/amdxdna/amdxdna_gem.h
@@ -20,6 +20,7 @@ struct amdxdna_umap {
struct kref refcnt;
bool invalid;
bool unmapped;
+ bool cleanup;
};
struct amdxdna_mem {
@@ -46,8 +47,7 @@ struct amdxdna_gem_obj {
/* Below members are initialized when needed */
struct drm_mm_node mm_node; /* For AMDXDNA_BO_DEV */
- u32 heap_start_id;
- u32 heap_end_id;
+ struct xarray heap_xa;
u64 dev_addr; /* For heap bo */
u32 assigned_hwctx;
struct dma_buf *dma_buf;
@@ -104,6 +104,7 @@ static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo)
}
void amdxdna_umap_put(struct amdxdna_umap *mapp);
+void amdxdna_gem_heap_free(struct amdxdna_client *client, struct amdxdna_gem_obj *abo);
struct drm_gem_object *
amdxdna_gem_create_shmem_object_cb(struct drm_device *dev, size_t size);
diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c
index 4ced44a65f23..3bc25324690f 100644
--- a/drivers/accel/ethosu/ethosu_job.c
+++ b/drivers/accel/ethosu/ethosu_job.c
@@ -345,10 +345,8 @@ int ethosu_job_init(struct ethosu_device *edev)
ethosu_job_irq_handler_thread,
IRQF_SHARED, KBUILD_MODNAME,
edev);
- if (ret) {
- dev_err(dev, "failed to request irq\n");
+ if (ret)
return ret;
- }
edev->fence_context = dma_fence_context_alloc(1);
diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h
index 86d7c9966cac..8f29f1dc52b9 100644
--- a/drivers/accel/ivpu/ivpu_drv.h
+++ b/drivers/accel/ivpu/ivpu_drv.h
@@ -91,7 +91,7 @@
drm_warn(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
#define ivpu_warn_ratelimited(vdev, fmt, ...) \
- drm_err_ratelimited(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
+ drm_warn_ratelimited(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
#define ivpu_info(vdev, fmt, ...) drm_info(&(vdev)->drm, fmt, ##__VA_ARGS__)
diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c
index ef0351b6dc9b..4d55531bf1c9 100644
--- a/drivers/accel/qaic/qaic_data.c
+++ b/drivers/accel/qaic/qaic_data.c
@@ -15,6 +15,7 @@
#include <linux/math64.h>
#include <linux/mm.h>
#include <linux/moduleparam.h>
+#include <linux/overflow.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
@@ -455,7 +456,12 @@ static int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 s
int order;
if (size) {
- nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
+ u64 nr_pages_u64 = DIV_ROUND_UP_ULL(size, PAGE_SIZE);
+
+ if (overflows_type(nr_pages_u64, int))
+ return -E2BIG;
+
+ nr_pages = nr_pages_u64;
/*
* calculate how much extra we are going to allocate, to remove
* later
diff --git a/drivers/accel/qaic/qaic_debugfs.c b/drivers/accel/qaic/qaic_debugfs.c
index 95c78e12dd61..909a4d774d90 100644
--- a/drivers/accel/qaic/qaic_debugfs.c
+++ b/drivers/accel/qaic/qaic_debugfs.c
@@ -5,6 +5,7 @@
#include <linux/debugfs.h>
#include <linux/device.h>
+#include <linux/device-id/mhi.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/mhi.h>
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index 0acf6cdd44f8..c1bb6db78d12 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -4,6 +4,7 @@
/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/delay.h>
+#include <linux/device-id/mhi.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c
index e0205c2cda3d..236e520f708f 100644
--- a/drivers/accel/qaic/qaic_ras.c
+++ b/drivers/accel/qaic/qaic_ras.c
@@ -5,6 +5,7 @@
#include <asm/byteorder.h>
#include <linux/device.h>
+#include <linux/device-id/mhi.h>
#include <linux/kernel.h>
#include <linux/mhi.h>
diff --git a/drivers/accel/qaic/qaic_ssr.c b/drivers/accel/qaic/qaic_ssr.c
index a98928654959..418f14936f9a 100644
--- a/drivers/accel/qaic/qaic_ssr.c
+++ b/drivers/accel/qaic/qaic_ssr.c
@@ -8,6 +8,7 @@
#include <drm/drm_managed.h>
#include <linux/devcoredump.h>
#include <linux/device.h>
+#include <linux/device-id/mhi.h>
#include <linux/kernel.h>
#include <linux/mhi.h>
#include <linux/workqueue.h>
diff --git a/drivers/accel/qaic/qaic_timesync.c b/drivers/accel/qaic/qaic_timesync.c
index 45e5f0728ebe..b5162677ccc4 100644
--- a/drivers/accel/qaic/qaic_timesync.c
+++ b/drivers/accel/qaic/qaic_timesync.c
@@ -2,6 +2,7 @@
/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
+#include <linux/device-id/mhi.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/math64.h>
diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c
index c7c0b3eb4b65..8476be9b9148 100644
--- a/drivers/accel/qaic/sahara.c
+++ b/drivers/accel/qaic/sahara.c
@@ -3,6 +3,7 @@
/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/devcoredump.h>
+#include <linux/device-id/mhi.h>
#include <linux/firmware.h>
#include <linux/limits.h>
#include <linux/mhi.h>
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 4c9add51f9ef..36cdc342627e 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -260,7 +260,7 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
/* only support discovering the end of the buffer,
* but also allow SEEK_SET to maintain the idiomatic
- * SEEK_END(0), SEEK_CUR(0) pattern.
+ * SEEK_END(0), SEEK_SET(0) pattern.
*/
if (whence == SEEK_END)
base = dmabuf->size;
diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
index 39a92d9f2413..2365a6d8ca15 100644
--- a/drivers/dma-buf/dma-resv.c
+++ b/drivers/dma-buf/dma-resv.c
@@ -633,7 +633,7 @@ int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage,
int r;
r = dma_resv_get_fences(obj, usage, &count, &fences);
- if (r)
+ if (r)
return r;
if (count == 0) {
diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index 26e7a48b55f3..2f2aaadafe35 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -3,11 +3,14 @@
* Copyright © 2021 Intel Corporation
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bug.h>
#include <linux/export.h>
#include <linux/kmemleak.h>
#include <linux/module.h>
#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/gpu_buddy.h>
@@ -34,6 +37,430 @@
#endif
static struct kmem_cache *slab_blocks;
+static struct kmem_cache *slab_extents;
+
+/*
+ * Dirty tracker
+ * -------------
+ *
+ * The dirty tracker maintains an augmented interval rbtree of contiguous
+ * dirty address ranges, decoupled from the buddy free trees.
+ * Each node covers a maximal coalesced run; adjacent extents are merged
+ * on insertion so the tree always holds the smallest possible number of
+ * extents. The augmentation field @subtree_max_size lets the allocator
+ * locate the largest dirty extent in O(log E).
+ *
+ * Free trees (mm->free_tree[])
+ * ----------------------------
+ *
+ * Per-order augmented rbtrees of FREE buddy blocks, keyed by offset.
+ * Every node carries:
+ * - subtree_max_alignment: largest natural alignment in the subtree,
+ * used by aligned/range allocations to skip unsuitable subtrees in
+ * O(log N).
+ * - subtree_block_state: the highest clear class (DIRTY < MIXED < CLEAR)
+ * of any block in the subtree, maintained as a max augment. A value of
+ * >= MIXED means a clear-or-mixed block exists; == CLEAR means a
+ * fully-clear block exists.
+ *
+ * Block classes
+ * -------------
+ *
+ * Each FREE block falls into one of three classes, determined in
+ * mark_free() by querying the dirty tracker for the block's range:
+ *
+ * clear -- HEADER_CLEAR set; no dirty extent overlaps the range.
+ * mixed -- HEADER_CLEAR unset; range has both dirty and clear bytes.
+ * dirty -- HEADER_CLEAR unset; range is fully dirty.
+ *
+ * Clear allocation
+ * ----------------
+ *
+ * A clear (CLEAR_ALLOCATION) request prefers clear -> mixed -> dirty.
+ * Climbing from the requested order up to max_order, rbtree_last_clear_free_block()
+ * returns, in one O(log N) descent per order, the right-most clear-or-mixed block
+ * (fully-clear preferred over mixed) at the lowest order whose free tree contains
+ * such a block. Only if no clear-or-mixed block exists at any order >= the
+ * requested one does it fall back to a dirty block.
+ *
+ * Clear state is reported to the driver per whole block via HEADER_CLEAR, so a
+ * fully-clear block of the requested order lets the driver skip the clear pass.
+ *
+ * The effective allocation preference depends on how the driver handles
+ * freed blocks:
+ *
+ * 1) Never clear on free:
+ * No free block contains clear bytes, so clear allocations always
+ * fall back to dirty blocks.
+ *
+ * 2) Always clear on free:
+ * Freed blocks become clear while untouched blocks remain dirty.
+ * Merging clear and dirty buddies produces mixed blocks, which are
+ * reclassified when split. Over time, clear blocks become dominant,
+ * so clear allocations are typically satisfied from clear blocks,
+ * following a clear -> mixed -> dirty preference.
+ *
+ * 3) Selective clear on free:
+ * For each order examined, fully-clear blocks are preferred over
+ * mixed blocks, and mixed blocks are preferred over dirty blocks.
+ * If a clear or mixed block is found at an order, it is selected
+ * without searching higher orders. Dirty blocks are used only when
+ * no clear or mixed block exists at any eligible order.
+ */
+
+static u64 extent_size(struct gpu_dirty_extent *dirty_extent)
+{
+ return dirty_extent->end - dirty_extent->start;
+}
+
+RB_DECLARE_CALLBACKS_MAX(static, gpu_dirty_augment_cb,
+ struct gpu_dirty_extent, rb,
+ u64, subtree_max_size,
+ extent_size)
+
+static struct gpu_dirty_extent *extent_alloc(struct gpu_dirty_tracker *dirty_tracker)
+{
+ return kmem_cache_alloc(slab_extents, GFP_KERNEL);
+}
+
+static void extent_free(struct gpu_dirty_tracker *dirty_tracker,
+ struct gpu_dirty_extent *dirty_extent)
+{
+ kmem_cache_free(slab_extents, dirty_extent);
+}
+
+/* Return the rightmost extent whose start is strictly below @offset. */
+static struct gpu_dirty_extent *
+prev_extent(struct gpu_dirty_tracker *dirty_tracker, u64 offset)
+{
+ struct rb_node *rb = dirty_tracker->root.rb_node;
+ struct gpu_dirty_extent *dirty_extent = NULL;
+
+ while (rb) {
+ struct gpu_dirty_extent *tmp_extent =
+ rb_entry(rb, struct gpu_dirty_extent, rb);
+
+ if (tmp_extent->start < offset) {
+ dirty_extent = tmp_extent;
+ rb = rb->rb_right;
+ } else {
+ rb = rb->rb_left;
+ }
+ }
+
+ return dirty_extent;
+}
+
+/* Return the leftmost extent whose start is at or above @offset. */
+static struct gpu_dirty_extent *
+next_extent(struct gpu_dirty_tracker *dirty_tracker, u64 offset)
+{
+ struct rb_node *rb = dirty_tracker->root.rb_node;
+ struct gpu_dirty_extent *dirty_extent = NULL;
+
+ while (rb) {
+ struct gpu_dirty_extent *tmp_extent =
+ rb_entry(rb, struct gpu_dirty_extent, rb);
+
+ if (tmp_extent->start >= offset) {
+ dirty_extent = tmp_extent;
+ rb = rb->rb_left;
+ } else {
+ rb = rb->rb_right;
+ }
+ }
+
+ return dirty_extent;
+}
+
+static void insert_extent(struct gpu_dirty_tracker *dirty_tracker,
+ struct gpu_dirty_extent *dirty_extent)
+{
+ struct rb_node **link = &dirty_tracker->root.rb_node;
+ struct rb_node *parent = NULL;
+ u64 size = extent_size(dirty_extent);
+
+ while (*link) {
+ struct gpu_dirty_extent *tmp_extent;
+
+ parent = *link;
+ tmp_extent = rb_entry(parent, struct gpu_dirty_extent, rb);
+
+ if (tmp_extent->subtree_max_size < size)
+ tmp_extent->subtree_max_size = size;
+
+ if (dirty_extent->start < tmp_extent->start)
+ link = &parent->rb_left;
+ else
+ link = &parent->rb_right;
+ }
+
+ dirty_extent->subtree_max_size = size;
+ rb_link_node(&dirty_extent->rb, parent, link);
+ rb_insert_augmented(&dirty_extent->rb, &dirty_tracker->root, &gpu_dirty_augment_cb);
+}
+
+static void remove_extent(struct gpu_dirty_tracker *dirty_tracker,
+ struct gpu_dirty_extent *dirty_extent)
+{
+ rb_erase_augmented(&dirty_extent->rb, &dirty_tracker->root, &gpu_dirty_augment_cb);
+ RB_CLEAR_NODE(&dirty_extent->rb);
+}
+
+static void gpu_dirty_tracker_init(struct gpu_dirty_tracker *dirty_tracker)
+{
+ dirty_tracker->root = RB_ROOT;
+ dirty_tracker->total_dirty = 0;
+}
+
+static void gpu_dirty_tracker_empty(struct gpu_dirty_tracker *dirty_tracker)
+{
+ struct rb_node *rb;
+
+ while ((rb = rb_first(&dirty_tracker->root))) {
+ struct gpu_dirty_extent *dirty_extent =
+ rb_entry(rb, struct gpu_dirty_extent, rb);
+
+ remove_extent(dirty_tracker, dirty_extent);
+ extent_free(dirty_tracker, dirty_extent);
+ }
+
+ dirty_tracker->total_dirty = 0;
+}
+
+static void gpu_dirty_tracker_fini(struct gpu_dirty_tracker *dirty_tracker)
+{
+ gpu_dirty_tracker_empty(dirty_tracker);
+}
+
+/*
+ * Mark the range [start, start + size] as dirty. Merge with the neighbour on
+ * each side if they are contiguous, so the tree never holds two adjacent ranges.
+ */
+static void gpu_dirty_tracker_mark_dirty(struct gpu_dirty_tracker *dirty_tracker,
+ u64 start, u64 size)
+{
+ struct gpu_dirty_extent *left, *right, *dirty_extent;
+ u64 end = start + size;
+
+ gpu_buddy_assert(size);
+
+ /* Find contiguous neighbours, if any. */
+ left = prev_extent(dirty_tracker, start);
+ if (left && left->end != start)
+ left = NULL;
+
+ right = next_extent(dirty_tracker, end);
+ if (right && right->start != end)
+ right = NULL;
+
+ if (left && right) {
+ /* Merge left + new + right into a single extent. */
+ remove_extent(dirty_tracker, left);
+ remove_extent(dirty_tracker, right);
+ left->end = right->end;
+ extent_free(dirty_tracker, right);
+ insert_extent(dirty_tracker, left);
+ } else if (left) {
+ /* Extend left neighbour rightwards. */
+ remove_extent(dirty_tracker, left);
+ left->end = end;
+ insert_extent(dirty_tracker, left);
+ } else if (right) {
+ /* Extend right neighbour leftwards. */
+ remove_extent(dirty_tracker, right);
+ right->start = start;
+ insert_extent(dirty_tracker, right);
+ } else {
+ /* Standalone extent. */
+ dirty_extent = extent_alloc(dirty_tracker);
+ if (!dirty_extent) {
+ pr_warn_once("dirty extent allocation failed, skipping tracker update\n");
+ return;
+ }
+ dirty_extent->start = start;
+ dirty_extent->end = end;
+ insert_extent(dirty_tracker, dirty_extent);
+ }
+
+ dirty_tracker->total_dirty += size;
+}
+
+/*
+ * Remove the range [start, start + size] from the dirty tracker. Punch the
+ * range out of every overlapping dirty extent, splitting one extent in two if
+ * the removed range falls strictly inside it.
+ */
+static void gpu_dirty_tracker_remove_range(struct gpu_dirty_tracker *dirty_tracker,
+ u64 start, u64 size)
+{
+ struct gpu_dirty_extent *dirty_extent, *next;
+ u64 end = start + size;
+
+ gpu_buddy_assert(size);
+
+ dirty_extent = prev_extent(dirty_tracker, start + 1);
+ if (!dirty_extent)
+ dirty_extent = next_extent(dirty_tracker, start);
+
+ while (dirty_extent && dirty_extent->start < end) {
+ struct rb_node *next_node = rb_next(&dirty_extent->rb);
+ u64 extent_start = dirty_extent->start;
+ u64 extent_end = dirty_extent->end;
+
+ if (next_node)
+ next = rb_entry(next_node, struct gpu_dirty_extent, rb);
+ else
+ next = NULL;
+
+ /* Skip a non-overlapping neighbour returned by prev_extent(). */
+ if (extent_end <= start) {
+ dirty_extent = next;
+ continue;
+ }
+
+ if (extent_start < start && extent_end > end) {
+ /*
+ * Removed range lies strictly inside this dirty extent:
+ * split it into the dirty left and right halves.
+ */
+ struct gpu_dirty_extent *right = extent_alloc(dirty_tracker);
+
+ if (!right) {
+ pr_warn_once("dirty extent allocation failed, skipping tracker update\n");
+ dirty_extent = next;
+ continue;
+ }
+
+ remove_extent(dirty_tracker, dirty_extent);
+
+ dirty_extent->end = start;
+ right->start = end;
+ right->end = extent_end;
+
+ insert_extent(dirty_tracker, dirty_extent);
+ insert_extent(dirty_tracker, right);
+
+ dirty_tracker->total_dirty -= size;
+ } else if (extent_start >= start && extent_end <= end) {
+ /* Extent fully covered: drop it. */
+ remove_extent(dirty_tracker, dirty_extent);
+ extent_free(dirty_tracker, dirty_extent);
+
+ dirty_tracker->total_dirty -= (extent_end - extent_start);
+ } else if (extent_start < start) {
+ /* Extent overlaps from the left: trim its right end. */
+ remove_extent(dirty_tracker, dirty_extent);
+ dirty_extent->end = start;
+ insert_extent(dirty_tracker, dirty_extent);
+
+ dirty_tracker->total_dirty -= (extent_end - start);
+ } else {
+ /* Extent overlaps from the right: trim its left end. */
+ remove_extent(dirty_tracker, dirty_extent);
+ dirty_extent->start = end;
+ insert_extent(dirty_tracker, dirty_extent);
+
+ dirty_tracker->total_dirty -= (end - extent_start);
+ }
+
+ dirty_extent = next;
+ }
+}
+
+static enum gpu_block_state
+gpu_dirty_range_state(struct gpu_dirty_tracker *dirty_tracker,
+ u64 start, u64 size)
+{
+ struct gpu_dirty_extent *dirty_extent;
+ u64 end = start + size;
+
+ dirty_extent = prev_extent(dirty_tracker, start + 1);
+ if (dirty_extent) {
+ if (dirty_extent->start <= start && dirty_extent->end >= end)
+ return GPU_BLOCK_DIRTY;
+ if (dirty_extent->start < end && dirty_extent->end > start)
+ return GPU_BLOCK_MIXED;
+ }
+
+ dirty_extent = next_extent(dirty_tracker, start);
+ if (dirty_extent && dirty_extent->start < end)
+ return GPU_BLOCK_MIXED;
+
+ return GPU_BLOCK_CLEAR;
+}
+
+static struct rb_node *
+dirty_tracker_descend_right(struct rb_node *node, u64 min_size)
+{
+ while (node->rb_right) {
+ struct gpu_dirty_extent *tmp_extent;
+
+ tmp_extent = rb_entry(node->rb_right, struct gpu_dirty_extent, rb);
+
+ if (tmp_extent->subtree_max_size < min_size)
+ break;
+ node = node->rb_right;
+ }
+
+ return node;
+}
+
+static struct gpu_dirty_extent *
+gpu_dirty_tracker_find(struct gpu_dirty_tracker *dirty_tracker,
+ u64 min_size, u64 *aligned_start_out)
+{
+ struct rb_node *rb = dirty_tracker->root.rb_node;
+ struct gpu_dirty_extent *root_extent;
+ struct rb_node *parent;
+
+ if (!min_size || !is_power_of_2(min_size))
+ return NULL;
+
+ if (!rb)
+ return NULL;
+
+ root_extent = rb_entry(rb, struct gpu_dirty_extent, rb);
+ if (root_extent->subtree_max_size < min_size)
+ return NULL;
+
+ rb = dirty_tracker_descend_right(rb, min_size);
+
+ while (rb) {
+ struct gpu_dirty_extent *dirty_extent;
+ u64 aligned_start;
+
+ dirty_extent = rb_entry(rb, struct gpu_dirty_extent, rb);
+ aligned_start = ALIGN(dirty_extent->start, min_size);
+
+ /* Check if a min_size block fits after the alignment skip. */
+ if (aligned_start <= dirty_extent->end &&
+ dirty_extent->end - aligned_start >= min_size) {
+ *aligned_start_out = aligned_start;
+ return dirty_extent;
+ }
+
+ if (rb->rb_left) {
+ struct gpu_dirty_extent *tmp_extent;
+
+ tmp_extent = rb_entry(rb->rb_left, struct gpu_dirty_extent, rb);
+ if (tmp_extent->subtree_max_size >= min_size) {
+ rb = dirty_tracker_descend_right(rb->rb_left, min_size);
+ continue;
+ }
+ }
+
+ /* Walk up until we exit a node via its right child. */
+ parent = rb_parent(rb);
+ while (parent && parent->rb_right != rb) {
+ rb = parent;
+ parent = rb_parent(rb);
+ }
+ rb = parent;
+ }
+
+ return NULL;
+}
static unsigned int
gpu_buddy_block_state(struct gpu_buddy_block *block)
@@ -67,10 +494,97 @@ static unsigned int gpu_buddy_block_offset_alignment(struct gpu_buddy_block *blo
return __ffs64(offset);
}
-RB_DECLARE_CALLBACKS_MAX(static, gpu_buddy_augment_cb,
- struct gpu_buddy_block, rb,
- unsigned int, subtree_max_alignment,
- gpu_buddy_block_offset_alignment);
+static inline enum gpu_block_state
+gpu_block_cached_state(struct gpu_buddy_block *block)
+{
+ if (gpu_buddy_block_is_clear(block))
+ return GPU_BLOCK_CLEAR;
+ if (block->has_clear)
+ return GPU_BLOCK_MIXED;
+ return GPU_BLOCK_DIRTY;
+}
+
+static inline void gpu_buddy_augment_compute(struct gpu_buddy_block *block)
+{
+ enum gpu_block_state block_state;
+ struct gpu_buddy_block *right;
+ struct gpu_buddy_block *left;
+ unsigned int max_align;
+
+ max_align = gpu_buddy_block_offset_alignment(block);
+ block_state = gpu_block_cached_state(block);
+
+ left = rb_entry_safe(block->rb.rb_left, struct gpu_buddy_block, rb);
+ if (left) {
+ if (left->subtree_max_alignment > max_align)
+ max_align = left->subtree_max_alignment;
+
+ block_state = max(block_state, left->subtree_block_state);
+ }
+
+ right = rb_entry_safe(block->rb.rb_right, struct gpu_buddy_block, rb);
+ if (right) {
+ if (right->subtree_max_alignment > max_align)
+ max_align = right->subtree_max_alignment;
+
+ block_state = max(block_state, right->subtree_block_state);
+ }
+
+ block->subtree_max_alignment = max_align;
+ block->subtree_block_state = block_state;
+}
+
+static void gpu_buddy_augment_propagate(struct rb_node *rb, struct rb_node *stop)
+{
+ while (rb != stop) {
+ struct gpu_buddy_block *block;
+ unsigned int old_align;
+ enum gpu_block_state old_block_state;
+
+ block = rb_entry(rb, struct gpu_buddy_block, rb);
+ old_align = block->subtree_max_alignment;
+ old_block_state = block->subtree_block_state;
+
+ gpu_buddy_augment_compute(block);
+ if (block->subtree_max_alignment == old_align &&
+ block->subtree_block_state == old_block_state)
+ break;
+
+ rb = rb_parent(&block->rb);
+ }
+}
+
+static void gpu_buddy_augment_copy(struct rb_node *rb_old, struct rb_node *rb_new)
+{
+ struct gpu_buddy_block *old;
+ struct gpu_buddy_block *new;
+
+ old = rb_entry(rb_old, struct gpu_buddy_block, rb);
+ new = rb_entry(rb_new, struct gpu_buddy_block, rb);
+
+ new->subtree_max_alignment = old->subtree_max_alignment;
+ new->subtree_block_state = old->subtree_block_state;
+}
+
+static void gpu_buddy_augment_rotate(struct rb_node *rb_old, struct rb_node *rb_new)
+{
+ struct gpu_buddy_block *old;
+ struct gpu_buddy_block *new;
+
+ old = rb_entry(rb_old, struct gpu_buddy_block, rb);
+ new = rb_entry(rb_new, struct gpu_buddy_block, rb);
+
+ new->subtree_max_alignment = old->subtree_max_alignment;
+ new->subtree_block_state = old->subtree_block_state;
+
+ gpu_buddy_augment_compute(old);
+}
+
+static const struct rb_augment_callbacks gpu_buddy_augment_cb = {
+ .propagate = gpu_buddy_augment_propagate,
+ .copy = gpu_buddy_augment_copy,
+ .rotate = gpu_buddy_augment_rotate,
+};
static struct gpu_buddy_block *gpu_block_alloc(struct gpu_buddy *mm,
struct gpu_buddy_block *parent,
@@ -101,13 +615,6 @@ static void gpu_block_free(struct gpu_buddy *mm,
kmem_cache_free(slab_blocks, block);
}
-static enum gpu_buddy_free_tree
-get_block_tree(struct gpu_buddy_block *block)
-{
- return gpu_buddy_block_is_clear(block) ?
- GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE;
-}
-
static struct gpu_buddy_block *
rbtree_get_free_block(const struct rb_node *node)
{
@@ -120,24 +627,59 @@ rbtree_last_free_block(struct rb_root *root)
return rbtree_get_free_block(rb_last(root));
}
-static bool rbtree_is_empty(struct rb_root *root)
+static struct gpu_buddy_block *
+rbtree_last_clear_free_block(struct rb_root *root,
+ enum gpu_block_state min_block_state)
{
- return RB_EMPTY_ROOT(root);
+ struct rb_node *node = root->rb_node;
+ struct gpu_buddy_block *block = NULL;
+ struct gpu_buddy_block *root_block;
+ enum gpu_block_state target_state;
+
+ root_block = rbtree_get_free_block(node);
+ if (!root_block || root_block->subtree_block_state < min_block_state)
+ return NULL;
+
+ target_state = root_block->subtree_block_state;
+
+ while (node) {
+ struct gpu_buddy_block *right_block;
+ struct gpu_buddy_block *node_block;
+
+ node_block = rbtree_get_free_block(node);
+ right_block = rbtree_get_free_block(node->rb_right);
+
+ if (right_block && right_block->subtree_block_state >= target_state) {
+ node = node->rb_right;
+ continue;
+ }
+
+ if (gpu_block_cached_state(node_block) == target_state) {
+ block = node_block;
+ break;
+ }
+
+ node = node->rb_left;
+ }
+
+ return block;
}
static void rbtree_insert(struct gpu_buddy *mm,
- struct gpu_buddy_block *block,
- enum gpu_buddy_free_tree tree)
+ struct gpu_buddy_block *block)
{
struct rb_node **link, *parent = NULL;
- unsigned int block_alignment, order;
+ enum gpu_block_state block_state;
struct gpu_buddy_block *node;
+ unsigned int block_alignment;
struct rb_root *root;
+ unsigned int order;
order = gpu_buddy_block_order(block);
block_alignment = gpu_buddy_block_offset_alignment(block);
+ block_state = gpu_block_cached_state(block);
- root = &mm->free_trees[tree][order];
+ root = &mm->free_tree[order];
link = &root->rb_node;
while (*link) {
@@ -147,10 +689,12 @@ static void rbtree_insert(struct gpu_buddy *mm,
* Manual augmentation update during insertion traversal. Required
* because rb_insert_augmented() only calls rotate callback during
* rotations. This ensures all ancestors on the insertion path have
- * correct subtree_max_alignment values.
+ * correct subtree_max_alignment / subtree_block_state values.
*/
if (node->subtree_max_alignment < block_alignment)
node->subtree_max_alignment = block_alignment;
+ if (node->subtree_block_state < block_state)
+ node->subtree_block_state = block_state;
if (gpu_buddy_block_offset(block) < gpu_buddy_block_offset(node))
link = &parent->rb_left;
@@ -159,6 +703,7 @@ static void rbtree_insert(struct gpu_buddy *mm,
}
block->subtree_max_alignment = block_alignment;
+ block->subtree_block_state = block_state;
rb_link_node(&block->rb, parent, link);
rb_insert_augmented(&block->rb, root, &gpu_buddy_augment_cb);
}
@@ -167,53 +712,55 @@ static void rbtree_remove(struct gpu_buddy *mm,
struct gpu_buddy_block *block)
{
unsigned int order = gpu_buddy_block_order(block);
- enum gpu_buddy_free_tree tree;
- struct rb_root *root;
-
- tree = get_block_tree(block);
- root = &mm->free_trees[tree][order];
- rb_erase_augmented(&block->rb, root, &gpu_buddy_augment_cb);
+ rb_erase_augmented(&block->rb, &mm->free_tree[order], &gpu_buddy_augment_cb);
RB_CLEAR_NODE(&block->rb);
}
-static void clear_reset(struct gpu_buddy_block *block)
-{
- block->header &= ~GPU_BUDDY_HEADER_CLEAR;
-}
-
-static void mark_cleared(struct gpu_buddy_block *block)
-{
- block->header |= GPU_BUDDY_HEADER_CLEAR;
-}
-
static void mark_allocated(struct gpu_buddy *mm,
struct gpu_buddy_block *block)
{
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_ALLOCATED;
+ block->has_clear = false;
+
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
mm->used_scoreboard[gpu_buddy_block_order(block)]++;
rbtree_remove(mm, block);
}
-static void mark_free(struct gpu_buddy *mm,
- struct gpu_buddy_block *block)
+static void __mark_free(struct gpu_buddy *mm,
+ struct gpu_buddy_block *block,
+ enum gpu_block_state block_state)
{
- enum gpu_buddy_free_tree tree;
-
if (gpu_buddy_block_is_allocated(block))
mm->used_scoreboard[gpu_buddy_block_order(block)]--;
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_FREE;
+ block->header &= ~GPU_BUDDY_HEADER_CLEAR;
+
+ block->has_clear = (block_state != GPU_BLOCK_DIRTY);
+ if (block_state == GPU_BLOCK_CLEAR)
+ block->header |= GPU_BUDDY_HEADER_CLEAR;
+
mm->free_scoreboard[gpu_buddy_block_order(block)]++;
- tree = get_block_tree(block);
- rbtree_insert(mm, block, tree);
+ rbtree_insert(mm, block);
+}
+
+static void mark_free(struct gpu_buddy *mm,
+ struct gpu_buddy_block *block)
+{
+ enum gpu_block_state block_state;
+
+ block_state = gpu_dirty_range_state(&mm->dirty,
+ gpu_buddy_block_offset(block),
+ gpu_buddy_block_size(mm, block));
+ __mark_free(mm, block, block_state);
}
static void mark_split(struct gpu_buddy *mm,
@@ -253,37 +800,31 @@ __get_buddy(struct gpu_buddy_block *block)
}
static unsigned int __gpu_buddy_free(struct gpu_buddy *mm,
- struct gpu_buddy_block *block,
- bool force_merge)
+ struct gpu_buddy_block *block)
{
+ enum gpu_block_state block_state;
struct gpu_buddy_block *parent;
unsigned int order;
- while ((parent = block->parent)) {
- struct gpu_buddy_block *buddy;
+ block_state = gpu_block_cached_state(block);
- buddy = __get_buddy(block);
+ while ((parent = block->parent)) {
+ struct gpu_buddy_block *buddy = __get_buddy(block);
if (!gpu_buddy_block_is_free(buddy))
break;
- if (!force_merge) {
- /*
- * Check the block and its buddy clear state and exit
- * the loop if they both have the dissimilar state.
- */
- if (gpu_buddy_block_is_clear(block) !=
- gpu_buddy_block_is_clear(buddy))
- break;
+ if (block_state != GPU_BLOCK_MIXED) {
+ enum gpu_block_state buddy_state;
- if (gpu_buddy_block_is_clear(block))
- mark_cleared(parent);
+ buddy_state = gpu_block_cached_state(buddy);
+
+ if (buddy_state != block_state)
+ block_state = GPU_BLOCK_MIXED;
}
rbtree_remove(mm, buddy);
mm->free_scoreboard[gpu_buddy_block_order(buddy)]--;
- if (force_merge && gpu_buddy_block_is_clear(buddy))
- mm->clear_avail -= gpu_buddy_block_size(mm, buddy);
if (gpu_buddy_block_is_allocated(block))
mm->used_scoreboard[gpu_buddy_block_order(block)]--;
@@ -295,74 +836,11 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm,
}
order = gpu_buddy_block_order(block);
- mark_free(mm, block);
+ __mark_free(mm, block, block_state);
return order;
}
-static int __force_merge(struct gpu_buddy *mm,
- u64 start,
- u64 end,
- unsigned int min_order)
-{
- unsigned int tree, order;
- int i;
-
- if (!min_order)
- return -ENOMEM;
-
- if (min_order > mm->max_order)
- return -EINVAL;
-
- for_each_free_tree(tree) {
- for (i = min_order - 1; i >= 0; i--) {
- struct rb_node *iter = rb_last(&mm->free_trees[tree][i]);
-
- while (iter) {
- struct gpu_buddy_block *block, *buddy;
- u64 block_start, block_end;
-
- block = rbtree_get_free_block(iter);
- iter = rb_prev(iter);
-
- if (!block || !block->parent)
- continue;
-
- block_start = gpu_buddy_block_offset(block);
- block_end = block_start + gpu_buddy_block_size(mm, block) - 1;
-
- if (!contains(start, end, block_start, block_end))
- continue;
-
- buddy = __get_buddy(block);
- if (!gpu_buddy_block_is_free(buddy))
- continue;
-
- gpu_buddy_assert(gpu_buddy_block_is_clear(block) !=
- gpu_buddy_block_is_clear(buddy));
-
- /*
- * Advance to the next node when the current node is the buddy,
- * as freeing the block will also remove its buddy from the tree.
- */
- if (iter == &buddy->rb)
- iter = rb_prev(iter);
-
- rbtree_remove(mm, block);
- mm->free_scoreboard[gpu_buddy_block_order(block)]--;
- if (gpu_buddy_block_is_clear(block))
- mm->clear_avail -= gpu_buddy_block_size(mm, block);
-
- order = __gpu_buddy_free(mm, block, true);
- if (order >= min_order)
- return 0;
- }
- }
- }
-
- return -ENOMEM;
-}
-
/**
* gpu_buddy_init - init memory manager
*
@@ -377,7 +855,7 @@ static int __force_merge(struct gpu_buddy *mm,
*/
int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
{
- unsigned int i, j, root_count = 0;
+ unsigned int root_count = 0;
u64 offset = 0;
if (size < chunk_size)
@@ -393,7 +871,6 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
mm->size = size;
mm->avail = size;
- mm->clear_avail = 0;
mm->chunk_size = chunk_size;
mm->max_order = ilog2(size) - ilog2(chunk_size);
@@ -411,19 +888,13 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
if (!mm->used_scoreboard)
goto out_free_free_scoreboard;
- mm->free_trees = kmalloc_objs(*mm->free_trees, GPU_BUDDY_MAX_FREE_TREES);
- if (!mm->free_trees)
+ mm->free_tree = kcalloc(mm->max_order + 1,
+ sizeof(struct rb_root),
+ GFP_KERNEL);
+ if (!mm->free_tree)
goto out_free_used_scoreboard;
- for_each_free_tree(i) {
- mm->free_trees[i] = kmalloc_objs(struct rb_root,
- mm->max_order + 1);
- if (!mm->free_trees[i])
- goto out_free_tree;
-
- for (j = 0; j <= mm->max_order; ++j)
- mm->free_trees[i][j] = RB_ROOT;
- }
+ gpu_dirty_tracker_init(&mm->dirty);
mm->n_roots = hweight64(size);
@@ -447,7 +918,8 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
if (!root)
goto out_free_roots;
- mark_free(mm, root);
+ gpu_dirty_tracker_mark_dirty(&mm->dirty, offset, root_size);
+ __mark_free(mm, root, GPU_BLOCK_DIRTY);
BUG_ON(root_count > mm->max_order);
BUG_ON(gpu_buddy_block_size(mm, root) < chunk_size);
@@ -469,9 +941,8 @@ out_free_roots:
gpu_block_free(mm, mm->roots[root_count]);
kfree(mm->roots);
out_free_tree:
- while (i--)
- kfree(mm->free_trees[i]);
- kfree(mm->free_trees);
+ gpu_dirty_tracker_fini(&mm->dirty);
+ kfree(mm->free_tree);
out_free_used_scoreboard:
kfree(mm->used_scoreboard);
out_free_free_scoreboard:
@@ -489,7 +960,7 @@ EXPORT_SYMBOL(gpu_buddy_init);
*/
void gpu_buddy_fini(struct gpu_buddy *mm)
{
- u64 root_size, size, start;
+ u64 root_size, size;
unsigned int order;
int i;
@@ -497,14 +968,10 @@ void gpu_buddy_fini(struct gpu_buddy *mm)
for (i = 0; i < mm->n_roots; ++i) {
order = ilog2(size) - ilog2(mm->chunk_size);
- start = gpu_buddy_block_offset(mm->roots[i]);
- __force_merge(mm, start, start + size, order);
+ root_size = mm->chunk_size << order;
gpu_buddy_assert(gpu_buddy_block_is_free(mm->roots[i]));
-
gpu_block_free(mm, mm->roots[i]);
-
- root_size = mm->chunk_size << order;
size -= root_size;
}
@@ -513,9 +980,8 @@ void gpu_buddy_fini(struct gpu_buddy *mm)
for (i = 0; i <= mm->max_order; ++i)
gpu_buddy_assert(!mm->used_scoreboard[i]);
- for_each_free_tree(i)
- kfree(mm->free_trees[i]);
- kfree(mm->free_trees);
+ gpu_dirty_tracker_fini(&mm->dirty);
+ kfree(mm->free_tree);
kfree(mm->roots);
kfree(mm->free_scoreboard);
kfree(mm->used_scoreboard);
@@ -527,6 +993,7 @@ static int split_block(struct gpu_buddy *mm,
{
unsigned int block_order = gpu_buddy_block_order(block) - 1;
u64 offset = gpu_buddy_block_offset(block);
+ enum gpu_block_state parent_state;
BUG_ON(!gpu_buddy_block_is_free(block));
BUG_ON(!gpu_buddy_block_order(block));
@@ -542,17 +1009,18 @@ static int split_block(struct gpu_buddy *mm,
return -ENOMEM;
}
+ parent_state = gpu_block_cached_state(block);
+
mark_split(mm, block);
- if (gpu_buddy_block_is_clear(block)) {
- mark_cleared(block->left);
- mark_cleared(block->right);
- clear_reset(block);
+ if (parent_state == GPU_BLOCK_MIXED) {
+ mark_free(mm, block->left);
+ mark_free(mm, block->right);
+ } else {
+ __mark_free(mm, block->left, parent_state);
+ __mark_free(mm, block->right, parent_state);
}
- mark_free(mm, block->left);
- mark_free(mm, block->right);
-
return 0;
}
@@ -567,45 +1035,47 @@ static int split_block(struct gpu_buddy *mm,
*/
void gpu_buddy_reset_clear(struct gpu_buddy *mm, bool is_clear)
{
- enum gpu_buddy_free_tree src_tree, dst_tree;
- u64 root_size, size, start;
- unsigned int order;
- int i;
+ unsigned int i;
gpu_buddy_driver_lock_held(mm);
- size = mm->size;
- for (i = 0; i < mm->n_roots; ++i) {
- order = ilog2(size) - ilog2(mm->chunk_size);
- start = gpu_buddy_block_offset(mm->roots[i]);
- __force_merge(mm, start, start + size, order);
-
- root_size = mm->chunk_size << order;
- size -= root_size;
- }
- src_tree = is_clear ? GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE;
- dst_tree = is_clear ? GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE;
+ gpu_dirty_tracker_empty(&mm->dirty);
for (i = 0; i <= mm->max_order; ++i) {
- struct rb_root *root = &mm->free_trees[src_tree][i];
struct gpu_buddy_block *block, *tmp;
- rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
- rbtree_remove(mm, block);
+ rbtree_postorder_for_each_entry_safe(block, tmp,
+ &mm->free_tree[i], rb) {
if (is_clear) {
- mark_cleared(block);
- mm->clear_avail += gpu_buddy_block_size(mm, block);
+ if (!gpu_buddy_block_is_clear(block))
+ block->header |= GPU_BUDDY_HEADER_CLEAR;
+ block->has_clear = true;
} else {
- clear_reset(block);
- mm->clear_avail -= gpu_buddy_block_size(mm, block);
+ block->header &= ~GPU_BUDDY_HEADER_CLEAR;
+ block->has_clear = false;
+ gpu_dirty_tracker_mark_dirty(&mm->dirty,
+ gpu_buddy_block_offset(block),
+ gpu_buddy_block_size(mm, block));
}
- rbtree_insert(mm, block, dst_tree);
+ gpu_buddy_augment_compute(block);
}
}
}
EXPORT_SYMBOL(gpu_buddy_reset_clear);
+static void __gpu_buddy_free_block_internal(struct gpu_buddy *mm,
+ struct gpu_buddy_block *block)
+{
+ u64 size = gpu_buddy_block_size(mm, block);
+
+ gpu_buddy_driver_lock_held(mm);
+ BUG_ON(!gpu_buddy_block_is_allocated(block));
+
+ mm->avail += size;
+ __gpu_buddy_free(mm, block);
+}
+
/**
* gpu_buddy_free_block - free a block
*
@@ -615,13 +1085,12 @@ EXPORT_SYMBOL(gpu_buddy_reset_clear);
void gpu_buddy_free_block(struct gpu_buddy *mm,
struct gpu_buddy_block *block)
{
- gpu_buddy_driver_lock_held(mm);
- BUG_ON(!gpu_buddy_block_is_allocated(block));
- mm->avail += gpu_buddy_block_size(mm, block);
- if (gpu_buddy_block_is_clear(block))
- mm->clear_avail += gpu_buddy_block_size(mm, block);
+ if (!gpu_buddy_block_is_clear(block))
+ gpu_dirty_tracker_mark_dirty(&mm->dirty,
+ gpu_buddy_block_offset(block),
+ gpu_buddy_block_size(mm, block));
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_free_block_internal(mm, block);
}
EXPORT_SYMBOL(gpu_buddy_free_block);
@@ -684,17 +1153,47 @@ static void __gpu_buddy_free_list(struct gpu_buddy *mm,
bool mark_dirty)
{
struct gpu_buddy_block *block, *on;
+ u64 dirty_start = 0, dirty_size = 0;
gpu_buddy_assert(!(mark_dirty && mark_clear));
list_for_each_entry_safe(block, on, objects, link) {
+ u64 offset = gpu_buddy_block_offset(block);
+ u64 size = gpu_buddy_block_size(mm, block);
+
if (mark_clear)
- mark_cleared(block);
+ block->header |= GPU_BUDDY_HEADER_CLEAR;
else if (mark_dirty)
- clear_reset(block);
- gpu_buddy_free_block(mm, block);
+ block->header &= ~GPU_BUDDY_HEADER_CLEAR;
+
+ /*
+ * Coalesce contiguous dirty blocks into one extent update so
+ * a multi-block contiguous free costs a single mark_dirty().
+ * Flush the pending extent and start over on a gap.
+ */
+ if (!gpu_buddy_block_is_clear(block)) {
+ if (dirty_size &&
+ (dirty_start + dirty_size == offset ||
+ offset + size == dirty_start)) {
+ dirty_start = min(dirty_start, offset);
+ dirty_size += size;
+ } else {
+ if (dirty_size)
+ gpu_dirty_tracker_mark_dirty(&mm->dirty,
+ dirty_start,
+ dirty_size);
+ dirty_start = offset;
+ dirty_size = size;
+ }
+ }
+
+ __gpu_buddy_free_block_internal(mm, block);
cond_resched();
}
+
+ if (dirty_size)
+ gpu_dirty_tracker_mark_dirty(&mm->dirty, dirty_start, dirty_size);
+
INIT_LIST_HEAD(objects);
}
@@ -727,13 +1226,6 @@ void gpu_buddy_free_list(struct gpu_buddy *mm,
}
EXPORT_SYMBOL(gpu_buddy_free_list);
-static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags)
-{
- bool needs_clear = flags & GPU_BUDDY_CLEAR_ALLOCATION;
-
- return needs_clear != gpu_buddy_block_is_clear(block);
-}
-
static void __gpu_buddy_undo_splits(struct gpu_buddy *mm,
struct gpu_buddy_block *block)
{
@@ -744,7 +1236,7 @@ static void __gpu_buddy_undo_splits(struct gpu_buddy *mm,
gpu_buddy_block_is_free(buddy))) {
rbtree_remove(mm, block);
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_free(mm, block);
}
}
@@ -752,8 +1244,7 @@ static struct gpu_buddy_block *
__alloc_range_bias(struct gpu_buddy *mm,
u64 start, u64 end,
unsigned int order,
- unsigned long flags,
- bool fallback)
+ unsigned long flags)
{
u64 req_size = mm->chunk_size << order;
struct gpu_buddy_block *block;
@@ -763,7 +1254,15 @@ __alloc_range_bias(struct gpu_buddy *mm,
end = end - 1;
- for (i = 0; i < mm->n_roots; ++i)
+ /*
+ * This range-constrained search hands back the highest/right-most
+ * address that satisfies the request: the roots are seeded high-to-low
+ * and the right (higher-address) child is descended first, making
+ * top-down the default placement here. A non-top-down clear request is
+ * the only exception, where the descent is biased towards clear or
+ * clear-containing subtrees to satisfy the clear preference.
+ */
+ for (i = mm->n_roots - 1; i >= 0; --i)
list_add_tail(&mm->roots[i]->tmp_link, &dfs);
do {
@@ -799,9 +1298,6 @@ __alloc_range_bias(struct gpu_buddy *mm,
continue;
}
- if (!fallback && block_incompatible(block, flags))
- continue;
-
if (contains(start, end, block_start, block_end) &&
order == gpu_buddy_block_order(block)) {
/*
@@ -819,8 +1315,38 @@ __alloc_range_bias(struct gpu_buddy *mm,
goto err_undo;
}
- list_add(&block->right->tmp_link, &dfs);
- list_add(&block->left->tmp_link, &dfs);
+ /*
+ * Top-down is a strict address-placement policy, so when it is
+ * requested we ignore clear steering and simply descend the
+ * right (higher-address) child first. Only a non-top-down clear
+ * request biases the descent towards clear/has_clear subtrees.
+ */
+ if ((flags & GPU_BUDDY_CLEAR_ALLOCATION) &&
+ !(flags & GPU_BUDDY_TOPDOWN_ALLOCATION)) {
+ struct gpu_buddy_block *prefer;
+
+ if (gpu_buddy_block_is_clear(block->right))
+ prefer = block->right;
+ else if (gpu_buddy_block_is_clear(block->left))
+ prefer = block->left;
+ else if (block->right->has_clear)
+ prefer = block->right;
+ else if (block->left->has_clear)
+ prefer = block->left;
+ else
+ prefer = block->right;
+
+ if (prefer == block->right) {
+ list_add(&block->left->tmp_link, &dfs);
+ list_add(&block->right->tmp_link, &dfs);
+ } else {
+ list_add(&block->right->tmp_link, &dfs);
+ list_add(&block->left->tmp_link, &dfs);
+ }
+ } else {
+ list_add(&block->left->tmp_link, &dfs);
+ list_add(&block->right->tmp_link, &dfs);
+ }
} while (1);
return ERR_PTR(-ENOSPC);
@@ -835,48 +1361,32 @@ err_undo:
return ERR_PTR(err);
}
-static struct gpu_buddy_block *
-__gpu_buddy_alloc_range_bias(struct gpu_buddy *mm,
- u64 start, u64 end,
- unsigned int order,
- unsigned long flags)
-{
- struct gpu_buddy_block *block;
- bool fallback = false;
-
- block = __alloc_range_bias(mm, start, end, order,
- flags, fallback);
- if (IS_ERR(block))
- return __alloc_range_bias(mm, start, end, order,
- flags, !fallback);
-
- return block;
-}
-
+/* Return the highest-address free block of at least @order. */
static struct gpu_buddy_block *
get_maxblock(struct gpu_buddy *mm,
- unsigned int order,
- enum gpu_buddy_free_tree tree)
+ unsigned int order)
{
- struct gpu_buddy_block *max_block = NULL, *block = NULL;
- struct rb_root *root;
+ struct gpu_buddy_block *max_block;
+ struct gpu_buddy_block *block;
unsigned int i;
+ /*
+ * Top-down allocation is a strict address-placement policy: the block
+ * is chosen purely by offset, regardless of its clear/dirty state.
+ * Clear state is re-derived from the dirty tracker once the allocation
+ * completes, and the driver is responsible for issuing the clear pass
+ * if a clear region is required.
+ */
+ max_block = NULL;
+
for (i = order; i <= mm->max_order; ++i) {
- root = &mm->free_trees[tree][i];
- block = rbtree_last_free_block(root);
+ block = rbtree_last_free_block(&mm->free_tree[i]);
if (!block)
continue;
- if (!max_block) {
+ if (!max_block ||
+ gpu_buddy_block_offset(block) > gpu_buddy_block_offset(max_block))
max_block = block;
- continue;
- }
-
- if (gpu_buddy_block_offset(block) >
- gpu_buddy_block_offset(max_block)) {
- max_block = block;
- }
}
return max_block;
@@ -888,45 +1398,34 @@ alloc_from_freetree(struct gpu_buddy *mm,
unsigned long flags)
{
struct gpu_buddy_block *block = NULL;
- struct rb_root *root;
- enum gpu_buddy_free_tree tree;
unsigned int tmp;
int err;
- tree = (flags & GPU_BUDDY_CLEAR_ALLOCATION) ?
- GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE;
-
if (flags & GPU_BUDDY_TOPDOWN_ALLOCATION) {
- block = get_maxblock(mm, order, tree);
+ block = get_maxblock(mm, order);
if (block)
- /* Store the obtained block order */
tmp = gpu_buddy_block_order(block);
} else {
- for (tmp = order; tmp <= mm->max_order; ++tmp) {
- /* Get RB tree root for this order and tree */
- root = &mm->free_trees[tree][tmp];
- block = rbtree_last_free_block(root);
- if (block)
- break;
+ if (flags & GPU_BUDDY_CLEAR_ALLOCATION) {
+ for (tmp = order; tmp <= mm->max_order; ++tmp) {
+ block = rbtree_last_clear_free_block(&mm->free_tree[tmp],
+ GPU_BLOCK_MIXED);
+ if (block)
+ break;
+ }
}
- }
-
- if (!block) {
- /* Try allocating from the other tree */
- tree = (tree == GPU_BUDDY_CLEAR_TREE) ?
- GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE;
-
- for (tmp = order; tmp <= mm->max_order; ++tmp) {
- root = &mm->free_trees[tree][tmp];
- block = rbtree_last_free_block(root);
- if (block)
- break;
+ if (!block) {
+ for (tmp = order; tmp <= mm->max_order; ++tmp) {
+ block = rbtree_last_free_block(&mm->free_tree[tmp]);
+ if (block)
+ break;
+ }
}
-
- if (!block)
- return ERR_PTR(-ENOSPC);
}
+ if (!block)
+ return ERR_PTR(-ENOSPC);
+
BUG_ON(!gpu_buddy_block_is_free(block));
while (tmp != order) {
@@ -934,7 +1433,26 @@ alloc_from_freetree(struct gpu_buddy *mm,
if (unlikely(err))
goto err_undo;
- block = block->right;
+ if ((flags & GPU_BUDDY_CLEAR_ALLOCATION) &&
+ !(flags & GPU_BUDDY_TOPDOWN_ALLOCATION)) {
+ bool right_clear, left_clear;
+
+ right_clear = gpu_buddy_block_is_clear(block->right);
+ left_clear = gpu_buddy_block_is_clear(block->left);
+
+ if (right_clear)
+ block = block->right;
+ else if (left_clear)
+ block = block->left;
+ else if (block->right->has_clear)
+ block = block->right;
+ else if (block->left->has_clear)
+ block = block->left;
+ else
+ block = block->right;
+ } else {
+ block = block->right;
+ }
tmp--;
}
return block;
@@ -961,12 +1479,10 @@ static bool gpu_buddy_subtree_can_satisfy(struct rb_node *node,
static struct gpu_buddy_block *
gpu_buddy_find_block_aligned(struct gpu_buddy *mm,
- enum gpu_buddy_free_tree tree,
unsigned int order,
- unsigned int alignment,
- unsigned long flags)
+ unsigned int alignment)
{
- struct rb_root *root = &mm->free_trees[tree][order];
+ struct rb_root *root = &mm->free_tree[order];
struct rb_node *rb = root->rb_node;
while (rb) {
@@ -999,12 +1515,10 @@ gpu_buddy_find_block_aligned(struct gpu_buddy *mm,
static struct gpu_buddy_block *
gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
u64 size,
- u64 min_block_size,
- unsigned long flags)
+ u64 min_block_size)
{
struct gpu_buddy_block *block = NULL;
unsigned int order, tmp, alignment;
- enum gpu_buddy_free_tree tree;
unsigned long pages;
int err;
@@ -1012,19 +1526,15 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
pages = size >> ilog2(mm->chunk_size);
order = fls(pages) - 1;
- tree = (flags & GPU_BUDDY_CLEAR_ALLOCATION) ?
- GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE;
-
+ /*
+ * Offset-aligned allocation is a strict address-placement policy: the
+ * block is chosen purely by its offset alignment, regardless of its
+ * clear/dirty state. Clear state is re-derived from the dirty tracker
+ * once the allocation completes, and the driver is responsible for
+ * issuing the clear pass if a clear region is required.
+ */
for (tmp = order; tmp <= mm->max_order; ++tmp) {
- block = gpu_buddy_find_block_aligned(mm, tree, tmp,
- alignment, flags);
- if (!block) {
- tree = (tree == GPU_BUDDY_CLEAR_TREE) ?
- GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE;
- block = gpu_buddy_find_block_aligned(mm, tree, tmp,
- alignment, flags);
- }
-
+ block = gpu_buddy_find_block_aligned(mm, tmp, alignment);
if (block)
break;
}
@@ -1063,6 +1573,7 @@ err_undo:
static int __alloc_range(struct gpu_buddy *mm,
struct list_head *dfs,
u64 start, u64 size,
+ unsigned long flags,
struct list_head *blocks,
u64 *total_allocated_on_err)
{
@@ -1099,16 +1610,23 @@ static int __alloc_range(struct gpu_buddy *mm,
if (contains(start, end, block_start, block_end)) {
if (gpu_buddy_block_is_free(block)) {
+ u64 block_offset;
+ u64 block_size;
+
+ block_size = gpu_buddy_block_size(mm, block);
+ block_offset = gpu_buddy_block_offset(block);
+
+ if (!gpu_buddy_block_is_clear(block))
+ gpu_dirty_tracker_remove_range(&mm->dirty,
+ block_offset,
+ block_size);
+
mark_allocated(mm, block);
- total_allocated += gpu_buddy_block_size(mm, block);
- mm->avail -= gpu_buddy_block_size(mm, block);
- if (gpu_buddy_block_is_clear(block))
- mm->clear_avail -= gpu_buddy_block_size(mm, block);
+ total_allocated += block_size;
+ mm->avail -= block_size;
+
list_add_tail(&block->link, &allocated);
continue;
- } else if (!mm->clear_avail) {
- err = -ENOSPC;
- goto err_free;
}
}
@@ -1153,6 +1671,7 @@ err_free:
static int __gpu_buddy_alloc_range(struct gpu_buddy *mm,
u64 start,
u64 size,
+ unsigned long flags,
u64 *total_allocated_on_err,
struct list_head *blocks)
{
@@ -1162,7 +1681,7 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm,
for (i = 0; i < mm->n_roots; ++i)
list_add_tail(&mm->roots[i]->tmp_link, &dfs);
- return __alloc_range(mm, &dfs, start, size,
+ return __alloc_range(mm, &dfs, start, size, flags,
blocks, total_allocated_on_err);
}
@@ -1170,85 +1689,89 @@ static int __alloc_contig_aligned_retry(struct gpu_buddy *mm,
u64 unaligned_offset,
u64 size,
u64 min_block_size,
+ unsigned long flags,
struct list_head *blocks)
{
u64 aligned_offset = round_down(unaligned_offset, min_block_size);
- return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks);
+ return __gpu_buddy_alloc_range(mm, aligned_offset, size, flags,
+ NULL, blocks);
}
static int __alloc_contig_try_harder(struct gpu_buddy *mm,
u64 size,
u64 min_block_size,
+ unsigned long flags,
struct list_head *blocks)
{
u64 rhs_offset, lhs_offset, filled;
struct gpu_buddy_block *block;
- unsigned int tree, order;
+ struct rb_root *root;
+ struct rb_node *iter;
+ unsigned long pages;
+ unsigned int order;
u64 modify_size;
int err;
modify_size = rounddown_pow_of_two(size);
- order = ilog2(modify_size) - ilog2(mm->chunk_size);
+ pages = modify_size >> ilog2(mm->chunk_size);
+ order = fls(pages) - 1;
if (order == 0)
return -ENOSPC;
- for_each_free_tree(tree) {
- struct rb_root *root;
- struct rb_node *iter;
-
- root = &mm->free_trees[tree][order];
- if (rbtree_is_empty(root))
- continue;
-
- iter = rb_last(root);
- while (iter) {
- block = rbtree_get_free_block(iter);
-
- rhs_offset = gpu_buddy_block_offset(block);
-
- /* Allocate blocks traversing RHS */
- err = __gpu_buddy_alloc_range(mm, rhs_offset, size,
- &filled, blocks);
- if (err && err != -ENOSPC)
- return err;
- if (!err && IS_ALIGNED(rhs_offset, min_block_size))
- return 0;
- if (!err) {
- /* Allocate the unaligned RHS offset using round_down */
- gpu_buddy_free_list_internal(mm, blocks);
- err = __alloc_contig_aligned_retry(mm, rhs_offset,
- size,
- min_block_size,
- blocks);
- if (!err)
- return 0;
- if (err != -ENOSPC) {
- gpu_buddy_free_list_internal(mm, blocks);
- return err;
- }
- goto next;
- }
-
- if (size - filled > rhs_offset)
- goto next;
-
- lhs_offset = rhs_offset - (size - filled);
+ root = &mm->free_tree[order];
+ if (RB_EMPTY_ROOT(root))
+ return -ENOSPC;
- /* Allocate the unaligned LHS offset using round_down */
+ iter = rb_last(root);
+ while (iter) {
+ block = rbtree_get_free_block(iter);
+
+ rhs_offset = gpu_buddy_block_offset(block);
+
+ /* Allocate blocks traversing RHS */
+ err = __gpu_buddy_alloc_range(mm, rhs_offset, size,
+ flags, &filled, blocks);
+ if (err && err != -ENOSPC)
+ return err;
+ if (!err && IS_ALIGNED(rhs_offset, min_block_size))
+ return 0;
+ if (!err) {
+ /* Allocate the unaligned RHS offset using round_down */
gpu_buddy_free_list_internal(mm, blocks);
- err = __alloc_contig_aligned_retry(mm, lhs_offset, size,
- min_block_size, blocks);
+ err = __alloc_contig_aligned_retry(mm, rhs_offset,
+ size,
+ min_block_size,
+ flags, blocks);
if (!err)
return 0;
if (err != -ENOSPC) {
gpu_buddy_free_list_internal(mm, blocks);
return err;
}
-next:
+ goto next;
+ }
+
+ if (size - filled > rhs_offset)
+ goto next;
+
+ lhs_offset = rhs_offset - (size - filled);
+
+ /* Allocate the unaligned LHS offset using round_down */
+ gpu_buddy_free_list_internal(mm, blocks);
+ err = __alloc_contig_aligned_retry(mm, lhs_offset,
+ size,
+ min_block_size,
+ flags, blocks);
+ if (!err)
+ return 0;
+ if (err != -ENOSPC) {
gpu_buddy_free_list_internal(mm, blocks);
- iter = rb_prev(iter);
+ return err;
}
+next:
+ gpu_buddy_free_list_internal(mm, blocks);
+ iter = rb_prev(iter);
}
return -ENOSPC;
@@ -1282,6 +1805,7 @@ int gpu_buddy_block_trim(struct gpu_buddy *mm,
struct gpu_buddy_block *block;
u64 block_start, block_end;
LIST_HEAD(dfs);
+ bool was_clear;
u64 new_start;
int err;
@@ -1324,22 +1848,35 @@ int gpu_buddy_block_trim(struct gpu_buddy *mm,
}
list_del(&block->link);
- mark_free(mm, block);
+
+ was_clear = gpu_buddy_block_is_clear(block);
+
+ if (!was_clear)
+ gpu_dirty_tracker_mark_dirty(&mm->dirty,
+ gpu_buddy_block_offset(block),
+ gpu_buddy_block_size(mm, block));
+
+ __mark_free(mm, block, was_clear ? GPU_BLOCK_CLEAR : GPU_BLOCK_DIRTY);
mm->avail += gpu_buddy_block_size(mm, block);
- if (gpu_buddy_block_is_clear(block))
- mm->clear_avail += gpu_buddy_block_size(mm, block);
/* Prevent recursively freeing this node */
parent = block->parent;
block->parent = NULL;
list_add(&block->tmp_link, &dfs);
- err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL);
+ err = __alloc_range(mm, &dfs, new_start, new_size,
+ was_clear ? GPU_BUDDY_CLEAR_ALLOCATION : 0,
+ blocks, NULL);
if (err) {
mark_allocated(mm, block);
mm->avail -= gpu_buddy_block_size(mm, block);
- if (gpu_buddy_block_is_clear(block))
- mm->clear_avail -= gpu_buddy_block_size(mm, block);
+ if (!was_clear) {
+ gpu_dirty_tracker_remove_range(&mm->dirty,
+ gpu_buddy_block_offset(block),
+ gpu_buddy_block_size(mm, block));
+ }
+ if (was_clear)
+ block->header |= GPU_BUDDY_HEADER_CLEAR;
list_add(&block->link, blocks);
}
@@ -1348,6 +1885,22 @@ int gpu_buddy_block_trim(struct gpu_buddy *mm,
}
EXPORT_SYMBOL(gpu_buddy_block_trim);
+static bool dirty_steer_window(struct gpu_buddy *mm, u64 req_size,
+ u64 *start, u64 *end, unsigned long *flags)
+{
+ u64 aligned_start;
+ struct gpu_dirty_extent *ext =
+ gpu_dirty_tracker_find(&mm->dirty, req_size, &aligned_start);
+
+ if (!ext)
+ return false;
+
+ *start = aligned_start;
+ *end = ext->end;
+ *flags |= GPU_BUDDY_RANGE_ALLOCATION;
+ return true;
+}
+
static struct gpu_buddy_block *
__gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
u64 start, u64 end,
@@ -1355,18 +1908,36 @@ __gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
unsigned int order,
unsigned long flags)
{
- if (flags & GPU_BUDDY_RANGE_ALLOCATION)
+ struct gpu_buddy_block *block;
+ bool steered = false;
+
+ /* Allocate from dirty tracker */
+ if (!(flags & GPU_BUDDY_RANGE_ALLOCATION) &&
+ !(flags & GPU_BUDDY_CLEAR_ALLOCATION) &&
+ size >= min_block_size &&
+ gpu_buddy_clear_avail(mm) && mm->dirty.total_dirty) {
+ u64 block_size = mm->chunk_size << order;
+
+ steered = dirty_steer_window(mm, block_size,
+ &start, &end, &flags);
+ }
+
+ if (flags & GPU_BUDDY_RANGE_ALLOCATION) {
/* Allocate traversing within the range */
- return __gpu_buddy_alloc_range_bias(mm, start, end,
- order, flags);
- else if (size < min_block_size)
+ block = __alloc_range_bias(mm, start, end, order, flags);
+ if (!IS_ERR(block) || !steered)
+ return block;
+
+ flags &= ~GPU_BUDDY_RANGE_ALLOCATION;
+ }
+
+ if (size < min_block_size)
/* Allocate from an offset-aligned region without size rounding */
return gpu_buddy_offset_aligned_allocation(mm, size,
- min_block_size,
- flags);
- else
- /* Allocate from freetree */
- return alloc_from_freetree(mm, order, flags);
+ min_block_size);
+
+ /* Allocate from freetree */
+ return alloc_from_freetree(mm, order, flags);
}
/**
@@ -1427,7 +1998,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
if (!IS_ALIGNED(start | end, min_block_size))
return -EINVAL;
- return __gpu_buddy_alloc_range(mm, start, size, NULL, blocks);
+ return __gpu_buddy_alloc_range(mm, start, size, flags, NULL, blocks);
}
original_size = size;
@@ -1453,7 +2024,8 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
if ((flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) &&
!(flags & GPU_BUDDY_RANGE_ALLOCATION))
return __alloc_contig_try_harder(mm, original_size,
- original_min_size, blocks);
+ original_min_size,
+ flags, blocks);
return -EINVAL;
}
@@ -1468,8 +2040,6 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
BUG_ON(size >= min_block_size && order < min_order);
do {
- unsigned int fallback_order;
-
block = __gpu_buddy_alloc_blocks(mm, start,
end,
size,
@@ -1479,48 +2049,40 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
if (!IS_ERR(block))
break;
- if (size < min_block_size) {
- fallback_order = order;
- } else if (order == min_order) {
- fallback_order = min_order;
- } else {
+ if (size >= min_block_size && order > min_order) {
order--;
continue;
}
- /* Try allocation through force merge method */
- if (mm->clear_avail &&
- !__force_merge(mm, start, end, fallback_order)) {
- block = __gpu_buddy_alloc_blocks(mm, start,
- end,
- size,
- min_block_size,
- fallback_order,
- flags);
- if (!IS_ERR(block)) {
- order = fallback_order;
- break;
- }
- }
-
/*
* Try contiguous block allocation through
* try harder method.
*/
if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION &&
- !(flags & GPU_BUDDY_RANGE_ALLOCATION))
- return __alloc_contig_try_harder(mm,
- original_size,
- original_min_size,
- blocks);
+ !(flags & GPU_BUDDY_RANGE_ALLOCATION)) {
+ err = __alloc_contig_try_harder(mm,
+ original_size,
+ original_min_size,
+ flags,
+ blocks);
+ if (!err)
+ return 0;
+ if (err != -ENOSPC)
+ return err;
+ goto err_free;
+ }
err = -ENOSPC;
goto err_free;
} while (1);
+ if (!gpu_buddy_block_is_clear(block))
+ gpu_dirty_tracker_remove_range(&mm->dirty,
+ gpu_buddy_block_offset(block),
+ gpu_buddy_block_size(mm, block));
+
mark_allocated(mm, block);
mm->avail -= gpu_buddy_block_size(mm, block);
- if (gpu_buddy_block_is_clear(block))
- mm->clear_avail -= gpu_buddy_block_size(mm, block);
+
kmemleak_update_trace(block);
list_add_tail(&block->link, &allocated);
@@ -1594,7 +2156,8 @@ void gpu_buddy_print(struct gpu_buddy *mm)
gpu_buddy_driver_lock_held(mm);
pr_info("chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n",
- mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
+ mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20,
+ gpu_buddy_clear_avail(mm) >> 20);
for (order = mm->max_order; order >= 0; order--) {
u64 free_count = mm->free_scoreboard[order];
@@ -1615,6 +2178,7 @@ EXPORT_SYMBOL(gpu_buddy_print);
static void gpu_buddy_module_exit(void)
{
+ kmem_cache_destroy(slab_extents);
kmem_cache_destroy(slab_blocks);
}
@@ -1624,7 +2188,15 @@ static int __init gpu_buddy_module_init(void)
if (!slab_blocks)
return -ENOMEM;
+ slab_extents = KMEM_CACHE(gpu_dirty_extent, 0);
+ if (!slab_extents)
+ goto err_destroy_blocks;
+
return 0;
+
+err_destroy_blocks:
+ kmem_cache_destroy(slab_blocks);
+ return -ENOMEM;
}
module_init(gpu_buddy_module_init);
diff --git a/drivers/gpu/drm/adp/adp_drv.c b/drivers/gpu/drm/adp/adp_drv.c
index ce9dd6222634..fdf0e6316175 100644
--- a/drivers/gpu/drm/adp/adp_drv.c
+++ b/drivers/gpu/drm/adp/adp_drv.c
@@ -351,7 +351,7 @@ static const struct drm_crtc_funcs adp_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = adp_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 2fe934036e36..4426e18f7543 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -36,7 +36,6 @@
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc/dc_dmub_srv.h"
-#include "dc/dc_edid_parser.h"
#include "dc/dc_stat.h"
#include "dc/dc_state.h"
#include "amdgpu_dm_trace.h"
@@ -2883,7 +2882,8 @@ STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state)
EXPORT_IF_KUNIT(modereset_required);
STATIC_IFN_KUNIT int
-fill_plane_color_attributes(const struct drm_plane_state *plane_state,
+fill_plane_color_attributes(struct drm_atomic_commit *state,
+ const struct drm_plane_state *plane_state,
const enum surface_pixel_format format,
enum dc_color_space *color_space)
{
@@ -2892,7 +2892,7 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state,
*color_space = COLOR_SPACE_SRGB;
/* Ignore properties when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set */
- if (plane_state->state && plane_state->state->plane_color_pipeline)
+ if (state && state->plane_color_pipeline)
return 0;
/* DRM color properties only affect non-RGB formats. */
@@ -2933,6 +2933,7 @@ EXPORT_IF_KUNIT(fill_plane_color_attributes);
static int
fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
+ struct drm_atomic_commit *state,
const struct drm_plane_state *plane_state,
struct dc_plane_info *plane_info,
struct dc_plane_address *address,
@@ -3025,7 +3026,7 @@ fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
plane_info->layer_index = plane_state->normalized_zpos;
- ret = fill_plane_color_attributes(plane_state, plane_info->format,
+ ret = fill_plane_color_attributes(state, plane_state, plane_info->format,
&plane_info->color_space);
if (ret)
return ret;
@@ -3066,7 +3067,7 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
dc_plane_state->clip_rect = scaling_info.clip_rect;
dc_plane_state->scaling_quality = scaling_info.scaling_quality;
- ret = fill_dc_plane_info_and_addr(adev, plane_state,
+ ret = fill_dc_plane_info_and_addr(adev, plane_state->state, plane_state,
&plane_info,
&dc_plane_state->address,
afb->tmz_surface);
@@ -3903,11 +3904,13 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
}
fill_dc_plane_info_and_addr(
- dm->adev, new_plane_state,
+ dm->adev, state, new_plane_state,
&bundle->plane_infos[planes_count],
&bundle->flip_addrs[planes_count].address,
afb->tmz_surface);
+ bundle->plane_infos[planes_count].color_space = dc_plane->color_space;
+
drm_dbg_state(state->dev, "plane: id=%d dcc_en=%d\n",
new_plane_state->plane->index,
bundle->plane_infos[planes_count].dcc.enable);
@@ -5897,6 +5900,19 @@ static int dm_update_plane_state(struct dc *dc,
if (ret)
goto out;
+ /*
+ * Recreating the plane re-derives its DC color pipeline from the
+ * colorop states in this commit. Pull the plane's colorops in so
+ * the derivation sees the current pipeline; otherwise every stage
+ * silently defaults to bypass (dropping YUV->RGB, shaper, 3D LUT
+ * and regamma) when userspace didn't touch color in this commit.
+ */
+ if (new_plane_state->color_pipeline) {
+ ret = drm_atomic_add_affected_colorops(state, plane);
+ if (ret)
+ goto out;
+ }
+
WARN_ON(dm_new_plane_state->dc_state);
dc_new_plane_state = dc_create_plane_state(dc);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index 3524931451c8..774028dd9ff4 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -1172,9 +1172,10 @@ struct dm_atomic_state *dm_atomic_get_new_state(struct drm_atomic_commit *state)
void dm_atomic_destroy_state(struct drm_private_obj *obj,
struct drm_private_state *state);
int dm_plane_layer_index_cmp(const void *a, const void *b);
-int fill_plane_color_attributes(const struct drm_plane_state *plane_state,
- const enum surface_pixel_format format,
- enum dc_color_space *color_space);
+int fill_plane_color_attributes(struct drm_atomic_commit *state,
+ const struct drm_plane_state *plane_state,
+ const enum surface_pixel_format format,
+ enum dc_color_space *color_space);
bool modereset_required(struct drm_crtc_state *crtc_state);
bool is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index d55dc06167a8..1125ebac6ccb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -1551,6 +1551,8 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state,
struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func;
struct drm_colorop *colorop = colorop_state->colorop;
struct drm_device *drm = colorop->dev;
+ struct dc_color_caps *color_caps = NULL;
+ bool is_subsampled_format;
if (colorop->type != DRM_COLOROP_1D_CURVE)
return -EINVAL;
@@ -1564,14 +1566,82 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state,
}
drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id);
-
tf->type = TF_TYPE_PREDEFINED;
+
+ /* Check if format requires post-scale color processing (subsampled formats) */
+ is_subsampled_format = (dc_plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN &&
+ dc_plane_state->format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END);
+
tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+ if (dc_plane_state->ctx && dc_plane_state->ctx->dc)
+ color_caps = &dc_plane_state->ctx->dc->caps.color;
+
+ if (!mod_color_calculate_degamma_params(color_caps, tf, NULL,
+ is_subsampled_format)) {
+ drm_err(drm, "Failed to calculate degamma params\n");
+ return -EINVAL;
+ }
+
return 0;
}
EXPORT_IF_KUNIT(__set_colorop_in_tf_1d_curve);
+static int
+__set_dm_plane_colorop_fixed_matrix(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_atomic_commit *state = plane_state->state;
+ int i = 0;
+
+ old_colorop = colorop;
+
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (!colorop_state)
+ return -EINVAL;
+
+ if (colorop_state->bypass) {
+ dc_plane_state->color_space = COLOR_SPACE_SRGB;
+ return 0;
+ }
+
+ switch (colorop_state->fixed_matrix_type) {
+ case DRM_COLOROP_FM_YCBCR601_FULL_RGB:
+ dc_plane_state->color_space = COLOR_SPACE_YCBCR601;
+ break;
+ case DRM_COLOROP_FM_YCBCR601_LIMITED_RGB:
+ dc_plane_state->color_space = COLOR_SPACE_YCBCR601_LIMITED;
+ break;
+ case DRM_COLOROP_FM_YCBCR709_FULL_RGB:
+ dc_plane_state->color_space = COLOR_SPACE_YCBCR709;
+ break;
+ case DRM_COLOROP_FM_YCBCR709_LIMITED_RGB:
+ dc_plane_state->color_space = COLOR_SPACE_YCBCR709_LIMITED;
+ break;
+ case DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB:
+ dc_plane_state->color_space = COLOR_SPACE_2020_YCBCR_FULL;
+ break;
+ case DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB:
+ dc_plane_state->color_space = COLOR_SPACE_2020_YCBCR_LIMITED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dc_plane_state->update_bits.full_update = 1;
+
+ return 0;
+}
+
STATIC_IFN_KUNIT int
__set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state,
struct dc_plane_state *dc_plane_state,
@@ -1976,10 +2046,21 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state,
bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend;
int ret;
- /* 1D Curve - DEGAM TF */
+ /* Fixed Matrix (YUV to RGB) */
if (!colorop)
return -EINVAL;
+ ret = __set_dm_plane_colorop_fixed_matrix(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+
+ /* 1D Curve - DEGAM TF */
+ colorop = colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no degamma colorop found\n");
+ return -EINVAL;
+ }
+
ret = __set_dm_plane_colorop_degamma(plane_state, dc_plane_state, colorop);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index fae9007b9998..d66f71336247 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -55,6 +55,14 @@ const u64 amdgpu_dm_supported_blnd_tfs =
BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
EXPORT_IF_KUNIT(amdgpu_dm_supported_blnd_tfs);
+const u64 amdgpu_dm_supported_fm =
+ BIT(DRM_COLOROP_FM_YCBCR601_FULL_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR601_LIMITED_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR709_FULL_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR709_LIMITED_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB);
+
#define MAX_COLOR_PIPELINE_OPS 10
#define LUT3D_SIZE 17
@@ -73,6 +81,23 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
memset(ops, 0, sizeof(ops));
+ /* Fixed Matrix (YUV to RGB) */
+ ops[i] = kzalloc_obj(*ops[0]);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_fixed_matrix_init(dev, ops[i], plane, &dm_colorop_funcs,
+ amdgpu_dm_supported_fm,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ list->type = ops[i]->base.id;
+
+ i++;
+
/* 1D curve - DEGAM TF */
ops[i] = kzalloc_obj(*ops[0]);
if (!ops[i]) {
@@ -86,7 +111,7 @@ amdgpu_dm_build_default_pipeline(struct drm_device *dev, struct drm_plane *plane
if (ret)
goto cleanup;
- list->type = ops[i]->base.id;
+ drm_colorop_set_next_property(ops[i - 1], ops[i]);
i++;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
index 77364d954d3b..7802efa5312d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
@@ -30,6 +30,7 @@
extern const u64 amdgpu_dm_supported_degam_tfs;
extern const u64 amdgpu_dm_supported_shaper_tfs;
extern const u64 amdgpu_dm_supported_blnd_tfs;
+extern const u64 amdgpu_dm_supported_fm;
int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index c8a1ab8c3b16..e7b555fa666f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -27,7 +27,6 @@
#include "dm_services_types.h"
#include "dc.h"
#include "dc/dc_dmub_srv.h"
-#include "dc/dc_edid_parser.h"
#include "dc/dc_stat.h"
#include "dc/dc_state.h"
#include "dc/dc_stream.h"
@@ -3591,139 +3590,6 @@ void dm_restore_drm_connector_state(struct drm_device *dev,
dm_force_atomic_commit(&aconnector->base);
}
-static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
- unsigned int offset,
- unsigned int total_length,
- u8 *data,
- unsigned int length,
- struct amdgpu_hdmi_vsdb_info *vsdb)
-{
- bool res;
- union dmub_rb_cmd cmd;
- struct dmub_cmd_send_edid_cea *input;
- struct dmub_cmd_edid_cea_output *output;
-
- if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
- return false;
-
- memset(&cmd, 0, sizeof(cmd));
-
- input = &cmd.edid_cea.data.input;
-
- cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
- cmd.edid_cea.header.sub_type = 0;
- cmd.edid_cea.header.payload_bytes =
- sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
- input->offset = offset;
- input->length = length;
- input->cea_total_length = total_length;
- memcpy(input->payload, data, length);
-
- res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
- if (!res) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n");
- return false;
- }
-
- output = &cmd.edid_cea.data.output;
-
- if (output->type == DMUB_CMD__EDID_CEA_ACK) {
- if (!output->ack.success) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n",
- output->ack.offset);
- }
- } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
- if (!output->amd_vsdb.vsdb_found)
- return false;
-
- vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
- vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
- vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
- vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
- vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code;
- } else {
- drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n");
- return false;
- }
-
- return true;
-}
-
-static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- bool res;
- int offset;
-
- /* send 8 bytes a time */
- if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
- return false;
-
- if (i+8 == len) {
- /* EDID block sent completed, expect result */
- int version, min_rate, max_rate;
-
- res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
- if (res) {
- /* amd vsdb found */
- vsdb_info->freesync_supported = 1;
- vsdb_info->amd_vsdb_version = version;
- vsdb_info->min_refresh_rate_hz = min_rate;
- vsdb_info->max_refresh_rate_hz = max_rate;
- /* Not enabled on DMCU*/
- vsdb_info->freesync_mccs_vcp_code = 0;
- return true;
- }
- /* not amd vsdb */
- return false;
- }
-
- /* check for ack*/
- res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
- if (!res)
- return false;
- }
-
- return false;
-}
-
-static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- /* send 8 bytes a time */
- if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
- return false;
- }
-
- return vsdb_info->freesync_supported;
-}
-
-static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
- bool ret;
-
- mutex_lock(&adev->dm.dc_lock);
- if (adev->dm.dmub_srv)
- ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
- else
- ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
- mutex_unlock(&adev->dm.dc_lock);
- return ret;
-}
-
STATIC_IFN_KUNIT void parse_edid_displayid_vrr(struct drm_connector *connector,
const struct edid *edid)
{
@@ -3775,44 +3641,15 @@ STATIC_IFN_KUNIT int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
+ vsdb_info->freesync_supported = connector->display_info.amd_vsdb.freesync_supported;
+ vsdb_info->min_refresh_rate_hz = connector->display_info.amd_vsdb.min_frame_rate;
+ vsdb_info->max_refresh_rate_hz = connector->display_info.amd_vsdb.max_frame_rate;
+ vsdb_info->freesync_mccs_vcp_code = connector->display_info.amd_vsdb.freesync_vcp_code;
return connector->display_info.amd_vsdb.version != 0;
}
EXPORT_IF_KUNIT(get_amd_vsdb);
-STATIC_IFN_KUNIT int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- const struct edid *edid,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- u8 *edid_ext = NULL;
- int i;
- bool valid_vsdb_found = false;
-
- /*----- drm_find_cea_extension() -----*/
- /* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == CEA_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return -ENODEV;
-
- /*----- cea_db_offsets() -----*/
- if (edid_ext[0] != CEA_EXT)
- return -ENODEV;
-
- valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info);
-
- return valid_vsdb_found ? i : -ENODEV;
-}
-EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb);
-
/**
* amdgpu_dm_update_freesync_caps - Update Freesync capabilities
*
@@ -3831,7 +3668,7 @@ EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb);
void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
const struct drm_edid *drm_edid, bool do_mccs)
{
- int i = 0;
+ bool has_vsdb = 0;
struct amdgpu_dm_connector *amdgpu_dm_connector =
to_amdgpu_dm_connector(connector);
struct dm_connector_state *dm_con_state = NULL;
@@ -3876,63 +3713,57 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
connector->display_info.monitor_range.max_vfreq == 0))
parse_edid_displayid_vrr(connector, edid);
- if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
+ has_vsdb = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info) != 0;
+
+ if (has_vsdb) {
+ amdgpu_dm_connector->vsdb_info = vsdb_info;
+
+ /* copy refresh rate info as long as VSDB advertises FreeSync */
+ if (vsdb_info.freesync_supported) {
+ amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
+ amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
+
+ connector->display_info.monitor_range.min_vfreq =
+ vsdb_info.min_refresh_rate_hz;
+ connector->display_info.monitor_range.max_vfreq =
+ vsdb_info.max_refresh_rate_hz;
}
+ } else {
+ /* fall back to the base EDID range when there is no VSDB */
+ amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
+ amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
+ }
- get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
+ if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ sink->sink_signal == SIGNAL_TYPE_EDP) {
+ if (amdgpu_dm_connector->dc_link &&
+ amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param &&
+ amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
if (vsdb_info.replay_mode) {
- amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
- amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
}
+ } else if (has_vsdb && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+ sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
- } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported) {
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
+ if (vsdb_info.freesync_supported &&
+ amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
}
if (amdgpu_dm_connector->dc_link)
as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
- if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
- amdgpu_dm_connector->pack_sdp_v1_3 = true;
- amdgpu_dm_connector->as_type = as_type;
+ if (has_vsdb && as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
+ sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
+ if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
+ amdgpu_dm_connector->pack_sdp_v1_3 = true;
+ amdgpu_dm_connector->as_type = as_type;
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
+ if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
}
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
index 5645866610fc..d9331e999ec3 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
@@ -335,7 +335,7 @@ static void dm_test_fill_color_attr_rgb_format(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -355,7 +355,7 @@ static void dm_test_fill_color_attr_bt601_full(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT601;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -375,7 +375,7 @@ static void dm_test_fill_color_attr_bt601_limited(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT601;
plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -396,7 +396,7 @@ static void dm_test_fill_color_attr_bt709_full(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -416,7 +416,7 @@ static void dm_test_fill_color_attr_bt709_limited(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -437,7 +437,7 @@ static void dm_test_fill_color_attr_bt2020_full(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -458,7 +458,7 @@ static void dm_test_fill_color_attr_bt2020_limited(struct kunit *test)
plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020;
plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -479,7 +479,7 @@ static void dm_test_fill_color_attr_invalid_encoding(struct kunit *test)
plane_state.color_encoding = 99;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
- ret = fill_plane_color_attributes(&plane_state,
+ ret = fill_plane_color_attributes(NULL, &plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile
index 93d02956c5eb..e88a9cea8d88 100644
--- a/drivers/gpu/drm/amd/display/dc/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/Makefile
@@ -63,7 +63,6 @@ include $(AMD_DC)
FILES =
FILES += dc_dmub_srv.o
-FILES += dc_edid_parser.o
FILES += dc_fused_io.o
FILES += dc_helper.o
FILES += core/dc.o
diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
deleted file mode 100644
index 0db5b49e9d5e..000000000000
--- a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2021 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: AMD
- *
- */
-
-#include "dce/dce_dmcu.h"
-#include "dc_edid_parser.h"
-
-bool dc_edid_parser_send_cea(struct dc *dc,
- int offset,
- int total_length,
- uint8_t *data,
- int length)
-{
- struct dmcu *dmcu = dc->res_pool->dmcu;
-
- if (dmcu &&
- dmcu->funcs->is_dmcu_initialized(dmcu) &&
- dmcu->funcs->send_edid_cea) {
- return dmcu->funcs->send_edid_cea(dmcu,
- offset,
- total_length,
- data,
- length);
- }
-
- return false;
-}
-
-bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset)
-{
- struct dmcu *dmcu = dc->res_pool->dmcu;
-
- if (dmcu &&
- dmcu->funcs->is_dmcu_initialized(dmcu) &&
- dmcu->funcs->recv_edid_cea_ack) {
- return dmcu->funcs->recv_edid_cea_ack(dmcu, offset);
- }
-
- return false;
-}
-
-bool dc_edid_parser_recv_amd_vsdb(struct dc *dc,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate)
-{
- struct dmcu *dmcu = dc->res_pool->dmcu;
-
- if (dmcu &&
- dmcu->funcs->is_dmcu_initialized(dmcu) &&
- dmcu->funcs->recv_amd_vsdb) {
- return dmcu->funcs->recv_amd_vsdb(dmcu,
- version,
- min_frame_rate,
- max_frame_rate);
- }
-
- return false;
-}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
deleted file mode 100644
index da67ec06f0a2..000000000000
--- a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2021 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: AMD
- *
- */
-
-#ifndef _DC_EDID_PARSER_H_
-#define _DC_EDID_PARSER_H_
-
-#include "core_types.h"
-
-bool dc_edid_parser_send_cea(struct dc *dc,
- int offset,
- int total_length,
- uint8_t *data,
- int length);
-
-bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset);
-
-bool dc_edid_parser_recv_amd_vsdb(struct dc *dc,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate);
-
-#endif /* _DC_EDID_PARSER_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
index 25ebd8a52ae4..23de1c793ec3 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
@@ -56,9 +56,6 @@
#define MCP_BL_SET_PWM_FRAC 0x6A /* Enable or disable Fractional PWM */
#define CRC_WIN_NOTIFY 0x92
#define CRC_STOP_UPDATE 0x93
-#define MCP_SEND_EDID_CEA 0xA0
-#define EDID_CEA_CMD_ACK 1
-#define EDID_CEA_CMD_NACK 2
#define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK 0x00000001L
// PSP FW version
@@ -812,121 +809,6 @@ static bool dcn20_unlock_phy(struct dmcu *dmcu)
return true;
}
-static bool dcn10_send_edid_cea(struct dmcu *dmcu,
- int offset,
- int total_length,
- uint8_t *data,
- int length)
-{
- struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu);
- uint32_t header, data1, data2;
-
- /* If microcontroller is not running, do nothing */
- if (dmcu->dmcu_state != DMCU_RUNNING)
- return false;
-
- if (length > 8 || length <= 0)
- return false;
-
- header = ((uint32_t)offset & 0xFFFF) << 16 | (total_length & 0xFFFF);
- data1 = (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) |
- (((uint32_t)data[2]) << 8) | ((uint32_t)data[3]);
- data2 = (((uint32_t)data[4]) << 24) | (((uint32_t)data[5]) << 16) |
- (((uint32_t)data[6]) << 8) | ((uint32_t)data[7]);
-
- /* waitDMCUReadyForCmd */
- REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000);
-
- /* setDMCUParam_Cmd */
- REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SEND_EDID_CEA);
-
- REG_WRITE(MASTER_COMM_DATA_REG1, header);
- REG_WRITE(MASTER_COMM_DATA_REG2, data1);
- REG_WRITE(MASTER_COMM_DATA_REG3, data2);
-
- /* notifyDMCUMsg */
- REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
-
- /* waitDMCUReadyForCmd */
- REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000);
-
- return true;
-}
-
-static bool dcn10_get_scp_results(struct dmcu *dmcu,
- uint32_t *cmd,
- uint32_t *data1,
- uint32_t *data2,
- uint32_t *data3)
-{
- struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu);
-
- /* If microcontroller is not running, do nothing */
- if (dmcu->dmcu_state != DMCU_RUNNING)
- return false;
-
- *cmd = REG_READ(SLAVE_COMM_CMD_REG);
- *data1 = REG_READ(SLAVE_COMM_DATA_REG1);
- *data2 = REG_READ(SLAVE_COMM_DATA_REG2);
- *data3 = REG_READ(SLAVE_COMM_DATA_REG3);
-
- /* clear SCP interrupt */
- REG_UPDATE(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, 0);
-
- return true;
-}
-
-static bool dcn10_recv_amd_vsdb(struct dmcu *dmcu,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate)
-{
- uint32_t data[4];
- int cmd, ack, len;
-
- if (!dcn10_get_scp_results(dmcu, &data[0], &data[1], &data[2], &data[3]))
- return false;
-
- cmd = data[0] & 0x3FF;
- len = (data[0] >> 10) & 0x3F;
- ack = data[1];
-
- if (cmd != MCP_SEND_EDID_CEA || ack != EDID_CEA_CMD_ACK || len != 12)
- return false;
-
- if ((data[2] & 0xFF)) {
- *version = (data[2] >> 8) & 0xFF;
- *min_frame_rate = (data[3] >> 16) & 0xFFFF;
- *max_frame_rate = data[3] & 0xFFFF;
- return true;
- }
-
- return false;
-}
-
-static bool dcn10_recv_edid_cea_ack(struct dmcu *dmcu, int *offset)
-{
- uint32_t data[4];
- int cmd, ack;
-
- if (!dcn10_get_scp_results(dmcu,
- &data[0], &data[1], &data[2], &data[3]))
- return false;
-
- cmd = data[0] & 0x3FF;
- ack = data[1];
-
- if (cmd != MCP_SEND_EDID_CEA)
- return false;
-
- if (ack == EDID_CEA_CMD_ACK)
- return true;
-
- *offset = data[2]; /* nack */
- return false;
-}
-
-
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
static void dcn10_forward_crc_window(struct dmcu *dmcu,
struct rect *rect,
@@ -1030,9 +912,6 @@ static const struct dmcu_funcs dcn10_funcs = {
.get_psr_state = dcn10_get_dmcu_psr_state,
.set_psr_wait_loop = dcn10_psr_wait_loop,
.get_psr_wait_loop = dcn10_get_psr_wait_loop,
- .send_edid_cea = dcn10_send_edid_cea,
- .recv_amd_vsdb = dcn10_recv_amd_vsdb,
- .recv_edid_cea_ack = dcn10_recv_edid_cea_ack,
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
.forward_crc_window = dcn10_forward_crc_window,
.stop_crc_win_update = dcn10_stop_crc_win_update,
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h
index de3113ecbc77..eebfd177463d 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h
@@ -74,16 +74,6 @@ struct dmcu_funcs {
bool (*is_dmcu_initialized)(struct dmcu *dmcu);
bool (*lock_phy)(struct dmcu *dmcu);
bool (*unlock_phy)(struct dmcu *dmcu);
- bool (*send_edid_cea)(struct dmcu *dmcu,
- int offset,
- int total_length,
- uint8_t *data,
- int length);
- bool (*recv_amd_vsdb)(struct dmcu *dmcu,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate);
- bool (*recv_edid_cea_ack)(struct dmcu *dmcu, int *offset);
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
void (*forward_crc_window)(struct dmcu *dmcu,
struct rect *rect,
diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
index f0231f355fa7..7d29599a0b1d 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -1924,10 +1924,6 @@ enum dmub_cmd_type {
*/
DMUB_CMD__DPIA = 77,
/**
- * Command type used for EDID CEA parsing
- */
- DMUB_CMD__EDID_CEA = 79,
- /**
* Command type used for getting usbc cable ID
*/
DMUB_CMD_GET_USBC_CABLE_ID = 81,
@@ -6887,69 +6883,6 @@ struct dmub_rb_cmd_transmitter_set_phy_fsm {
};
/**
- * Maximum number of bytes a chunk sent to DMUB for parsing
- */
-#define DMUB_EDID_CEA_DATA_CHUNK_BYTES 8
-
-/**
- * Represent a chunk of CEA blocks sent to DMUB for parsing
- */
-struct dmub_cmd_send_edid_cea {
- uint16_t offset; /**< offset into the CEA block */
- uint8_t length; /**< number of bytes in payload to copy as part of CEA block */
- uint16_t cea_total_length; /**< total length of the CEA block */
- uint8_t payload[DMUB_EDID_CEA_DATA_CHUNK_BYTES]; /**< data chunk of the CEA block */
- uint8_t pad[3]; /**< padding and for future expansion */
-};
-
-/**
- * Result of VSDB parsing from CEA block
- */
-struct dmub_cmd_edid_cea_amd_vsdb {
- uint8_t vsdb_found; /**< 1 if parsing has found valid AMD VSDB */
- uint8_t freesync_supported; /**< 1 if Freesync is supported */
- uint16_t amd_vsdb_version; /**< AMD VSDB version */
- uint16_t min_frame_rate; /**< Maximum frame rate */
- uint16_t max_frame_rate; /**< Minimum frame rate */
- uint8_t freesync_mccs_vcp_code; /**< Freesync MCCS VCP code */
-};
-
-/**
- * Result of sending a CEA chunk
- */
-struct dmub_cmd_edid_cea_ack {
- uint16_t offset; /**< offset of the chunk into the CEA block */
- uint8_t success; /**< 1 if this sending of chunk succeeded */
- uint8_t pad; /**< padding and for future expansion */
-};
-
-/**
- * Specify whether the result is an ACK/NACK or the parsing has finished
- */
-enum dmub_cmd_edid_cea_reply_type {
- DMUB_CMD__EDID_CEA_AMD_VSDB = 1, /**< VSDB parsing has finished */
- DMUB_CMD__EDID_CEA_ACK = 2, /**< acknowledges the CEA sending is OK or failing */
-};
-
-/**
- * Definition of a DMUB_CMD__EDID_CEA command.
- */
-struct dmub_rb_cmd_edid_cea {
- struct dmub_cmd_header header; /**< Command header */
- union dmub_cmd_edid_cea_data {
- struct dmub_cmd_send_edid_cea input; /**< input to send CEA chunks */
- struct dmub_cmd_edid_cea_output { /**< output with results */
- uint8_t type; /**< dmub_cmd_edid_cea_reply_type */
- union {
- struct dmub_cmd_edid_cea_amd_vsdb amd_vsdb;
- struct dmub_cmd_edid_cea_ack ack;
- };
- } output; /**< output to retrieve ACK/NACK or VSDB parsing results */
- } data; /**< Command data */
-
-};
-
-/**
* struct dmub_cmd_cable_id_input - Defines the input of DMUB_CMD_GET_USBC_CABLE_ID command.
*/
struct dmub_cmd_cable_id_input {
@@ -7802,10 +7735,6 @@ union dmub_rb_cmd {
*/
struct dmub_rb_cmd_set_tps_notification set_tps_notification;
/**
- * Definition of a DMUB_CMD__EDID_CEA command.
- */
- struct dmub_rb_cmd_edid_cea edid_cea;
- /**
* Definition of a DMUB_CMD_GET_USBC_CABLE_ID command.
*/
struct dmub_rb_cmd_get_usbc_cable_id cable_id;
diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
index 03d88e78165d..5786ef5787ac 100644
--- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
+++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
@@ -1696,7 +1696,8 @@ bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps,
return true;
if (dc_caps != NULL &&
- dc_caps->dpp.dcn_arch == 1) {
+ dc_caps->dpp.dcn_arch == 1 &&
+ !map_user_ramp) {
if (input_tf->tf == TRANSFER_FUNCTION_PQ &&
dc_caps->dpp.dgam_rom_caps.pq == 1)
diff --git a/drivers/gpu/drm/arm/display/include/malidp_product.h b/drivers/gpu/drm/arm/display/include/malidp_product.h
index 6f954bcdf40e..f9a3ee7ba4b7 100644
--- a/drivers/gpu/drm/arm/display/include/malidp_product.h
+++ b/drivers/gpu/drm/arm/display/include/malidp_product.h
@@ -8,14 +8,18 @@
#define _MALIDP_PRODUCT_H_
/* Product identification */
+/* GLB_CORE_ID fields as per HW specification:
+ * MINOR is 8 bits ([11:4]) and STATUS is 4 bits ([3:0]).
+ * Update masks/shifts accordingly.
+ */
#define MALIDP_CORE_ID(__product, __major, __minor, __status) \
((((__product) & 0xFFFF) << 16) | (((__major) & 0xF) << 12) | \
- (((__minor) & 0xF) << 8) | ((__status) & 0xFF))
+ (((__minor) & 0xFF) << 4) | ((__status) & 0xF))
#define MALIDP_CORE_ID_PRODUCT_ID(__core_id) ((__u32)(__core_id) >> 16)
#define MALIDP_CORE_ID_MAJOR(__core_id) (((__u32)(__core_id) >> 12) & 0xF)
-#define MALIDP_CORE_ID_MINOR(__core_id) (((__u32)(__core_id) >> 8) & 0xF)
-#define MALIDP_CORE_ID_STATUS(__core_id) (((__u32)(__core_id)) & 0xFF)
+#define MALIDP_CORE_ID_MINOR(__core_id) (((__u32)(__core_id) >> 4) & 0xFF)
+#define MALIDP_CORE_ID_STATUS(__core_id) ((__u32)(__core_id) & 0xF)
/* Mali-display product IDs */
#define MALIDP_D71_PRODUCT_ID 0x0071
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index e8cb782a6f8e..3d81b413efae 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -11,9 +11,9 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_bridge.h>
#include "komeda_dev.h"
@@ -493,19 +493,17 @@ static const struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
.mode_fixup = komeda_crtc_mode_fixup,
};
-static void komeda_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *komeda_crtc_create_state(struct drm_crtc *crtc)
{
struct komeda_crtc_state *state;
- if (crtc->state)
- __drm_atomic_helper_crtc_destroy_state(crtc->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- kfree(to_kcrtc_st(crtc->state));
- crtc->state = NULL;
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
- state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ return &state->base;
}
static struct drm_crtc_state *
@@ -555,7 +553,7 @@ static const struct drm_crtc_funcs komeda_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = komeda_crtc_reset,
+ .atomic_create_state = komeda_crtc_create_state,
.atomic_duplicate_state = komeda_crtc_atomic_duplicate_state,
.atomic_destroy_state = komeda_crtc_atomic_destroy_state,
.enable_vblank = komeda_crtc_vblank_enable,
@@ -635,6 +633,10 @@ static int komeda_attach_bridge(struct device *dev,
return err;
}
+static const struct drm_encoder_funcs komeda_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static int komeda_crtc_add(struct komeda_kms_dev *kms,
struct komeda_crtc *kcrtc)
{
@@ -658,7 +660,8 @@ static int komeda_crtc_add(struct komeda_kms_dev *kms,
* bridge
*/
kcrtc->encoder.possible_crtcs = drm_crtc_mask(crtc);
- err = drm_simple_encoder_init(base, encoder, DRM_MODE_ENCODER_TMDS);
+ err = drm_encoder_init(base, encoder, &komeda_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
if (err)
return err;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
index 57a4a0c7d404..955dbef5d34d 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
@@ -132,19 +132,17 @@ static void komeda_plane_destroy(struct drm_plane *plane)
kfree(to_kplane(plane));
}
-static void komeda_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *komeda_plane_create_state(struct drm_plane *plane)
{
struct komeda_plane_state *state;
- if (plane->state)
- __drm_atomic_helper_plane_destroy_state(plane->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- kfree(plane->state);
- plane->state = NULL;
+ __drm_atomic_helper_plane_state_init(&state->base, plane);
- state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_plane_reset(plane, &state->base);
+ return &state->base;
}
static struct drm_plane_state *
@@ -188,7 +186,7 @@ static const struct drm_plane_funcs komeda_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = komeda_plane_destroy,
- .reset = komeda_plane_reset,
+ .atomic_create_state = komeda_plane_create_state,
.atomic_duplicate_state = komeda_plane_atomic_duplicate_state,
.atomic_destroy_state = komeda_plane_atomic_destroy_state,
.format_mod_supported = komeda_plane_format_mod_supported,
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
index 41cc3e080dc9..4872253a2449 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
@@ -143,12 +143,17 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
struct komeda_wb_connector *kwb_conn;
struct drm_writeback_connector *wb_conn;
struct drm_display_info *info;
+ struct drm_encoder *encoder;
+ struct drm_encoder *wb_encoder;
u32 *formats, n_formats = 0;
+ u32 crtc_mask;
int err;
if (!kcrtc->master->wb_layer)
return 0;
+ crtc_mask = drm_crtc_mask(&kcrtc->base);
+
kwb_conn = kzalloc_obj(*kwb_conn);
if (!kwb_conn)
return -ENOMEM;
@@ -176,6 +181,34 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
return err;
}
+ wb_encoder = &wb_conn->encoder;
+
+ /*
+ * The writeback connector is associated with a single CRTC. Make its
+ * encoder clone-compatible only with encoders that can drive that CRTC.
+ *
+ * possible_clones must contain the encoder's own bit whenever it is
+ * non-zero. Add both the encoder itself and the writeback encoder when
+ * updating the reciprocal clone relationship.
+ */
+ wb_encoder->possible_clones = drm_encoder_mask(wb_encoder);
+
+ drm_for_each_encoder(encoder, &kms->base) {
+ u32 encoder_mask;
+
+ if (encoder == wb_encoder)
+ continue;
+
+ if (!(encoder->possible_crtcs & crtc_mask))
+ continue;
+
+ encoder_mask = drm_encoder_mask(encoder);
+
+ wb_encoder->possible_clones |= encoder_mask;
+ encoder->possible_clones |= encoder_mask |
+ drm_encoder_mask(wb_encoder);
+ }
+
drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs);
info = &kwb_conn->base.base.display_info;
diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c
index 8565749a3e48..f288a9c7775d 100644
--- a/drivers/gpu/drm/arm/hdlcd_crtc.c
+++ b/drivers/gpu/drm/arm/hdlcd_crtc.c
@@ -67,7 +67,7 @@ static const struct drm_crtc_funcs hdlcd_crtc_funcs = {
.destroy = hdlcd_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = hdlcd_crtc_enable_vblank,
@@ -297,7 +297,7 @@ static const struct drm_plane_helper_funcs hdlcd_plane_helper_funcs = {
static const struct drm_plane_funcs hdlcd_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
index ebe8e1078777..157ff148efe7 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -476,17 +476,17 @@ static void malidp_crtc_destroy_state(struct drm_crtc *crtc,
kfree(mali_state);
}
-static void malidp_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *malidp_crtc_create_state(struct drm_crtc *crtc)
{
- struct malidp_crtc_state *state = kzalloc_obj(*state);
+ struct malidp_crtc_state *state;
- if (crtc->state)
- malidp_crtc_destroy_state(crtc, crtc->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ return &state->base;
}
static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -511,7 +511,7 @@ static void malidp_crtc_disable_vblank(struct drm_crtc *crtc)
static const struct drm_crtc_funcs malidp_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = malidp_crtc_reset,
+ .atomic_create_state = malidp_crtc_create_state,
.atomic_duplicate_state = malidp_crtc_duplicate_state,
.atomic_destroy_state = malidp_crtc_destroy_state,
.enable_vblank = malidp_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
index 60b3f2bb153b..d971ed6eb6eb 100644
--- a/drivers/gpu/drm/arm/malidp_planes.c
+++ b/drivers/gpu/drm/arm/malidp_planes.c
@@ -73,17 +73,17 @@
* allocate a new empty object. We just need enough space to store
* a malidp_plane_state instead of a drm_plane_state.
*/
-static void malidp_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *malidp_plane_create_state(struct drm_plane *plane)
{
- struct malidp_plane_state *state = to_malidp_plane_state(plane->state);
+ struct malidp_plane_state *state;
- if (state)
- __drm_atomic_helper_plane_destroy_state(&state->base);
- kfree(state);
- plane->state = NULL;
state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_plane_reset(plane, &state->base);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_plane_state_init(&state->base, plane);
+
+ return &state->base;
}
static struct
@@ -252,7 +252,7 @@ static bool malidp_format_mod_supported_per_plane(struct drm_plane *plane,
static const struct drm_plane_funcs malidp_de_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = malidp_plane_reset,
+ .atomic_create_state = malidp_plane_create_state,
.atomic_duplicate_state = malidp_duplicate_plane_state,
.atomic_destroy_state = malidp_destroy_plane_state,
.atomic_print_state = malidp_plane_atomic_print_state,
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 5e006212fea0..d349beaa2990 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -817,7 +817,7 @@ static void armada_drm_crtc_disable_vblank(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs armada_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.cursor_set = armada_drm_crtc_cursor_set,
.cursor_move = armada_drm_crtc_cursor_move,
.destroy = armada_drm_crtc_destroy,
diff --git a/drivers/gpu/drm/ast/ast_dp.c b/drivers/gpu/drm/ast/ast_dp.c
index 5f266a05419b..f22c0c215a21 100644
--- a/drivers/gpu/drm/ast/ast_dp.c
+++ b/drivers/gpu/drm/ast/ast_dp.c
@@ -88,8 +88,14 @@ static int ast_astdp_read_edid_block(void *data, u8 *buf, unsigned int block, si
int ret = 0;
unsigned int i;
- if (block > 0)
- return -EIO; /* extension headers not supported */
+ if (block > 1) {
+ /* ASPEED DP's EDID buffer holds blocks 0 and 1 only (256
+ * bytes). Report anything beyond that as an all-zero block
+ * rather than a read failure.
+ */
+ memset(buf, 0, len);
+ return 0;
+ }
/*
* Protect access to I/O registers from concurrent modesetting
@@ -154,20 +160,6 @@ static int ast_astdp_read_edid_block(void *data, u8 *buf, unsigned int block, si
ediddata[2] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xda);
ediddata[3] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xdb);
- if (i == 31) {
- /*
- * For 128-bytes EDID_1.3,
- * 1. Add the value of Bytes-126 to Bytes-127.
- * The Bytes-127 is Checksum. Sum of all 128bytes should
- * equal 0 (mod 256).
- * 2. Modify Bytes-126 to be 0.
- * The Bytes-126 indicates the Number of extensions to
- * follow. 0 represents noextensions.
- */
- ediddata[3] = ediddata[3] + ediddata[2];
- ediddata[2] = 0;
- }
-
memcpy(buf, ediddata, min((len - i), 4));
buf += 4;
}
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index d5ed8c5c7925..ea0ef8096aa9 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -888,17 +888,17 @@ static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
.atomic_disable = ast_crtc_helper_atomic_disable,
};
-static void ast_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *ast_crtc_create_state(struct drm_crtc *crtc)
{
- struct ast_crtc_state *ast_state = kzalloc_obj(*ast_state);
+ struct ast_crtc_state *ast_state;
- if (crtc->state)
- crtc->funcs->atomic_destroy_state(crtc, crtc->state);
+ ast_state = kzalloc_obj(*ast_state);
+ if (!ast_state)
+ return ERR_PTR(-ENOMEM);
- if (ast_state)
- __drm_atomic_helper_crtc_reset(crtc, &ast_state->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ __drm_atomic_helper_crtc_state_init(&ast_state->base, crtc);
+
+ return &ast_state->base;
}
static struct drm_crtc_state *
@@ -934,7 +934,7 @@ static void ast_crtc_atomic_destroy_state(struct drm_crtc *crtc,
}
static const struct drm_crtc_funcs ast_crtc_funcs = {
- .reset = ast_crtc_reset,
+ .atomic_create_state = ast_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 9dbac2def333..c2c4c9c3086b 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -529,20 +529,17 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
}
-static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *atmel_hlcdc_crtc_create_state(struct drm_crtc *crtc)
{
struct atmel_hlcdc_crtc_state *state;
- if (crtc->state) {
- __drm_atomic_helper_crtc_destroy_state(crtc->state);
- state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
- kfree(state);
- crtc->state = NULL;
- }
-
state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
+
+ return &state->base;
}
static struct drm_crtc_state *
@@ -599,7 +596,7 @@ static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c)
static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.set_config = drm_atomic_helper_set_config,
- .reset = atmel_hlcdc_crtc_reset,
+ .atomic_create_state = atmel_hlcdc_crtc_create_state,
.atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state,
.atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
.enable_vblank = atmel_hlcdc_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 4a57d49b4c6d..837db36cdf48 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -225,7 +225,6 @@ config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW
tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw"
depends on OF
select DRM_KMS_HELPER
- select DRM_PANEL
help
This is a driver for the display bridges of
GE B850v3 that convert dual channel LVDS
@@ -291,7 +290,7 @@ config DRM_PARADE_PS8640
select DRM_DISPLAY_DP_AUX_BUS
select DRM_KMS_HELPER
select DRM_MIPI_DSI
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
Choose this option if you have PS8640 for display
The PS8640 is a high-performance and low-power
@@ -463,7 +462,7 @@ config DRM_TI_SN65DSI83
depends on OF
select DRM_KMS_HELPER
select REGMAP_I2C
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
help
Texas Instruments SN65DSI83 and SN65DSI84 DSI to LVDS Bridge driver
@@ -476,10 +475,10 @@ config DRM_TI_SN65DSI86
select DRM_BRIDGE_CONNECTOR
select DRM_KMS_HELPER
select REGMAP_I2C
- select DRM_PANEL
select DRM_MIPI_DSI
select AUXILIARY_BUS
select DRM_DISPLAY_DP_AUX_BUS
+ select DRM_PANEL_BRIDGE
help
Texas Instruments SN65DSI86 DSI to eDP Bridge driver
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 30c0c0b41d5a..566f1e5eb8cd 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1414,10 +1414,8 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
analogix_dp_hardirq,
analogix_dp_irq_thread,
irq_flags, "analogix-dp", dp);
- if (ret) {
- dev_err(&pdev->dev, "failed to request irq\n");
+ if (ret)
return ERR_PTR(ret);
- }
dp->aux.name = "DP-AUX";
dp->aux.transfer = analogix_dpaux_transfer;
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
index 504a3186ebb3..f47717bd1079 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -2343,7 +2343,6 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
cdns_mhdp_irq_handler, IRQF_ONESHOT,
"mhdp8546", mhdp);
if (ret) {
- dev_err(dev, "cannot install IRQ %d\n", irq);
ret = -EIO;
goto plat_fini;
}
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index 0f6173c61502..4ec50fb63aba 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -531,7 +531,7 @@ static int chipone_dsi_attach(struct chipone *icn)
dsi->hs_rate = 500000000;
dsi->lp_rate = 16000000;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0)
dev_err(icn->dev, "failed to attach dsi\n");
@@ -763,11 +763,6 @@ static int chipone_i2c_probe(struct i2c_client *client)
return chipone_dsi_host_attach(icn);
}
-static void chipone_dsi_remove(struct mipi_dsi_device *dsi)
-{
- mipi_dsi_detach(dsi);
-}
-
static const struct of_device_id chipone_of_match[] = {
{ .compatible = "chipone,icn6211", },
{ /* sentinel */ }
@@ -776,7 +771,6 @@ MODULE_DEVICE_TABLE(of, chipone_of_match);
static struct mipi_dsi_driver chipone_dsi_driver = {
.probe = chipone_dsi_probe,
- .remove = chipone_dsi_remove,
.driver = {
.name = "chipone-icn6211",
.of_match_table = chipone_of_match,
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 025a19c1e530..e136ac71edbb 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -3607,10 +3607,8 @@ static int it6505_i2c_probe(struct i2c_client *client)
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
IRQF_NO_AUTOEN,
"it6505-intp", it6505);
- if (err) {
- dev_err(dev, "Failed to request INTP threaded IRQ: %d", err);
+ if (err)
return err;
- }
INIT_WORK(&it6505->link_works, it6505_link_training_work);
INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index fb34f661ee0a..3ebd8992cd94 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -1156,10 +1156,8 @@ static int lt9611_probe(struct i2c_client *client)
ret = devm_request_threaded_irq(dev, client->irq, NULL,
lt9611_irq_thread_handler,
IRQF_ONESHOT, "lt9611", lt9611);
- if (ret) {
- dev_err(dev, "failed to request irq\n");
+ if (ret)
goto err_disable_regulators;
- }
i2c_set_clientdata(client, lt9611);
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index e2fc69fc51b6..5457a7b02a33 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -2184,10 +2184,8 @@ int samsung_dsim_probe(struct platform_device *pdev)
samsung_dsim_irq,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
dev_name(dev), dsi);
- if (ret) {
- dev_err(dev, "failed to request dsi irq\n");
+ if (ret)
return ret;
- }
ret = samsung_dsim_parse_dt(dsi);
if (ret)
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index b452748ad9db..88f9cd9ba6db 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -2320,8 +2320,7 @@ static int sii8620_probe(struct i2c_client *client)
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"sii8620", ctx);
if (ret < 0)
- return dev_err_probe(dev, ret,
- "failed to install IRQ handler\n");
+ return ret;
ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->gpio_reset))
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 8b8529e1723c..86a20a0b9c01 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -2079,10 +2079,8 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
ret = devm_request_threaded_irq(dev, dp->irq, NULL, dw_dp_irq,
IRQF_ONESHOT, dev_name(dev), dp);
- if (ret) {
- dev_err_probe(dev, ret, "failed to request irq\n");
+ if (ret)
goto unregister_aux;
- }
return dp;
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 34c5f583e910..937d61c42b58 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3353,7 +3353,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->channels = 2;
hdmi->disabled = true;
hdmi->rxsense = true;
- hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+ hdmi->phy_mask = ~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE) & 0xff;
hdmi->mc_clkdis = 0x7f;
hdmi->last_connector_result = connector_status_disconnected;
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index e3a57f8228da..ac83af7902cd 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -2530,10 +2530,8 @@ static int tc_probe(struct i2c_client *client)
NULL, tc_irq_handler,
IRQF_ONESHOT,
"tc358767-irq", tc);
- if (ret) {
- dev_err(dev, "failed to register dp interrupt\n");
+ if (ret)
return ret;
- }
tc->have_irq = true;
}
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index f9fdbf48c6b3..41c217074cc3 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -825,48 +825,35 @@ static int sn65dsi83_select_lvds_vod_swing(struct device *dev,
static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel)
{
struct device *dev = ctx->dev;
- struct device_node *endpoint;
- int endpoint_reg;
+ int endpoint_reg = (channel == CHANNEL_A) ? 2 : 3;
+ struct device_node *endpoint __free(device_node) =
+ of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1);
/* Set so the property can be freely selected if not defined */
u32 lvds_vod_swing_data[2] = { 0, 1000000 };
u32 lvds_vod_swing_clk[2] = { 0, 1000000 };
/* Set default near end terminataion to 200 Ohm */
u32 lvds_term = 200;
int lvds_vod_swing_conf;
- int ret = 0;
int ret_data;
int ret_clock;
- if (channel == CHANNEL_A)
- endpoint_reg = 2;
- else
- endpoint_reg = 3;
-
- endpoint = of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1);
-
of_property_read_u32(endpoint, "ti,lvds-termination-ohms", &lvds_term);
if (lvds_term == 100)
ctx->lvds_term_conf[channel] = OHM_100;
else if (lvds_term == 200)
ctx->lvds_term_conf[channel] = OHM_200;
- else {
- ret = -EINVAL;
- goto exit;
- }
+ else
+ return -EINVAL;
ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt",
lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data));
- if (ret_data != 0 && ret_data != -EINVAL) {
- ret = ret_data;
- goto exit;
- }
+ if (ret_data != 0 && ret_data != -EINVAL)
+ return ret_data;
ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt",
lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk));
- if (ret_clock != 0 && ret_clock != -EINVAL) {
- ret = ret_clock;
- goto exit;
- }
+ if (ret_clock != 0 && ret_clock != -EINVAL)
+ return ret_clock;
/* Use default value if both properties are NOT defined. */
if (ret_data == -EINVAL && ret_clock == -EINVAL)
@@ -876,17 +863,13 @@ static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel)
if (!ret_data || !ret_clock) {
lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data,
lvds_vod_swing_clk, ctx->lvds_term_conf[channel]);
- if (lvds_vod_swing_conf < 0) {
- ret = lvds_vod_swing_conf;
- goto exit;
- }
+ if (lvds_vod_swing_conf < 0)
+ return lvds_vod_swing_conf;
}
ctx->lvds_vod_swing_conf[channel] = lvds_vod_swing_conf;
- ret = 0;
-exit:
- of_node_put(endpoint);
- return ret;
+
+ return 0;
}
static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
@@ -1040,7 +1023,7 @@ static int sn65dsi83_probe(struct i2c_client *client)
ret = devm_request_threaded_irq(ctx->dev, ctx->irq, NULL, sn65dsi83_irq,
IRQF_ONESHOT, dev_name(ctx->dev), ctx);
if (ret)
- return dev_err_probe(dev, ret, "failed to request irq\n");
+ return ret;
}
dev_set_drvdata(dev, ctx);
diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c
index 99062609d9a6..4654091fe11a 100644
--- a/drivers/gpu/drm/clients/drm_log.c
+++ b/drivers/gpu/drm/clients/drm_log.c
@@ -89,25 +89,21 @@ static void drm_log_blit(struct iosys_map *dst, unsigned int dst_pitch,
}
}
-static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line)
+static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line,
+ struct iosys_map map)
{
struct drm_framebuffer *fb = scanout->buffer->fb;
unsigned long height = scanout->scaled_font_h;
- struct iosys_map map;
struct drm_rect r = DRM_RECT_INIT(0, line * height, fb->width, height);
- if (drm_client_buffer_vmap_local(scanout->buffer, &map))
- return;
iosys_map_memset(&map, r.y1 * fb->pitches[0], 0, height * fb->pitches[0]);
- drm_client_buffer_vunmap_local(scanout->buffer);
- drm_client_buffer_flush(scanout->buffer, &r);
}
static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
- unsigned int len, unsigned int prefix_len)
+ unsigned int len, unsigned int prefix_len,
+ struct iosys_map map)
{
struct drm_framebuffer *fb = scanout->buffer->fb;
- struct iosys_map map;
const struct font_desc *font = scanout->font;
size_t font_pitch = DIV_ROUND_UP(font->width, 8);
const u8 *src;
@@ -116,10 +112,9 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
fb->width, (scanout->line + 1) * scanout->scaled_font_h);
u32 i;
- if (drm_client_buffer_vmap_local(scanout->buffer, &map))
- return;
iosys_map_incr(&map, r.y1 * fb->pitches[0]);
+
for (i = 0; i < len && i < scanout->columns; i++) {
u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color;
src = font_data_glyph_buf(font->data, font->width, font->height,
@@ -134,21 +129,40 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
scanout->line++;
if (scanout->line >= scanout->rows)
scanout->line = 0;
- drm_client_buffer_vunmap_local(scanout->buffer);
- drm_client_buffer_flush(scanout->buffer, &r);
}
static void drm_log_draw_new_line(struct drm_log_scanout *scanout,
- const char *s, unsigned int len, unsigned int prefix_len)
+ const char *s, unsigned int len,
+ unsigned int prefix_len)
{
+ struct iosys_map map;
+ struct drm_framebuffer *fb = scanout->buffer->fb;
+ u32 height = scanout->scaled_font_h;
+ u32 line = scanout->line;
+ u32 y2;
+ struct drm_rect dirty;
+
+ if (drm_client_buffer_vmap_local(scanout->buffer, &map))
+ return;
+
if (scanout->line == 0) {
- drm_log_clear_line(scanout, 0);
- drm_log_clear_line(scanout, 1);
- drm_log_clear_line(scanout, 2);
- } else if (scanout->line + 2 < scanout->rows)
- drm_log_clear_line(scanout, scanout->line + 2);
+ drm_log_clear_line(scanout, 0, map);
+ drm_log_clear_line(scanout, 1, map);
+ drm_log_clear_line(scanout, 2, map);
+ y2 = min(3, scanout->rows) * height;
+ } else if (scanout->line + 2 < scanout->rows) {
+ drm_log_clear_line(scanout, scanout->line + 2, map);
+ y2 = (line + 3) * height;
+ } else {
+ y2 = (line + 1) * height;
+ }
+
+ drm_log_draw_line(scanout, s, len, prefix_len, map);
+
+ drm_client_buffer_vunmap_local(scanout->buffer);
- drm_log_draw_line(scanout, s, len, prefix_len);
+ dirty = DRM_RECT_INIT(0, line * height, fb->width, y2 - line * height);
+ drm_client_buffer_flush(scanout->buffer, &dirty);
}
/*
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 8b54069fa53a..00feb73e63d7 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -263,6 +263,9 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
if (bridge->funcs->debugfs_init)
bridge->funcs->debugfs_init(bridge, root);
}
+
+ if (bridge_connector->bridge_hdmi)
+ drm_hdmi_connector_debugfs_init(connector, root);
}
static struct drm_connector_state *
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index d6669e36ca1b..bccc5d7c8eed 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
+#include <linux/debugfs.h>
#include <linux/export.h>
#include <drm/drm_atomic.h>
@@ -12,6 +13,7 @@
#include <drm/display/drm_hdmi_cec_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
+#include <drm/display/drm_scdc_helper.h>
/**
* DOC: hdmi helpers
@@ -965,6 +967,160 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_hdmi_connector_mode_valid);
+static ssize_t
+audio_infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct drm_connector_hdmi_infoframe *infoframe;
+ struct drm_connector *connector;
+ union hdmi_infoframe *frame;
+ u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
+ ssize_t len = 0;
+
+ connector = filp->private_data;
+ mutex_lock(&connector->hdmi.infoframes.lock);
+
+ infoframe = &connector->hdmi.infoframes.audio;
+ if (!infoframe->set)
+ goto out;
+
+ frame = &infoframe->data;
+ len = hdmi_infoframe_pack(frame, buf, sizeof(buf));
+ if (len < 0)
+ goto out;
+
+ len = simple_read_from_buffer(ubuf, count, ppos, buf, len);
+
+out:
+ mutex_unlock(&connector->hdmi.infoframes.lock);
+ return len;
+}
+
+static const struct file_operations audio_infoframe_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = audio_infoframe_read,
+};
+
+static int create_hdmi_audio_infoframe_file(struct drm_connector *connector,
+ struct dentry *parent)
+{
+ struct dentry *file;
+
+ if (!connector->hdmi.funcs ||
+ !connector->hdmi.funcs->audio.write_infoframe)
+ return 0;
+
+ file = debugfs_create_file("audio", 0400, parent, connector, &audio_infoframe_fops);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ return 0;
+}
+
+#define DEFINE_INFOFRAME_FILE(_f) \
+static ssize_t _f##_read_infoframe(struct file *filp, \
+ char __user *ubuf, \
+ size_t count, \
+ loff_t *ppos) \
+{ \
+ struct drm_connector_hdmi_infoframe *infoframe; \
+ struct drm_connector_state *conn_state; \
+ struct drm_connector *connector; \
+ union hdmi_infoframe *frame; \
+ struct drm_device *dev; \
+ u8 buf[HDMI_INFOFRAME_SIZE(MAX)]; \
+ ssize_t len = 0; \
+ \
+ connector = filp->private_data; \
+ dev = connector->dev; \
+ \
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); \
+ \
+ conn_state = connector->state; \
+ infoframe = &conn_state->hdmi.infoframes._f; \
+ if (!infoframe->set) \
+ goto out; \
+ \
+ frame = &infoframe->data; \
+ len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); \
+ if (len < 0) \
+ goto out; \
+ \
+ len = simple_read_from_buffer(ubuf, count, ppos, buf, len); \
+ \
+out: \
+ drm_modeset_unlock(&dev->mode_config.connection_mutex); \
+ return len; \
+} \
+\
+static const struct file_operations _f##_infoframe_fops = { \
+ .owner = THIS_MODULE, \
+ .open = simple_open, \
+ .read = _f##_read_infoframe, \
+}; \
+\
+static int create_hdmi_## _f ## _infoframe_file(struct drm_connector *connector, \
+ struct dentry *parent) \
+{ \
+ struct dentry *file; \
+ \
+ if (!connector->hdmi.funcs || \
+ !connector->hdmi.funcs->_f.write_infoframe) \
+ return 0; \
+ file = debugfs_create_file(#_f, 0400, parent, connector, &_f ## _infoframe_fops); \
+ if (IS_ERR(file)) \
+ return PTR_ERR(file); \
+ \
+ return 0; \
+}
+
+DEFINE_INFOFRAME_FILE(avi);
+DEFINE_INFOFRAME_FILE(hdmi);
+DEFINE_INFOFRAME_FILE(hdr_drm);
+DEFINE_INFOFRAME_FILE(spd);
+
+static int create_hdmi_infoframe_files(struct drm_connector *connector,
+ struct dentry *parent)
+{
+ int ret;
+
+ ret = create_hdmi_audio_infoframe_file(connector, parent);
+ if (ret)
+ return ret;
+
+ ret = create_hdmi_avi_infoframe_file(connector, parent);
+ if (ret)
+ return ret;
+
+ ret = create_hdmi_hdmi_infoframe_file(connector, parent);
+ if (ret)
+ return ret;
+
+ ret = create_hdmi_hdr_drm_infoframe_file(connector, parent);
+ if (ret)
+ return ret;
+
+ ret = create_hdmi_spd_infoframe_file(connector, parent);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void drm_hdmi_connector_debugfs_init(struct drm_connector *connector,
+ struct dentry *root)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("infoframes", root);
+ if (IS_ERR(dir))
+ return;
+
+ create_hdmi_infoframe_files(connector, dir);
+ drm_scdc_debugfs_init(connector, root);
+}
+EXPORT_SYMBOL(drm_hdmi_connector_debugfs_init);
+
static int clear_infoframe(struct drm_connector *connector,
const struct drm_connector_infoframe_funcs *funcs,
const char *type)
diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c
index df878aad4a36..d00da20862fc 100644
--- a/drivers/gpu/drm/display/drm_scdc_helper.c
+++ b/drivers/gpu/drm/display/drm_scdc_helper.c
@@ -21,10 +21,13 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/bitfield.h>
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/overflow.h>
#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_connector.h>
@@ -55,6 +58,38 @@
#define SCDC_I2C_SLAVE_ADDRESS 0x54
+static const char *drm_scdc_frl_rate_str(enum drm_scdc_frl_rate rate)
+{
+ switch (rate) {
+ case SCDC_FRL_RATE_OFF:
+ return "Off";
+ case SCDC_FRL_RATE_3X3:
+ return "3 Gbit/s x 3 lanes";
+ case SCDC_FRL_RATE_6X3:
+ return "6 Gbit/s x 3 lanes";
+ case SCDC_FRL_RATE_6X4:
+ return "6 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_8X4:
+ return "8 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_10X4:
+ return "10 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_12X4:
+ return "12 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_RESV_7:
+ case SCDC_FRL_RATE_RESV_8:
+ case SCDC_FRL_RATE_RESV_9:
+ case SCDC_FRL_RATE_RESV_10:
+ case SCDC_FRL_RATE_RESV_11:
+ case SCDC_FRL_RATE_RESV_12:
+ case SCDC_FRL_RATE_RESV_13:
+ case SCDC_FRL_RATE_RESV_14:
+ case SCDC_FRL_RATE_RESV_15:
+ return "(Reserved)";
+ default:
+ return NULL;
+ }
+}
+
/**
* drm_scdc_read - read a block of data from SCDC
* @adapter: I2C controller
@@ -67,8 +102,8 @@
* Returns:
* 0 on success, negative error code on failure.
*/
-ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
- size_t size)
+int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
+ size_t size)
{
int ret;
struct i2c_msg msgs[2] = {
@@ -107,8 +142,8 @@ EXPORT_SYMBOL(drm_scdc_read);
* Returns:
* 0 on success, negative error code on failure.
*/
-ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
- const void *buffer, size_t size)
+int drm_scdc_write(struct i2c_adapter *adapter, u8 offset, const void *buffer,
+ size_t size)
{
struct i2c_msg msg = {
.addr = SCDC_I2C_SLAVE_ADDRESS,
@@ -276,3 +311,249 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector,
return true;
}
EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);
+
+static void
+drm_scdc_parse_status0_flags(u8 val, struct drm_scdc_status_flags *flags)
+{
+ flags->clock_detected = val & SCDC_CLOCK_DETECT;
+ flags->ch0_locked = val & SCDC_CH0_LOCK;
+ flags->ch1_locked = val & SCDC_CH1_LOCK;
+ flags->ch2_locked = val & SCDC_CH2_LOCK;
+ flags->ln3_locked = val & SCDC_LN3_LOCK;
+ flags->flt_ready = val & SCDC_FLT_READY;
+ flags->dsc_fail = val & SCDC_DSC_FAIL;
+}
+
+static void
+drm_scdc_parse_status1_2_flags(u8 val_flag1, u8 val_flag2,
+ struct drm_scdc_status_flags *flags)
+{
+ flags->ln0_training_pattern = FIELD_GET(SCDC_LN_EVEN_TRAIN_PTRN, val_flag1);
+ flags->ln1_training_pattern = FIELD_GET(SCDC_LN_ODD_TRAIN_PTRN, val_flag1);
+
+ flags->ln2_training_pattern = FIELD_GET(SCDC_LN_EVEN_TRAIN_PTRN, val_flag2);
+ flags->ln3_training_pattern = FIELD_GET(SCDC_LN_ODD_TRAIN_PTRN, val_flag2);
+}
+
+static int drm_scdc_parse_error_counters(const u8 scdc[256], u16 counter[4],
+ unsigned int num_lanes)
+{
+ u8 end_reg;
+ u8 sum = 0;
+ int i;
+
+ switch (num_lanes) {
+ case 3:
+ end_reg = SCDC_ERR_DET_CHECKSUM;
+ break;
+ case 4:
+ end_reg = SCDC_ERR_DET_3_H;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = SCDC_ERR_DET_0_L; i <= end_reg; i++)
+ sum = wrapping_add(u8, sum, scdc[i]);
+
+ if (sum)
+ return -EPROTO;
+
+ for (i = 0; i < 3; i++) {
+ if (scdc[SCDC_ERR_DET_0_H + i * 2] & SCDC_CHANNEL_VALID)
+ counter[i] = (scdc[SCDC_ERR_DET_0_H + i * 2] &
+ ~SCDC_CHANNEL_VALID) << 8 |
+ scdc[SCDC_ERR_DET_0_L + i * 2];
+ else
+ counter[i] = 0;
+ }
+
+ if (num_lanes == 4 && scdc[SCDC_ERR_DET_3_H] & SCDC_CHANNEL_VALID)
+ counter[3] = (scdc[SCDC_ERR_DET_3_H] & ~SCDC_CHANNEL_VALID) << 8 |
+ scdc[SCDC_ERR_DET_3_L];
+ else
+ counter[3] = 0;
+
+ return 0;
+}
+
+/**
+ * drm_scdc_read_state - Update state from SCDC
+ * @connector: pointer to a &struct drm_connector on which to operate on
+ * @state: pointer to a &struct drm_scdc_state to fill
+ *
+ * Reads the entire 256 byte SCDC state and parses it.
+ *
+ * Returns: %0 on success, negative errno on failure.
+ */
+int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *state)
+{
+ struct i2c_adapter *ddc;
+ struct drm_scdc *scdc;
+ u8 *buf = state->scdc;
+ int num_lanes;
+ int ret;
+
+ if (!state || !connector)
+ return -ENODEV;
+
+ scdc = &connector->display_info.hdmi.scdc;
+ ddc = connector->ddc;
+
+ if (!scdc->supported || !ddc)
+ return -EOPNOTSUPP;
+
+ /* Read in 128-byte chunks, to work around DP<->HDMI converters with issues. */
+ ret = drm_scdc_read(ddc, 0, buf, 128);
+ if (ret)
+ return ret;
+
+ ret = drm_scdc_read(ddc, 128, &buf[128], 128);
+ if (ret)
+ return ret;
+
+ state->scrambling_enabled = buf[SCDC_TMDS_CONFIG] & SCDC_SCRAMBLING_ENABLE;
+ state->tmds_bclk_x40 = buf[SCDC_TMDS_CONFIG] & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
+
+ state->scrambling_detected = buf[SCDC_SCRAMBLER_STATUS] & SCDC_SCRAMBLING_STATUS;
+
+ state->rate = FIELD_GET(SCDC_FRL_RATE, buf[SCDC_CONFIG_1]);
+ num_lanes = drm_scdc_num_frl_lanes(state->rate);
+ if (num_lanes < 0)
+ return num_lanes;
+ if (!num_lanes)
+ num_lanes = 3;
+
+ state->ffe_levels = FIELD_GET(SCDC_FFE_LEVELS, buf[SCDC_CONFIG_1]);
+
+ drm_scdc_parse_status0_flags(buf[SCDC_STATUS_FLAGS_0], &state->stf);
+ drm_scdc_parse_status1_2_flags(buf[SCDC_STATUS_FLAGS_1],
+ buf[SCDC_STATUS_FLAGS_2], &state->stf);
+ ret = drm_scdc_parse_error_counters(buf, state->error_count, num_lanes);
+ if (ret)
+ return ret;
+
+ if (state->rate && (buf[SCDC_ERR_DET_RS_H] & SCDC_CHANNEL_VALID))
+ state->rs_corrections = (buf[SCDC_ERR_DET_RS_H] & ~SCDC_CHANNEL_VALID) << 8 |
+ buf[SCDC_ERR_DET_RS_L];
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_scdc_read_state);
+
+#define scdc_print_str(_f, key, s) \
+ (seq_printf((_f), "%-30s: %s\n", (key), (s)))
+#define scdc_print_flag(_f, key, val) \
+ (scdc_print_str((_f), (key), str_yes_no((val))))
+#define scdc_print_dec(_f, key, val) \
+ (seq_printf((_f), "%-30s: %d\n", (key), (val)))
+
+static int scdc_status_show(struct seq_file *m, void *data)
+{
+ struct drm_connector *connector = m->private;
+ struct drm_scdc *scdc = &connector->display_info.hdmi.scdc;
+ struct drm_scdc_state *st;
+ int i, ret, frl_lanes;
+
+ drm_connector_get(connector);
+
+ ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex);
+ if (ret)
+ goto err_conn_put;
+
+ if (connector->status != connector_status_connected) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ if (scdc->supported) {
+ st = kzalloc_obj(*st);
+ if (!st) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+ ret = drm_scdc_read_state(connector, st);
+ if (ret)
+ goto err_free_state;
+
+ for (i = 0; i < ARRAY_SIZE(st->scdc); i += 16)
+ seq_printf(m, "%*ph\n", 16, &st->scdc[i]);
+
+ seq_puts(m, "\n----------------\n\n");
+ }
+
+ scdc_print_flag(m, "SCDC Supported", scdc->supported);
+ if (!scdc->supported) {
+ ret = 0;
+ goto err_unlock;
+ }
+
+ scdc_print_flag(m, "Sink Read Request Capable", scdc->read_request);
+ scdc_print_flag(m, "Scrambling Supported", scdc->scrambling.supported);
+ scdc_print_flag(m, "Low Rate Scrambling Supported", scdc->scrambling.low_rates);
+
+ mutex_unlock(&connector->dev->mode_config.mutex);
+
+ drm_connector_put(connector);
+
+ frl_lanes = drm_scdc_num_frl_lanes(st->rate);
+
+ scdc_print_flag(m, "Scrambling Enabled", st->scrambling_enabled);
+ scdc_print_flag(m, "Scrambling Detected", st->scrambling_detected);
+ scdc_print_str(m, "FRL Rate", drm_scdc_frl_rate_str(st->rate));
+ scdc_print_dec(m, "FFE Levels", st->ffe_levels);
+
+ if (st->tmds_bclk_x40)
+ scdc_print_str(m, "TMDS Bit Clock Ratio", "1/40");
+ else
+ scdc_print_str(m, "TMDS Bit Clock Ratio", "1/10");
+
+ scdc_print_flag(m, "Clock Detected", st->stf.clock_detected);
+ scdc_print_flag(m, "Channel 0 Locked", st->stf.ch0_locked);
+ scdc_print_flag(m, "Channel 1 Locked", st->stf.ch1_locked);
+ scdc_print_flag(m, "Channel 2 Locked", st->stf.ch2_locked);
+ if (frl_lanes == 4)
+ scdc_print_flag(m, "Lane 3 Locked", st->stf.ln3_locked);
+
+ scdc_print_flag(m, "Sink Ready For Link Training", st->stf.flt_ready);
+ scdc_print_flag(m, "Sink Failed To Decode DSC", st->stf.dsc_fail);
+
+ scdc_print_dec(m, "Channel 0 Errors", st->error_count[0]);
+ scdc_print_dec(m, "Channel 1 Errors", st->error_count[1]);
+ scdc_print_dec(m, "Channel 2 Errors", st->error_count[2]);
+ if (frl_lanes == 4)
+ scdc_print_dec(m, "Lane 3 Errors", st->error_count[3]);
+ if (frl_lanes > 0)
+ scdc_print_dec(m, "Reed-Solomon Corrections", st->rs_corrections);
+
+ kfree(st);
+
+ return 0;
+
+err_free_state:
+ kfree(st);
+err_unlock:
+ mutex_unlock(&connector->dev->mode_config.mutex);
+err_conn_put:
+ drm_connector_put(connector);
+
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(scdc_status);
+
+/**
+ * drm_scdc_debugfs_init - Initialize scdc files in connector debugfs
+ * @connector: pointer to &struct drm_connector to operate on
+ * @root: debugfs &struct dentry for the debugfs root of @connector
+ *
+ * Creates SCDC-related debugfs files for @connector. Must be called after
+ * @root is already created.
+ */
+void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root)
+{
+ if (!root || !connector)
+ return;
+
+ debugfs_create_file("scdc_status", 0444, root, connector, &scdc_status_fops);
+}
+EXPORT_SYMBOL(drm_scdc_debugfs_init);
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index e5c8ef06caed..45df6376f408 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -925,6 +925,10 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p,
drm_get_colorop_lut3d_interpolation_name(state->lut3d_interpolation));
drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0);
break;
+ case DRM_COLOROP_FIXED_MATRIX:
+ drm_printf_indent(p, 1, "fixed_matrix_type=%s\n",
+ drm_get_colorop_fixed_matrix_type_name(state->fixed_matrix_type));
+ break;
default:
break;
}
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 5ea593b3a98e..4acfb588c412 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -786,6 +786,11 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop,
return drm_atomic_color_set_data_property(colorop, state,
property, val,
replaced);
+ } else if (property == colorop->fixed_matrix_type_property) {
+ if (state->fixed_matrix_type != val) {
+ state->fixed_matrix_type = val;
+ *replaced = true;
+ }
} else {
drm_dbg_atomic(colorop->dev,
"[COLOROP:%d:%d] unknown property [PROP:%d:%s]\n",
@@ -818,6 +823,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop,
*val = state->lut3d_interpolation;
else if (property == colorop->data_property)
*val = (state->data) ? state->data->base.id : 0;
+ else if (property == colorop->fixed_matrix_type_property)
+ *val = state->fixed_matrix_type;
else
return -EINVAL;
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
index 1536e59c6fe7..02e0460baa7c 100644
--- a/drivers/gpu/drm/drm_buddy.c
+++ b/drivers/gpu/drm/drm_buddy.c
@@ -44,7 +44,8 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p)
gpu_buddy_driver_lock_held(mm);
drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n",
- mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
+ mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20,
+ gpu_buddy_clear_avail(mm) >> 20);
for (order = mm->max_order; order >= 0; order--) {
u64 free_count = mm->free_scoreboard[order];
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 682fcc651525..95da03d6c65c 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -68,6 +68,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
{ DRM_COLOROP_CTM_3X4, "3x4 Matrix"},
{ DRM_COLOROP_MULTIPLIER, "Multiplier"},
{ DRM_COLOROP_3D_LUT, "3D LUT"},
+ { DRM_COLOROP_FIXED_MATRIX, "Fixed Matrix"},
};
static const char * const colorop_curve_1d_type_names[] = {
@@ -90,6 +91,15 @@ static const struct drm_prop_enum_list drm_colorop_lut3d_interpolation_list[] =
{ DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, "Tetrahedral" },
};
+static const char * const colorop_fixed_matrix_type_names[] = {
+ [DRM_COLOROP_FM_YCBCR601_FULL_RGB] = "YCbCr 601 Full to RGB",
+ [DRM_COLOROP_FM_YCBCR601_LIMITED_RGB] = "YCbCr 601 Limited to RGB",
+ [DRM_COLOROP_FM_YCBCR709_FULL_RGB] = "YCbCr 709 Full to RGB",
+ [DRM_COLOROP_FM_YCBCR709_LIMITED_RGB] = "YCbCr 709 Limited to RGB",
+ [DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB] = "YCbCr 2020 NC Full to RGB",
+ [DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB] = "YCbCr 2020 NC Limited to RGB",
+};
+
/* Init Helpers */
static int drm_plane_colorop_init(struct drm_device *dev, struct drm_colorop *colorop,
@@ -453,6 +463,80 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col
}
EXPORT_SYMBOL(drm_plane_colorop_3dlut_init);
+/**
+ * drm_plane_colorop_fixed_matrix_init - Initialize a DRM_COLOROP_FIXED_MATRIX
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @funcs: control functions for the new colorop
+ * @supported_fm: A bitfield of supported drm_colorop_fixed_matrix_type enum values,
+ * created using BIT(fixed_matrix_type) and combined with the OR '|'
+ * operator.
+ * @flags: bitmask of misc, see DRM_COLOROP_FLAG_* defines.
+ * @return zero on success, -E value on failure
+ */
+int drm_plane_colorop_fixed_matrix_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane,
+ const struct drm_colorop_funcs *funcs,
+ u64 supported_fm, uint32_t flags)
+{
+ struct drm_prop_enum_list enum_list[DRM_COLOROP_FM_COUNT];
+ int i, len;
+ struct drm_property *prop;
+ int ret;
+
+ if (!supported_fm) {
+ drm_err(dev,
+ "No supported FM type op for new Fixed Matrix colorop on [PLANE:%d:%s]\n",
+ plane->base.id, plane->name);
+ return -EINVAL;
+ }
+
+ if ((supported_fm & -BIT(DRM_COLOROP_FM_COUNT)) != 0) {
+ drm_err(dev, "Unknown Fixed Matrix provided on [PLANE:%d:%s]\n",
+ plane->base.id, plane->name);
+ return -EINVAL;
+ }
+
+ ret = drm_plane_colorop_init(dev, colorop, plane, funcs, DRM_COLOROP_FIXED_MATRIX, flags);
+ if (ret)
+ return ret;
+
+ len = 0;
+ for (i = 0; i < DRM_COLOROP_FM_COUNT; i++) {
+ if ((supported_fm & BIT(i)) == 0)
+ continue;
+
+ enum_list[len].type = i;
+ enum_list[len].name = colorop_fixed_matrix_type_names[i];
+ len++;
+ }
+
+ if (WARN_ON(len <= 0))
+ return -EINVAL;
+
+ prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, "FIXED_MATRIX_TYPE",
+ enum_list, len);
+
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->fixed_matrix_type_property = prop;
+ /*
+ * Default to the first supported CSC mode as provided by the driver.
+ * Intuitively this should be something that keeps the colorop in pixel bypass
+ * mode but that is already handled via the standard colorop bypass
+ * property.
+ */
+ drm_object_attach_property(&colorop->base, colorop->fixed_matrix_type_property,
+ enum_list[0].type);
+ drm_colorop_reset(colorop);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_colorop_fixed_matrix_init);
+
static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop,
struct drm_colorop_state *state)
{
@@ -533,6 +617,13 @@ static void __drm_colorop_state_init(struct drm_colorop_state *colorop_state,
&val))
colorop_state->lut3d_interpolation = val;
}
+
+ if (colorop->fixed_matrix_type_property) {
+ if (!drm_object_property_get_default_value(&colorop->base,
+ colorop->fixed_matrix_type_property,
+ &val))
+ colorop_state->fixed_matrix_type = val;
+ }
}
/**
@@ -596,6 +687,7 @@ static const char * const colorop_type_name[] = {
[DRM_COLOROP_CTM_3X4] = "3x4 Matrix",
[DRM_COLOROP_MULTIPLIER] = "Multiplier",
[DRM_COLOROP_3D_LUT] = "3D LUT",
+ [DRM_COLOROP_FIXED_MATRIX] = "Fixed Matrix",
};
static const char * const colorop_lu3d_interpolation_name[] = {
@@ -653,6 +745,21 @@ const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_inte
}
/**
+ * drm_get_colorop_fixed_matrix_type_name: return a string for fixed matrix type
+ * @type: fixed matrix type to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
+const char *drm_get_colorop_fixed_matrix_type_name(enum drm_colorop_fixed_matrix_type type)
+{
+ if (WARN_ON(type >= ARRAY_SIZE(colorop_fixed_matrix_type_names)))
+ return "unknown";
+
+ return colorop_fixed_matrix_type_names[type];
+}
+
+/**
* drm_colorop_set_next_property - sets the next pointer
* @colorop: drm colorop
* @next: next colorop
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index ae1c6126c2c5..a72f9723d3a9 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -633,161 +633,6 @@ static const struct file_operations drm_connector_fops = {
.write = connector_write
};
-static ssize_t
-audio_infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos)
-{
- struct drm_connector_hdmi_infoframe *infoframe;
- struct drm_connector *connector;
- union hdmi_infoframe *frame;
- u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
- ssize_t len = 0;
-
- connector = filp->private_data;
- mutex_lock(&connector->hdmi.infoframes.lock);
-
- infoframe = &connector->hdmi.infoframes.audio;
- if (!infoframe->set)
- goto out;
-
- frame = &infoframe->data;
- len = hdmi_infoframe_pack(frame, buf, sizeof(buf));
- if (len < 0)
- goto out;
-
- len = simple_read_from_buffer(ubuf, count, ppos, buf, len);
-
-out:
- mutex_unlock(&connector->hdmi.infoframes.lock);
- return len;
-}
-
-static const struct file_operations audio_infoframe_fops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = audio_infoframe_read,
-};
-
-static int create_hdmi_audio_infoframe_file(struct drm_connector *connector,
- struct dentry *parent)
-{
- struct dentry *file;
-
- if (!connector->hdmi.funcs ||
- !connector->hdmi.funcs->audio.write_infoframe)
- return 0;
-
- file = debugfs_create_file("audio", 0400, parent, connector, &audio_infoframe_fops);
- if (IS_ERR(file))
- return PTR_ERR(file);
-
- return 0;
-}
-
-#define DEFINE_INFOFRAME_FILE(_f) \
-static ssize_t _f##_read_infoframe(struct file *filp, \
- char __user *ubuf, \
- size_t count, \
- loff_t *ppos) \
-{ \
- struct drm_connector_hdmi_infoframe *infoframe; \
- struct drm_connector_state *conn_state; \
- struct drm_connector *connector; \
- union hdmi_infoframe *frame; \
- struct drm_device *dev; \
- u8 buf[HDMI_INFOFRAME_SIZE(MAX)]; \
- ssize_t len = 0; \
- \
- connector = filp->private_data; \
- dev = connector->dev; \
- \
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); \
- \
- conn_state = connector->state; \
- infoframe = &conn_state->hdmi.infoframes._f; \
- if (!infoframe->set) \
- goto out; \
- \
- frame = &infoframe->data; \
- len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); \
- if (len < 0) \
- goto out; \
- \
- len = simple_read_from_buffer(ubuf, count, ppos, buf, len); \
- \
-out: \
- drm_modeset_unlock(&dev->mode_config.connection_mutex); \
- return len; \
-} \
-\
-static const struct file_operations _f##_infoframe_fops = { \
- .owner = THIS_MODULE, \
- .open = simple_open, \
- .read = _f##_read_infoframe, \
-}; \
-\
-static int create_hdmi_## _f ## _infoframe_file(struct drm_connector *connector, \
- struct dentry *parent) \
-{ \
- struct dentry *file; \
- \
- if (!connector->hdmi.funcs || \
- !connector->hdmi.funcs->_f.write_infoframe) \
- return 0; \
- file = debugfs_create_file(#_f, 0400, parent, connector, &_f ## _infoframe_fops); \
- if (IS_ERR(file)) \
- return PTR_ERR(file); \
- \
- return 0; \
-}
-
-DEFINE_INFOFRAME_FILE(avi);
-DEFINE_INFOFRAME_FILE(hdmi);
-DEFINE_INFOFRAME_FILE(hdr_drm);
-DEFINE_INFOFRAME_FILE(spd);
-
-static int create_hdmi_infoframe_files(struct drm_connector *connector,
- struct dentry *parent)
-{
- int ret;
-
- ret = create_hdmi_audio_infoframe_file(connector, parent);
- if (ret)
- return ret;
-
- ret = create_hdmi_avi_infoframe_file(connector, parent);
- if (ret)
- return ret;
-
- ret = create_hdmi_hdmi_infoframe_file(connector, parent);
- if (ret)
- return ret;
-
- ret = create_hdmi_hdr_drm_infoframe_file(connector, parent);
- if (ret)
- return ret;
-
- ret = create_hdmi_spd_infoframe_file(connector, parent);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static void hdmi_debugfs_add(struct drm_connector *connector)
-{
- struct dentry *dir;
-
- if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector->connector_type == DRM_MODE_CONNECTOR_HDMIB))
- return;
-
- dir = debugfs_create_dir("infoframes", connector->debugfs_entry);
- if (IS_ERR(dir))
- return;
-
- create_hdmi_infoframe_files(connector, dir);
-}
-
void drm_debugfs_connector_add(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -815,8 +660,6 @@ void drm_debugfs_connector_add(struct drm_connector *connector)
debugfs_create_file("output_bpc", 0444, root, connector,
&output_bpc_fops);
- hdmi_debugfs_add(connector);
-
if (connector->funcs->debugfs_init)
connector->funcs->debugfs_init(connector, root);
}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index cb53baa70995..8c0879c336e5 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -36,6 +36,7 @@
#include <linux/mount.h>
#include <linux/pseudo_fs.h>
#include <linux/sched.h>
+#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/sprintf.h>
#include <linux/srcu.h>
@@ -576,27 +577,31 @@ static const char *drm_get_wedge_recovery(unsigned int opt)
int drm_dev_wedged_event(struct drm_device *dev, unsigned long method,
struct drm_wedge_task_info *info)
{
- char event_string[WEDGE_STR_LEN], pid_string[PID_STR_LEN], comm_string[COMM_STR_LEN];
- char *envp[] = { event_string, NULL, NULL, NULL };
- const char *recovery = NULL;
- unsigned int len, opt;
+ DECLARE_SEQ_BUF(event_string, WEDGE_STR_LEN);
+ char pid_string[PID_STR_LEN], comm_string[COMM_STR_LEN];
+ char *envp[4] = { };
+ unsigned int len = 0, opt;
- len = scnprintf(event_string, sizeof(event_string), "%s", "WEDGED=");
+ seq_buf_puts(&event_string, "WEDGED=");
+ envp[0] = event_string.buffer;
for_each_set_bit(opt, &method, BITS_PER_TYPE(method)) {
- recovery = drm_get_wedge_recovery(opt);
+ const char *recovery = drm_get_wedge_recovery(opt);
if (drm_WARN_ONCE(dev, !recovery, "invalid recovery method %u\n", opt))
break;
- len += scnprintf(event_string + len, sizeof(event_string) - len, "%s,", recovery);
+ if (drm_WARN_ON_ONCE(dev, seq_buf_printf(&event_string, "%s,", recovery)))
+ break;
+
+ len = seq_buf_used(&event_string);
}
- if (recovery)
- /* Get rid of trailing comma */
- event_string[len - 1] = '\0';
+ if (len)
+ /* Strip trailing comma; also discards any partial overflow entry */
+ event_string.buffer[len - 1] = '\0';
else
- /* Caller is unsure about recovery, do the best we can at this point. */
- snprintf(event_string, sizeof(event_string), "%s", "WEDGED=unknown");
+ /* No complete entry written, do the best we can at this point. */
+ snprintf(event_string.buffer, event_string.size, "%s", "WEDGED=unknown");
drm_info(dev, "device wedged, %s\n", method == DRM_WEDGE_RECOVERY_NONE ?
"but no recovery needed" : "needs recovery");
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 07970e5b5f65..9990a836d0b6 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -101,20 +101,42 @@ enum drm_edid_internal_quirk {
#define MICROSOFT_IEEE_OUI 0xca125c
#define AMD_IEEE_OUI 0x00001A
+#define AMD_VSDB_V1_PAYLOAD_LEN 8
+#define AMD_VSDB_V2_PAYLOAD_LEN 13
#define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15
-#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20
+#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 21
-struct amd_vsdb_v3_payload {
+#define AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN 21
+
+struct amd_vsdb_common_payload {
u8 oui[3];
u8 version;
+} __packed;
+
+struct amd_vsdb_v1_payload {
+ struct amd_vsdb_common_payload common;
u8 feature_caps;
- u8 rsvd0[3];
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 freesync_vcp_code;
+} __packed;
+
+struct amd_vsdb_v2_payload {
+ struct amd_vsdb_v1_payload v1;
u8 cs_eotf_support;
u8 lum1_max;
u8 lum1_min;
u8 lum2_max;
u8 lum2_min;
- u8 rsvd1[2];
+} __packed;
+
+struct amd_vsdb_v3_payload {
+ struct amd_vsdb_v2_payload v2;
+ u8 max_vfreq_ext_low;
+ /*
+ * byte 14: bits 0-1 extend MSB of max refresh rate
+ */
+ u8 max_vfreq_ext_high;
/*
* Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a
* monitor may provide a payload as short as 15 bytes. Always
@@ -123,6 +145,12 @@ struct amd_vsdb_v3_payload {
u8 extra[AMD_VSDB_V3_PAYLOAD_MAX_LEN - AMD_VSDB_V3_PAYLOAD_MIN_LEN];
} __packed;
+static const u8 amd_vsdb_min_len[] = {
+ [1] = AMD_VSDB_V1_PAYLOAD_LEN,
+ [2] = AMD_VSDB_V2_PAYLOAD_LEN,
+ [3] = AMD_VSDB_V3_PAYLOAD_MIN_LEN,
+};
+
struct detailed_mode_closure {
struct drm_connector *connector;
const struct drm_edid *drm_edid;
@@ -5231,7 +5259,7 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
static bool cea_db_is_amd_vsdb(const struct cea_db *db)
{
return cea_db_is_vendor(db, AMD_IEEE_OUI) &&
- cea_db_payload_len(db) >= AMD_VSDB_V3_PAYLOAD_MIN_LEN &&
+ cea_db_payload_len(db) >= AMD_VSDB_V1_PAYLOAD_LEN &&
cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN;
}
@@ -6182,6 +6210,33 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
hdmi->y420_dc_modes = dc_mask;
}
+static void drm_parse_hdmi_gaming_info(struct drm_hdmi_info *hdmi, const u8 *db)
+{
+ struct drm_hdmi_vrr_cap *vrr = &hdmi->vrr_cap;
+
+ if (cea_db_payload_len(db) < 8)
+ return;
+
+ hdmi->fapa_start_location = db[8] & DRM_EDID_FAPA_START_LOCATION;
+ hdmi->allm = db[8] & DRM_EDID_ALLM;
+ vrr->fva = db[8] & DRM_EDID_FVA;
+ vrr->cnmvrr = db[8] & DRM_EDID_CNMVRR;
+ vrr->cinema_vrr = db[8] & DRM_EDID_CINEMA_VRR;
+ vrr->mdelta = db[8] & DRM_EDID_MDELTA;
+
+ if (cea_db_payload_len(db) < 9)
+ return;
+
+ vrr->vrr_min = db[9] & DRM_EDID_VRR_MIN_MASK;
+ vrr->supported = (vrr->vrr_min > 0 && vrr->vrr_min <= 48);
+
+ if (cea_db_payload_len(db) < 10)
+ return;
+
+ vrr->vrr_max = (db[9] & DRM_EDID_VRR_MAX_UPPER_MASK) << 2 | db[10];
+ vrr->supported &= (vrr->vrr_max == 0 || vrr->vrr_max >= 100);
+}
+
static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc,
const u8 *hf_scds)
{
@@ -6308,6 +6363,8 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
drm_parse_ycbcr420_deep_color_info(connector, hf_scds);
+ drm_parse_hdmi_gaming_info(&connector->display_info.hdmi, hf_scds);
+
if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) {
drm_parse_dsc_info(hdmi_dsc, hf_scds);
dsc_support = true;
@@ -6317,6 +6374,19 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
"[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n",
connector->base.id, connector->name,
max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support));
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] FAPA in blanking: %s, ALLM support: %s, Fast Vactive support: %s\n",
+ connector->base.id, connector->name, str_yes_no(hdmi->fapa_start_location),
+ str_yes_no(hdmi->allm), str_yes_no(hdmi->vrr_cap.fva));
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] Negative M VRR support: %s, CinemaVRR support: %s, Mdelta: %d\n",
+ connector->base.id, connector->name, str_yes_no(hdmi->vrr_cap.cnmvrr),
+ str_yes_no(hdmi->vrr_cap.cinema_vrr), hdmi->vrr_cap.mdelta);
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] VRRmin: %u, VRRmax: %u, VRR supported: %s\n",
+ connector->base.id, connector->name, hdmi->vrr_cap.vrr_min,
+ hdmi->vrr_cap.vrr_max, str_yes_no(hdmi->vrr_cap.supported));
+
}
static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
@@ -6431,43 +6501,104 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
connector->base.id, connector->name, version, db[5]);
}
+static void drm_parse_amd_vsdb_common(struct drm_display_info *info,
+ const struct amd_vsdb_common_payload *data)
+{
+ info->amd_vsdb.version = data->version;
+}
+
+static void drm_parse_amd_vsdb_v1(struct drm_display_info *info,
+ const struct amd_vsdb_v1_payload *data)
+{
+ info->amd_vsdb.freesync_supported = data->feature_caps & 0x1;
+ info->amd_vsdb.min_frame_rate = data->min_vfreq;
+ info->amd_vsdb.max_frame_rate = data->max_vfreq;
+ info->amd_vsdb.freesync_vcp_code = data->freesync_vcp_code;
+
+ drm_parse_amd_vsdb_common(info, &data->common);
+}
+
+static void drm_parse_amd_vsdb_v2(struct drm_display_info *info,
+ const struct amd_vsdb_v2_payload *data)
+{
+ info->amd_vsdb.luminance_range1.max_luminance = data->lum1_max;
+ info->amd_vsdb.luminance_range1.min_luminance = data->lum1_min;
+ info->amd_vsdb.luminance_range2.max_luminance = data->lum2_max;
+ info->amd_vsdb.luminance_range2.min_luminance = data->lum2_min;
+
+ drm_parse_amd_vsdb_v1(info, &data->v1);
+}
+
+static void drm_parse_amd_vsdb_v3(struct drm_display_info *info,
+ const struct amd_vsdb_v3_payload *data,
+ int payload_len)
+{
+ u16 max_frame_rate;
+
+ info->amd_vsdb.replay_mode = data->v2.v1.feature_caps & 0x40;
+ info->amd_vsdb.panel_type = (data->v2.cs_eotf_support & 0xC0) >> 6;
+
+ drm_parse_amd_vsdb_v2(info, &data->v2);
+
+ /* vfreq is provided in a different set of fields for v3. */
+ max_frame_rate = data->max_vfreq_ext_low |
+ (data->max_vfreq_ext_high & 0x3) << 8;
+
+ /*
+ * The AMD VSDB v3 payload length is variable (15..21 bytes).
+ * All fields through max_vfreq_ext_high (byte 14) are always
+ * present, but data->extra[] (bytes 15+) may not be, so any access
+ * to extra[] must be guarded with a runtime length check to
+ * avoid out-of-bounds reads on shorter (but spec-valid) payloads.
+ */
+ if (payload_len >= AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN)
+ max_frame_rate |= (data->extra[5] & 0x3) << 10;
+
+ info->amd_vsdb.max_frame_rate = max_frame_rate;
+}
+
static void drm_parse_amd_vsdb(struct drm_connector *connector,
const struct cea_db *db)
{
struct drm_display_info *info = &connector->display_info;
const u8 *data = cea_db_data(db);
- const struct amd_vsdb_v3_payload *p;
+ int payload_len = cea_db_payload_len(db);
+ u8 version;
- p = (const struct amd_vsdb_v3_payload *)data;
+ version = ((const struct amd_vsdb_common_payload *)data)->version;
- if (p->version != 0x03) {
+ if (version < 1) {
drm_dbg_kms(connector->dev,
"[CONNECTOR:%d:%s] Unsupported AMD VSDB version %u\n",
- connector->base.id, connector->name, p->version);
+ connector->base.id, connector->name, version);
return;
}
- info->amd_vsdb.version = p->version;
- info->amd_vsdb.replay_mode = p->feature_caps & 0x40;
- info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6;
- info->amd_vsdb.luminance_range1.max_luminance = p->lum1_max;
- info->amd_vsdb.luminance_range1.min_luminance = p->lum1_min;
- info->amd_vsdb.luminance_range2.max_luminance = p->lum2_max;
- info->amd_vsdb.luminance_range2.min_luminance = p->lum2_min;
+ if ((version <= 3 && payload_len < amd_vsdb_min_len[version]) ||
+ (version > 3 && payload_len < AMD_VSDB_V3_PAYLOAD_MIN_LEN)) {
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] AMD VSDB v%u payload too short (%d bytes)\n",
+ connector->base.id, connector->name, version, payload_len);
+ return;
+ }
- /*
- * The AMD VSDB v3 payload length is variable (15..20 bytes).
- * All fields through p->rsvd1 (byte 14) are always present,
- * but p->extra[] (bytes 15+) may not be. Any future access to
- * extra[] must be guarded with a runtime length check to avoid
- * out-of-bounds reads on shorter (but spec-valid) payloads.
- * For example:
- *
- * int len = cea_db_payload_len(db);
- *
- * if (len > AMD_VSDB_V3_PAYLOAD_MIN_LEN)
- * info->amd_vsdb.foo = p->extra[0];
- */
+ switch (version) {
+ default:
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] AMD VSDB v%u is unsupported, VSDB will be parsed as v3.\n",
+ connector->base.id, connector->name, version);
+ fallthrough;
+ case 3:
+ drm_parse_amd_vsdb_v3(info, (const struct amd_vsdb_v3_payload *)data,
+ payload_len);
+ break;
+ case 2:
+ drm_parse_amd_vsdb_v2(info, (const struct amd_vsdb_v2_payload *)data);
+ break;
+ case 1:
+ drm_parse_amd_vsdb_v1(info, (const struct amd_vsdb_v1_payload *)data);
+ break;
+ }
}
static void drm_parse_cea_ext(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c
index abef865c5f2c..3bb6bfd560d3 100644
--- a/drivers/gpu/drm/drm_gem_atomic_helper.c
+++ b/drivers/gpu/drm/drm_gem_atomic_helper.c
@@ -181,6 +181,48 @@ EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb);
*/
/**
+ * __drm_gem_shadow_plane_state_init - initializes shadow-buffered plane state
+ * @plane: the plane
+ * @shadow_plane_state: the shadow-buffered plane state
+ *
+ * This function initializes a pre-allocated shadow-buffered plane state.
+ * Helpful for drivers that subclass struct drm_shadow_plane_state.
+ */
+void __drm_gem_shadow_plane_state_init(struct drm_plane *plane,
+ struct drm_shadow_plane_state *shadow_plane_state)
+{
+ __drm_atomic_helper_plane_state_init(&shadow_plane_state->base, plane);
+ drm_format_conv_state_init(&shadow_plane_state->fmtcnv_state);
+}
+EXPORT_SYMBOL(__drm_gem_shadow_plane_state_init);
+
+/**
+ * drm_gem_create_shadow_plane_state - creates a shadow-buffered plane state
+ * @plane: the plane
+ *
+ * This function implements struct &drm_plane_funcs.atomic_create_state for
+ * shadow-buffered planes. It assumes the new state to be of type
+ * struct drm_shadow_plane_state.
+ *
+ * Returns:
+ * A pointer to a new plane state on success, or an ERR_PTR()-encoded
+ * error code otherwise.
+ */
+struct drm_plane_state *drm_gem_create_shadow_plane_state(struct drm_plane *plane)
+{
+ struct drm_shadow_plane_state *shadow_plane_state;
+
+ shadow_plane_state = kzalloc_obj(*shadow_plane_state);
+ if (!shadow_plane_state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_gem_shadow_plane_state_init(plane, shadow_plane_state);
+
+ return &shadow_plane_state->base;
+}
+EXPORT_SYMBOL(drm_gem_create_shadow_plane_state);
+
+/**
* __drm_gem_duplicate_shadow_plane_state - duplicates shadow-buffered plane state
* @plane: the plane
* @new_shadow_plane_state: the new shadow-buffered plane state
@@ -298,29 +340,6 @@ void __drm_gem_reset_shadow_plane(struct drm_plane *plane,
EXPORT_SYMBOL(__drm_gem_reset_shadow_plane);
/**
- * drm_gem_reset_shadow_plane - resets a shadow-buffered plane
- * @plane: the plane
- *
- * This function implements struct &drm_plane_funcs.reset_plane for
- * shadow-buffered planes. It assumes the current plane state to be
- * of type struct drm_shadow_plane and it allocates the new state of
- * this type.
- */
-void drm_gem_reset_shadow_plane(struct drm_plane *plane)
-{
- struct drm_shadow_plane_state *shadow_plane_state;
-
- if (plane->state) {
- drm_gem_destroy_shadow_plane_state(plane, plane->state);
- plane->state = NULL; /* must be set to NULL here */
- }
-
- shadow_plane_state = kzalloc_obj(*shadow_plane_state);
- __drm_gem_reset_shadow_plane(plane, shadow_plane_state);
-}
-EXPORT_SYMBOL(drm_gem_reset_shadow_plane);
-
-/**
* drm_gem_begin_shadow_fb_access - prepares shadow framebuffers for CPU access
* @plane: the plane
* @plane_state: the plane state of type struct drm_shadow_plane_state
@@ -408,17 +427,22 @@ void drm_gem_simple_kms_end_shadow_fb_access(struct drm_simple_display_pipe *pip
EXPORT_SYMBOL(drm_gem_simple_kms_end_shadow_fb_access);
/**
- * drm_gem_simple_kms_reset_shadow_plane - resets a shadow-buffered plane
+ * drm_gem_simple_kms_create_shadow_plane_state - creates shadow-buffered plane state
* @pipe: the simple display pipe
*
- * This function implements struct drm_simple_display_funcs.reset_plane
+ * This function implements struct drm_simple_display_pipe_funcs.create_plane_state
* for shadow-buffered planes.
+ *
+ * Returns:
+ * A pointer to a new plane state on success, or an ERR_PTR()-encoded
+ * error code otherwise.
*/
-void drm_gem_simple_kms_reset_shadow_plane(struct drm_simple_display_pipe *pipe)
+struct drm_plane_state *
+drm_gem_simple_kms_create_shadow_plane_state(struct drm_simple_display_pipe *pipe)
{
- drm_gem_reset_shadow_plane(&pipe->plane);
+ return drm_gem_create_shadow_plane_state(&pipe->plane);
}
-EXPORT_SYMBOL(drm_gem_simple_kms_reset_shadow_plane);
+EXPORT_SYMBOL(drm_gem_simple_kms_create_shadow_plane_state);
/**
* drm_gem_simple_kms_duplicate_shadow_plane_state - duplicates shadow-buffered plane state
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index e495117735e1..2b53124ded68 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -287,13 +287,12 @@ 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;
-
if (WARN_ON(!panel))
return -EINVAL;
*panel = NULL;
+ if (bridge)
+ *bridge = NULL;
/*
* of_graph_get_remote_node() produces a noisy error message if port
@@ -304,30 +303,27 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
if (!of_graph_is_present(np))
return -ENODEV;
- remote = of_graph_get_remote_node(np, port, endpoint);
+ struct device_node *remote __free(device_node) =
+ of_graph_get_remote_node(np, port, endpoint);
if (!remote)
return -ENODEV;
*panel = of_drm_find_panel(remote);
if (!IS_ERR(*panel))
- ret = 0;
- else
- *panel = NULL;
+ return 0;
+
+ *panel = NULL;
if (bridge) {
- if (ret) {
- /* No panel found yet, check for a bridge next. */
- *bridge = of_drm_find_bridge(remote);
- if (*bridge)
- ret = 0;
- } else {
- *bridge = NULL;
- }
+ /* No panel found yet, check for a bridge next. */
+ *bridge = of_drm_find_bridge(remote);
+ if (*bridge)
+ return 0;
+ *bridge = NULL;
}
- of_node_put(remote);
- return ret;
+ return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
@@ -550,8 +546,8 @@ EXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping);
*
* Return:
* * min..max - positive integer count of "data-lanes" elements
- * * -ve - the "data-lanes" property is missing or invalid
- * * -EINVAL - the "data-lanes" property is unsupported
+ * * -ENODATA - the property does not have a value.
+ * * -EINVAL - the "data-lanes" property is missing or invalid
*/
int drm_of_get_data_lanes_count(const struct device_node *endpoint,
const unsigned int min, const unsigned int max)
@@ -582,8 +578,8 @@ EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count);
*
* Return:
* * min..max - positive integer count of "data-lanes" elements
- * * -EINVAL - the "data-mapping" property is unsupported
- * * -ENODEV - the "data-mapping" property is missing
+ * * -ENODATA - the property does not have a value.
+ * * -EINVAL - the "data-lanes" property is missing or invalid
*/
int drm_of_get_data_lanes_count_ep(const struct device_node *port,
int port_reg, int reg,
diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
index 8e1d07b9f1e3..5e324c77ad27 100644
--- a/drivers/gpu/drm/drm_simple_kms_helper.c
+++ b/drivers/gpu/drm/drm_simple_kms_helper.c
@@ -20,16 +20,6 @@ static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
.destroy = drm_encoder_cleanup,
};
-int drm_simple_encoder_init(struct drm_device *dev,
- struct drm_encoder *encoder,
- int encoder_type)
-{
- return drm_encoder_init(dev, encoder,
- &drm_simple_encoder_funcs_cleanup,
- encoder_type, NULL);
-}
-EXPORT_SYMBOL(drm_simple_encoder_init);
-
void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
size_t offset, int encoder_type)
{
@@ -102,17 +92,6 @@ static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
.atomic_disable = drm_simple_kms_crtc_disable,
};
-static void drm_simple_kms_crtc_reset(struct drm_crtc *crtc)
-{
- struct drm_simple_display_pipe *pipe;
-
- pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
- if (!pipe->funcs || !pipe->funcs->reset_crtc)
- return drm_atomic_helper_crtc_reset(crtc);
-
- return pipe->funcs->reset_crtc(pipe);
-}
-
static struct drm_crtc_state *drm_simple_kms_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct drm_simple_display_pipe *pipe;
@@ -158,7 +137,7 @@ static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
- .reset = drm_simple_kms_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@@ -281,15 +260,16 @@ static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
.atomic_update = drm_simple_kms_plane_atomic_update,
};
-static void drm_simple_kms_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *
+drm_simple_kms_plane_create_state(struct drm_plane *plane)
{
struct drm_simple_display_pipe *pipe;
pipe = container_of(plane, struct drm_simple_display_pipe, plane);
- if (!pipe->funcs || !pipe->funcs->reset_plane)
- return drm_atomic_helper_plane_reset(plane);
+ if (!pipe->funcs || !pipe->funcs->create_plane_state)
+ return drm_atomic_helper_plane_create_state(plane);
- return pipe->funcs->reset_plane(pipe);
+ return pipe->funcs->create_plane_state(pipe);
}
static struct drm_plane_state *drm_simple_kms_plane_duplicate_state(struct drm_plane *plane)
@@ -319,7 +299,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
- .reset = drm_simple_kms_plane_reset,
+ .atomic_create_state = drm_simple_kms_plane_create_state,
.atomic_duplicate_state = drm_simple_kms_plane_duplicate_state,
.atomic_destroy_state = drm_simple_kms_plane_destroy_state,
.format_mod_supported = drm_simple_kms_format_mod_supported,
@@ -363,7 +343,8 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
- ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
+ ret = drm_encoder_init(dev, encoder, &drm_simple_encoder_funcs_cleanup,
+ DRM_MODE_ENCODER_NONE, NULL);
if (ret || !connector)
return ret;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index b071c019670d..458cad64bd07 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -163,7 +163,7 @@ static const struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.destroy = exynos_drm_crtc_destroy,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = exynos_drm_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 0683236aa5df..74d3a2bebb74 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -201,8 +201,7 @@ int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)
ret = drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
- DRM_DEV_ERROR(encoder_to_dpi(encoder)->dev,
- "failed to create encoder ret = %d\n", ret);
+ drm_err(dev, "failed to create encoder ret = %d\n", ret);
return ret;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 59dea853d364..077571eae9d6 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -452,8 +452,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
ret = drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
- DRM_DEV_ERROR(dev, "failed to initialize encoder ret = %d\n",
- ret);
+ drm_err(drm_dev, "failed to initialize encoder ret = %d\n", ret);
return ret;
}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index d4fce39c2b61..055dc04f9b3e 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -1869,8 +1869,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
ret = drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
- DRM_DEV_ERROR(dev, "failed to initialize encoder ret = %d\n",
- ret);
+ drm_err(drm_dev, "failed to initialize encoder ret = %d\n", ret);
return ret;
}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
index 9ef16f5eb613..73c0675e6c54 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
@@ -161,7 +161,7 @@ static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = {
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.set_config = drm_atomic_helper_set_config,
.enable_vblank = fsl_dcu_drm_crtc_enable_vblank,
.disable_vblank = fsl_dcu_drm_crtc_disable_vblank,
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index ec71fbbb0eb8..c64264541fe2 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -11,14 +11,18 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include "fsl_dcu_drm_drv.h"
#include "fsl_tcon.h"
+static const struct drm_encoder_funcs fsl_dcu_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
struct drm_crtc *crtc)
{
@@ -31,8 +35,8 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
if (fsl_dev->tcon)
fsl_tcon_bypass_enable(fsl_dev->tcon);
- ret = drm_simple_encoder_init(fsl_dev->drm, encoder,
- DRM_MODE_ENCODER_LVDS);
+ ret = drm_encoder_init(fsl_dev->drm, encoder, &fsl_dcu_encoder_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
index 3a1b9e2a2eaa..680447f426f3 100644
--- a/drivers/gpu/drm/gud/gud_drv.c
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -347,7 +347,7 @@ static const struct drm_crtc_helper_funcs gud_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs gud_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
index b4ab53db1c08..33402cf54072 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -491,7 +491,7 @@ static const struct drm_crtc_funcs hibmc_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.set_config = drm_atomic_helper_set_config,
.destroy = drm_crtc_cleanup,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = hibmc_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
index 15042365dec0..62c5bd3277da 100644
--- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
+++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
@@ -20,11 +20,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include "dw_dsi_reg.h"
@@ -687,6 +687,10 @@ static int dsi_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
+static const struct drm_encoder_funcs dw_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = {
.atomic_check = dsi_encoder_atomic_check,
.mode_valid = dsi_encoder_mode_valid,
@@ -708,7 +712,8 @@ static int dw_drm_encoder_init(struct device *dev,
}
encoder->possible_crtcs = crtc_mask;
- ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
+ ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
DRM_ERROR("failed to init dsi encoder\n");
return ret;
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
index 902c5dace4ff..5f4e738bc94e 100644
--- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
@@ -540,7 +540,7 @@ static const struct drm_crtc_funcs ade_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = ade_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h
index 78136ec2c2f4..29e6f54af15f 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm.h
+++ b/drivers/gpu/drm/hyperv/hyperv_drm.h
@@ -29,7 +29,6 @@ struct hv_drm_device {
unsigned long fb_base;
unsigned long fb_size;
struct completion wait;
- u32 synthvid_version;
u32 mmio_megabytes;
bool dirt_needed;
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 1855749c1e41..3261e730c69d 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -126,7 +126,7 @@ static const struct drm_crtc_helper_funcs hv_drm_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs hv_drm_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
index f0ef627b4898..1d09bba1efdd 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
@@ -17,7 +17,7 @@
#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff)
#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16)
-/* Support for VERSION_WIN7 is removed. #define is retained for reference. */
+/* Support for WIN7 and WIN8 is removed. #define's retained for reference. */
#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5)
@@ -181,16 +181,6 @@ struct synthvid_msg {
};
} __packed;
-static inline bool hv_drm_version_ge(u32 ver1, u32 ver2)
-{
- if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) ||
- (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) &&
- SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2)))
- return true;
-
- return false;
-}
-
static inline int hv_drm_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg)
{
static atomic64_t request_id = ATOMIC64_INIT(0);
@@ -236,7 +226,6 @@ static int hv_drm_negotiate_version(struct hv_device *hdev, u32 ver)
return -ENODEV;
}
- hv->synthvid_version = ver;
drm_info(dev, "Synthvid Version major %d, minor %d\n",
SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver));
@@ -557,23 +546,12 @@ int hv_drm_connect_vsp(struct hv_device *hdev)
return ret;
}
- /* Negotiate the protocol version with host */
- switch (vmbus_proto_version) {
- case VERSION_WIN10:
- case VERSION_WIN10_V5:
- ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
- if (!ret)
- break;
- fallthrough;
- case VERSION_WIN8:
- case VERSION_WIN8_1:
- ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN8);
- break;
- default:
- ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
- break;
- }
-
+ /*
+ * Negotiate the protocol version with host. Since support for hosts
+ * older than WIN10 has been removed from Linux, only negotiate the
+ * WIN10 version.
+ */
+ ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
if (ret) {
drm_err(dev, "Synthetic video device version not accepted %d\n", ret);
goto error;
@@ -581,11 +559,9 @@ int hv_drm_connect_vsp(struct hv_device *hdev)
hv->screen_depth = SYNTHVID_DEPTH_WIN8;
- if (hv_drm_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) {
- ret = hv_drm_get_supported_resolution(hdev);
- if (ret)
- drm_err(dev, "Failed to get supported resolution from host, use default\n");
- }
+ ret = hv_drm_get_supported_resolution(hdev);
+ if (ret)
+ drm_err(dev, "Failed to get supported resolution from host, use default\n");
if (!hv->screen_width_max) {
hv->screen_width_max = SYNTHVID_WIDTH_WIN8;
diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c
index 764510ff90a4..0d64186a9901 100644
--- a/drivers/gpu/drm/imx/dc/dc-crtc.c
+++ b/drivers/gpu/drm/imx/dc/dc-crtc.c
@@ -98,7 +98,7 @@ static void dc_crtc_disable_vblank(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs dc_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/imx/dc/dc-kms.c b/drivers/gpu/drm/imx/dc/dc-kms.c
index 0f8cfaf4c4d1..a9adcfc68b84 100644
--- a/drivers/gpu/drm/imx/dc/dc-kms.c
+++ b/drivers/gpu/drm/imx/dc/dc-kms.c
@@ -17,7 +17,6 @@
#include <drm/drm_mode_config.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include "dc-de.h"
@@ -30,6 +29,10 @@ static const struct drm_mode_config_funcs dc_drm_mode_config_funcs = {
.atomic_commit = drm_atomic_helper_commit,
};
+static const struct drm_encoder_funcs dc_kms_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm,
int crtc_index)
{
@@ -55,7 +58,8 @@ static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm,
}
encoder = &dc_drm->encoder[crtc_index];
- ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
+ ret = drm_encoder_init(drm, encoder, &dc_kms_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
if (ret) {
dev_err(dev, "failed to initialize encoder for CRTC%u: %d\n",
crtc->index, ret);
diff --git a/drivers/gpu/drm/imx/dcss/dcss-crtc.c b/drivers/gpu/drm/imx/dcss/dcss-crtc.c
index ab41759a9f52..5e9ee3f09d51 100644
--- a/drivers/gpu/drm/imx/dcss/dcss-crtc.c
+++ b/drivers/gpu/drm/imx/dcss/dcss-crtc.c
@@ -45,7 +45,7 @@ static const struct drm_crtc_funcs dcss_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = dcss_enable_vblank,
diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c
index 1746dc5d4b64..303e93fd036c 100644
--- a/drivers/gpu/drm/imx/dcss/dcss-plane.c
+++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c
@@ -96,7 +96,7 @@ static const struct drm_plane_funcs dcss_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = dcss_plane_destroy,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.format_mod_supported = dcss_plane_format_mod_supported,
diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
index 1405e50ee56b..f4fe1842cfae 100644
--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
@@ -107,19 +107,17 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
spin_unlock_irq(&crtc->dev->event_lock);
}
-static void imx_drm_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *imx_drm_crtc_create_state(struct drm_crtc *crtc)
{
struct imx_crtc_state *state;
- if (crtc->state)
- __drm_atomic_helper_crtc_destroy_state(crtc->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- kfree(to_imx_crtc_state(crtc->state));
- crtc->state = NULL;
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
- state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ return &state->base;
}
static struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc)
@@ -164,7 +162,7 @@ static void ipu_disable_vblank(struct drm_crtc *crtc)
static const struct drm_crtc_funcs ipu_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = imx_drm_crtc_reset,
+ .atomic_create_state = imx_drm_crtc_create_state,
.atomic_duplicate_state = imx_drm_crtc_duplicate_state,
.atomic_destroy_state = imx_drm_crtc_destroy_state,
.enable_vblank = ipu_enable_vblank,
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 738a80b2550f..625ec76cf04d 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -987,7 +987,7 @@ static const struct drm_driver ingenic_drm_driver_data = {
static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.destroy = drm_plane_cleanup,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
@@ -997,7 +997,7 @@ static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = {
static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c
index 56143a191f36..bf24882e8d9f 100644
--- a/drivers/gpu/drm/ingenic/ingenic-ipu.c
+++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c
@@ -718,7 +718,7 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane,
static const struct drm_plane_funcs ingenic_ipu_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.destroy = drm_plane_cleanup,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
diff --git a/drivers/gpu/drm/kmb/kmb_crtc.c b/drivers/gpu/drm/kmb/kmb_crtc.c
index 1dcdc55b0069..06cf5ad60b81 100644
--- a/drivers/gpu/drm/kmb/kmb_crtc.c
+++ b/drivers/gpu/drm/kmb/kmb_crtc.c
@@ -58,7 +58,7 @@ static const struct drm_crtc_funcs kmb_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = kmb_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/kmb/kmb_dsi.c b/drivers/gpu/drm/kmb/kmb_dsi.c
index 59d0e856392f..13adba96bc78 100644
--- a/drivers/gpu/drm/kmb/kmb_dsi.c
+++ b/drivers/gpu/drm/kmb/kmb_dsi.c
@@ -14,8 +14,8 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -1427,6 +1427,10 @@ struct kmb_dsi *kmb_dsi_init(struct platform_device *pdev)
return kmb_dsi;
}
+static const struct drm_encoder_funcs kmb_dsi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
int kmb_dsi_encoder_init(struct drm_device *dev, struct kmb_dsi *kmb_dsi)
{
struct drm_encoder *encoder;
@@ -1437,7 +1441,8 @@ int kmb_dsi_encoder_init(struct drm_device *dev, struct kmb_dsi *kmb_dsi)
encoder->possible_crtcs = 1;
encoder->possible_clones = 0;
- ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DSI);
+ ret = drm_encoder_init(dev, encoder, &kmb_dsi_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
dev_err(kmb_dsi->dev, "Failed to init encoder %d\n", ret);
return ret;
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index 7961139333ad..f82506e10fd5 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -21,12 +21,12 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
@@ -914,12 +914,16 @@ void mtk_dsi_ddp_stop(struct device *dev)
mtk_dsi_poweroff(dsi);
}
+static const struct drm_encoder_funcs mtk_dsi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static int mtk_dsi_encoder_init(struct drm_device *drm, struct mtk_dsi *dsi)
{
int ret;
- ret = drm_simple_encoder_init(drm, &dsi->encoder,
- DRM_MODE_ENCODER_DSI);
+ ret = drm_encoder_init(drm, &dsi->encoder, &mtk_dsi_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
drm_err(drm, "Failed to encoder init to drm\n");
return ret;
diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c
index b77d2d24ead1..056dbea8deec 100644
--- a/drivers/gpu/drm/meson/meson_crtc.c
+++ b/drivers/gpu/drm/meson/meson_crtc.c
@@ -74,7 +74,7 @@ static const struct drm_crtc_funcs meson_crtc_funcs = {
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.set_config = drm_atomic_helper_set_config,
.enable_vblank = meson_crtc_enable_vblank,
.disable_vblank = meson_crtc_disable_vblank,
diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
index 22cacb1660c4..cdb84d2283f8 100644
--- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c
+++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
@@ -17,8 +17,8 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include "meson_registers.h"
#include "meson_vclk.h"
@@ -218,6 +218,10 @@ static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = {
.atomic_create_state = drm_atomic_helper_bridge_create_state,
};
+static const struct drm_encoder_funcs meson_encoder_cvbs_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
int meson_encoder_cvbs_probe(struct meson_drm *priv)
{
struct drm_device *drm = priv->drm;
@@ -257,8 +261,9 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv)
meson_encoder_cvbs->priv = priv;
/* Encoder */
- ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder,
- DRM_MODE_ENCODER_TVDAC);
+ ret = drm_encoder_init(priv->drm, &meson_encoder_cvbs->encoder,
+ &meson_encoder_cvbs_funcs,
+ DRM_MODE_ENCODER_TVDAC, NULL);
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to init CVBS encoder\n");
diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c
index 3e422b612f74..faa309cb97a6 100644
--- a/drivers/gpu/drm/meson/meson_encoder_dsi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c
@@ -10,10 +10,10 @@
#include <linux/of_graph.h>
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_probe_helper.h>
#include "meson_drv.h"
@@ -99,6 +99,10 @@ static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = {
.atomic_create_state = drm_atomic_helper_bridge_create_state,
};
+static const struct drm_encoder_funcs meson_encoder_dsi_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
int meson_encoder_dsi_probe(struct meson_drm *priv)
{
struct meson_encoder_dsi *meson_encoder_dsi;
@@ -133,8 +137,9 @@ int meson_encoder_dsi_probe(struct meson_drm *priv)
meson_encoder_dsi->priv = priv;
/* Encoder */
- ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder,
- DRM_MODE_ENCODER_DSI);
+ ret = drm_encoder_init(priv->drm, &meson_encoder_dsi->encoder,
+ &meson_encoder_dsi_funcs, DRM_MODE_ENCODER_DSI,
+ NULL);
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to init DSI encoder\n");
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
index 0c7a72cb514a..c4355c5cc340 100644
--- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
@@ -23,8 +23,8 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <linux/media-bus-format.h>
#include <linux/videodev2.h>
@@ -369,6 +369,10 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
.atomic_create_state = drm_atomic_helper_bridge_create_state,
};
+static const struct drm_encoder_funcs meson_encoder_hdmi_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
int meson_encoder_hdmi_probe(struct meson_drm *priv)
{
struct meson_encoder_hdmi *meson_encoder_hdmi;
@@ -407,8 +411,9 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv)
meson_encoder_hdmi->priv = priv;
/* Encoder */
- ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder,
- DRM_MODE_ENCODER_TMDS);
+ ret = drm_encoder_init(priv->drm, &meson_encoder_hdmi->encoder,
+ &meson_encoder_hdmi_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
dev_err_probe(priv->dev, ret, "Failed to init HDMI encoder\n");
goto err_put_node;
diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c
index 7047807f1a3e..930119e18a4f 100644
--- a/drivers/gpu/drm/meson/meson_overlay.c
+++ b/drivers/gpu/drm/meson/meson_overlay.c
@@ -802,7 +802,7 @@ static const struct drm_plane_funcs meson_overlay_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.format_mod_supported = meson_overlay_format_mod_supported,
diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c
index 2d88fe87a11a..cf8c90158fcf 100644
--- a/drivers/gpu/drm/meson/meson_plane.c
+++ b/drivers/gpu/drm/meson/meson_plane.c
@@ -466,7 +466,7 @@ static const struct drm_plane_funcs meson_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.format_mod_supported = meson_plane_format_mod_supported,
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index 3732c62351bb..bccc70983d28 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -407,15 +407,15 @@ void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic
.atomic_enable = mgag200_crtc_helper_atomic_enable, \
.atomic_disable = mgag200_crtc_helper_atomic_disable
-void mgag200_crtc_reset(struct drm_crtc *crtc);
+struct drm_crtc_state *mgag200_crtc_atomic_create_state(struct drm_crtc *crtc);
struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc);
void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state);
#define MGAG200_CRTC_FUNCS \
- .reset = mgag200_crtc_reset, \
.destroy = drm_crtc_cleanup, \
.set_config = drm_atomic_helper_set_config, \
.page_flip = drm_atomic_helper_page_flip, \
+ .atomic_create_state = mgag200_crtc_atomic_create_state, \
.atomic_duplicate_state = mgag200_crtc_atomic_duplicate_state, \
.atomic_destroy_state = mgag200_crtc_atomic_destroy_state
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 7e07fc3f1a60..4e952655e4f5 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -679,18 +679,17 @@ void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic
mgag200_disable_display(mdev);
}
-void mgag200_crtc_reset(struct drm_crtc *crtc)
+struct drm_crtc_state *mgag200_crtc_atomic_create_state(struct drm_crtc *crtc)
{
struct mgag200_crtc_state *mgag200_crtc_state;
- if (crtc->state)
- crtc->funcs->atomic_destroy_state(crtc, crtc->state);
-
mgag200_crtc_state = kzalloc_obj(*mgag200_crtc_state);
- if (mgag200_crtc_state)
- __drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ if (!mgag200_crtc_state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(&mgag200_crtc_state->base, crtc);
+
+ return &mgag200_crtc_state->base;
}
struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index 42d0a529b4d5..77a2d1f6700d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -1140,17 +1140,17 @@ end:
DPU_ATRACE_END("crtc_commit");
}
-static void dpu_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *dpu_crtc_create_state(struct drm_crtc *crtc)
{
- struct dpu_crtc_state *cstate = kzalloc_obj(*cstate);
+ struct dpu_crtc_state *cstate;
- if (crtc->state)
- dpu_crtc_destroy_state(crtc, crtc->state);
+ cstate = kzalloc_obj(*cstate);
+ if (!cstate)
+ return ERR_PTR(-ENOMEM);
- if (cstate)
- __drm_atomic_helper_crtc_reset(crtc, &cstate->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ __drm_atomic_helper_crtc_state_init(&cstate->base, crtc);
+
+ return &cstate->base;
}
/**
@@ -1843,7 +1843,7 @@ static int dpu_crtc_late_register(struct drm_crtc *crtc)
static const struct drm_crtc_funcs dpu_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = dpu_crtc_reset,
+ .atomic_create_state = dpu_crtc_create_state,
.atomic_duplicate_state = dpu_crtc_duplicate_state,
.atomic_destroy_state = dpu_crtc_destroy_state,
.atomic_print_state = dpu_crtc_atomic_print_state,
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
index 7b92082d35a6..1a471cdf767b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
@@ -1728,32 +1728,28 @@ static void dpu_plane_atomic_print_state(struct drm_printer *p,
}
}
-static void dpu_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *dpu_plane_create_state(struct drm_plane *plane)
{
struct dpu_plane *pdpu;
struct dpu_plane_state *pstate;
if (!plane) {
DPU_ERROR("invalid plane\n");
- return;
+ return ERR_PTR(-EINVAL);
}
pdpu = to_dpu_plane(plane);
DPU_DEBUG_PLANE(pdpu, "\n");
- /* remove previous state, if present */
- if (plane->state) {
- dpu_plane_destroy_state(plane, plane->state);
- plane->state = NULL;
- }
-
pstate = kzalloc_obj(*pstate);
if (!pstate) {
DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");
- return;
+ return ERR_PTR(-ENOMEM);
}
- __drm_atomic_helper_plane_reset(plane, &pstate->base);
+ __drm_atomic_helper_plane_state_init(&pstate->base, plane);
+
+ return &pstate->base;
}
#ifdef CONFIG_DEBUG_FS
@@ -1796,7 +1792,7 @@ static bool dpu_plane_format_mod_supported(struct drm_plane *plane,
static const struct drm_plane_funcs dpu_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = dpu_plane_reset,
+ .atomic_create_state = dpu_plane_create_state,
.atomic_duplicate_state = dpu_plane_duplicate_state,
.atomic_destroy_state = dpu_plane_destroy_state,
.atomic_print_state = dpu_plane_atomic_print_state,
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
index 57dfce58450b..93050b3c3548 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
@@ -477,7 +477,7 @@ static const struct drm_crtc_funcs mdp4_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.cursor_set = mdp4_crtc_cursor_set,
.cursor_move = mdp4_crtc_cursor_move,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = msm_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
index 9459f70ce0ba..f9067e508f4c 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
@@ -71,7 +71,7 @@ static const struct drm_plane_funcs mdp4_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.set_property = mdp4_plane_set_property,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
index 4c4a897fc1ee..1e1fff37380c 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
@@ -1128,23 +1128,23 @@ static void mdp5_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state
kfree(mdp5_cstate);
}
-static void mdp5_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *mdp5_crtc_create_state(struct drm_crtc *crtc)
{
- struct mdp5_crtc_state *mdp5_cstate = kzalloc_obj(*mdp5_cstate);
+ struct mdp5_crtc_state *mdp5_cstate;
- if (crtc->state)
- mdp5_crtc_destroy_state(crtc, crtc->state);
+ mdp5_cstate = kzalloc_obj(*mdp5_cstate);
+ if (!mdp5_cstate)
+ return ERR_PTR(-ENOMEM);
- if (mdp5_cstate)
- __drm_atomic_helper_crtc_reset(crtc, &mdp5_cstate->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ __drm_atomic_helper_crtc_state_init(&mdp5_cstate->base, crtc);
+
+ return &mdp5_cstate->base;
}
static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = mdp5_crtc_reset,
+ .atomic_create_state = mdp5_crtc_create_state,
.atomic_duplicate_state = mdp5_crtc_duplicate_state,
.atomic_destroy_state = mdp5_crtc_destroy_state,
.atomic_print_state = mdp5_crtc_atomic_print_state,
@@ -1157,7 +1157,7 @@ static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = {
static const struct drm_crtc_funcs mdp5_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = mdp5_crtc_reset,
+ .atomic_create_state = mdp5_crtc_create_state,
.atomic_duplicate_state = mdp5_crtc_duplicate_state,
.atomic_destroy_state = mdp5_crtc_destroy_state,
.cursor_set = mdp5_crtc_cursor_set,
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
index 841f444a8d68..7e878c698247 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
@@ -80,19 +80,15 @@ mdp5_plane_atomic_print_state(struct drm_printer *p,
drm_printf(p, "\tstage=%s\n", stage2name(pstate->stage));
}
-static void mdp5_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *mdp5_plane_create_state(struct drm_plane *plane)
{
struct mdp5_plane_state *mdp5_state;
-
- if (plane->state)
- __drm_atomic_helper_plane_destroy_state(plane->state);
-
- kfree(to_mdp5_plane_state(plane->state));
- plane->state = NULL;
mdp5_state = kzalloc_obj(*mdp5_state);
if (!mdp5_state)
- return;
- __drm_atomic_helper_plane_reset(plane, &mdp5_state->base);
+ return ERR_PTR(-ENOMEM);
+ __drm_atomic_helper_plane_state_init(&mdp5_state->base, plane);
+
+ return &mdp5_state->base;
}
static struct drm_plane_state *
@@ -126,7 +122,7 @@ static void mdp5_plane_destroy_state(struct drm_plane *plane,
static const struct drm_plane_funcs mdp5_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = mdp5_plane_reset,
+ .atomic_create_state = mdp5_plane_create_state,
.atomic_duplicate_state = mdp5_plane_duplicate_state,
.atomic_destroy_state = mdp5_plane_destroy_state,
.atomic_print_state = mdp5_plane_atomic_print_state,
diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c
index 79e0f483ca20..5f0fbec3a390 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_kms.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c
@@ -596,18 +596,17 @@ static void lcdif_crtc_atomic_destroy_state(struct drm_crtc *crtc,
kfree(to_lcdif_crtc_state(state));
}
-static void lcdif_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *lcdif_crtc_create_state(struct drm_crtc *crtc)
{
struct lcdif_crtc_state *state;
- if (crtc->state)
- lcdif_crtc_atomic_destroy_state(crtc, crtc->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- crtc->state = NULL;
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
- state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ return &state->base;
}
static struct drm_crtc_state *
@@ -659,7 +658,7 @@ static const struct drm_crtc_helper_funcs lcdif_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs lcdif_crtc_funcs = {
- .reset = lcdif_crtc_reset,
+ .atomic_create_state = lcdif_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
index d8ebebc5314b..36ea285182b4 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
@@ -484,7 +484,7 @@ static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@@ -495,7 +495,7 @@ static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
};
static const struct drm_crtc_funcs mxsfb_crtc_with_crc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index c9f96ec8455d..00eb2aacbe93 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -344,9 +344,8 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
regp->fp_control |= (2 << 24);
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) {
bool duallink = false, dummy;
- if (nv_connector->edid &&
- nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
- duallink = (((u8 *)nv_connector->edid)[121] == 2);
+ if (nv_connector->spwg_links) {
+ duallink = nv_connector->spwg_links == 2;
} else {
nouveau_bios_parse_lvds_table(dev, output_mode->clock,
&duallink, &dummy);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 2c66e480b511..add19d479ebe 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -1803,8 +1803,8 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_commit *st
lvds_dual = bios->fp.dual_link;
lvds_8bpc = bios->fp.if_is_24bit;
} else {
- if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
- if (((u8 *)nv_connector->edid)[121] == 2)
+ if (nv_connector->spwg_links) {
+ if (nv_connector->spwg_links == 2)
lvds_dual = true;
} else
if (mode->clock >= bios->fp.duallink_transition_clk) {
diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c
index 1affa0378bd0..6a3a93de0cbf 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/head.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/head.c
@@ -491,18 +491,16 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc)
return &asyh->state;
}
-static void
-nv50_head_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *nv50_head_create_state(struct drm_crtc *crtc)
{
struct nv50_head_atom *asyh;
if (WARN_ON(!(asyh = kzalloc_obj(*asyh))))
- return;
+ return ERR_PTR(-ENOMEM);
- if (crtc->state)
- nv50_head_atomic_destroy_state(crtc, crtc->state);
+ __drm_atomic_helper_crtc_state_init(&asyh->state, crtc);
- __drm_atomic_helper_crtc_reset(crtc, &asyh->state);
+ return &asyh->state;
}
static int
@@ -525,7 +523,7 @@ nv50_head_destroy(struct drm_crtc *crtc)
static const struct drm_crtc_funcs
nv50_head_func = {
- .reset = nv50_head_reset,
+ .atomic_create_state = nv50_head_create_state,
.destroy = nv50_head_destroy,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@@ -539,7 +537,7 @@ nv50_head_func = {
static const struct drm_crtc_funcs
nvd9_head_func = {
- .reset = nv50_head_reset,
+ .atomic_create_state = nv50_head_create_state,
.destroy = nv50_head_destroy,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/headca7d.c b/drivers/gpu/drm/nouveau/dispnv50/headca7d.c
index eeaeb15aa664..678cbc78ca9f 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/headca7d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/headca7d.c
@@ -219,10 +219,11 @@ headca7d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nvif_push *push = &head->disp->core->chan.push;
struct nv50_head_mode *m = &asyh->mode;
+ const u64 hz = (u64)m->clock * 1000;
const int i = head->base.index;
int ret;
- ret = PUSH_WAIT(push, 11);
+ ret = PUSH_WAIT(push, 14);
if (ret)
return ret;
@@ -245,11 +246,25 @@ headca7d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
PUSH_MTHD(push, NVCA7D, HEAD_SET_CONTROL(i),
NVDEF(NVCA7D, HEAD_SET_CONTROL, STRUCTURE, PROGRESSIVE));
+ /* The FREQUENCY methods carry only 31 HERTZ bits; the upper bits
+ * of anything past 2.147GHz live in the HI methods
+ * (EvoSetRasterParams9()). Truncating would scan out at pclk modulo 2^31.
+ */
PUSH_MTHD(push, NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i),
- NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000));
+ NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ,
+ (u32)(hz & 0x7fffffff)));
PUSH_MTHD(push, NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(i),
- NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, m->clock * 1000));
+ NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ,
+ (u32)(hz & 0x7fffffff)));
+
+ PUSH_MTHD(push, NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI(i),
+ NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI, HERTZ,
+ (u32)(hz >> 31)),
+
+ HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX(i),
+ NVVAL(NVCA7D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX, HERTZ,
+ (u32)(hz >> 31)));
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h b/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h
index 0fec6fc21d44..5754878ddc69 100644
--- a/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h
+++ b/drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h
@@ -734,6 +734,10 @@
#define NVCA7D_HEAD_SET_CONTROL_CURSOR_COMPOSITION_BYPASS 20:20
#define NVCA7D_HEAD_SET_CONTROL_CURSOR_COMPOSITION_BYPASS_DISABLE (0x00000000)
#define NVCA7D_HEAD_SET_CONTROL_CURSOR_COMPOSITION_BYPASS_ENABLE (0x00000001)
+#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI(a) (0x000020C0 + (a)*0x00000800)
+#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_HERTZ 3:0
+#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX(a) (0x000020C4 + (a)*0x00000800)
+#define NVCA7D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_HI_MAX_HERTZ 3:0
#define NVCA7D_HEAD_SET_SURFACE_ADDRESS_HI_CRC(a) (0x00002150 + (a)*0x00000800)
#define NVCA7D_HEAD_SET_SURFACE_ADDRESS_HI_CRC_ADDRESS_HI 31:0
#define NVCA7D_HEAD_SET_SURFACE_ADDRESS_LO_CRC(a) (0x00002154 + (a)*0x00000800)
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
index 112b674ed9c8..0172e0d200f8 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
@@ -46,6 +46,7 @@ int nv4c_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct n
int g84_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int g92_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int g94_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
+int mcp79_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int gf100_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int gf106_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int gk104_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index 4542d5f4ded8..e907c1d07887 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -666,6 +666,10 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
return nouveau_abi16_put(abi16, -EINVAL);
+ /* zero size yields a zero-length node, underflowing args.limit */
+ if (unlikely(!info->size))
+ return nouveau_abi16_put(abi16, -EINVAL);
+
chan = nouveau_abi16_chan(abi16, info->channel);
if (!chan)
return nouveau_abi16_put(abi16, -ENOENT);
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 21b56cc7605c..9e6906490db5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -360,9 +360,10 @@ void nouveau_unregister_dsm_handler(void) {}
void nouveau_switcheroo_optimus_dsm(void) {}
#endif
-void *
+const struct drm_edid *
nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
{
+ const struct drm_edid *drm_edid;
struct acpi_device *acpidev;
int type, ret;
void *edid;
@@ -384,7 +385,34 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
if (ret < 0)
return NULL;
- return edid;
+ /* Take what _DDC returned as-is first as its length is the only one that
+ * can describe an HF-EEODB EDID, whose block count is not the byte 126
+ * one. Only if that does not validate fall back to trimming the buffer
+ * to the byte 126 size, which is what the padding some firmware appends
+ * needs.
+ */
+ drm_edid = drm_edid_alloc(edid, ret);
+ if (drm_edid && !drm_edid_valid(drm_edid)) {
+ drm_edid_free(drm_edid);
+ drm_edid = NULL;
+
+ if (ret > EDID_LENGTH) {
+ int trimmed = EDID_LENGTH *
+ (1 + ((const struct edid *)edid)->extensions);
+
+ if (trimmed < ret)
+ drm_edid = drm_edid_alloc(edid, trimmed);
+ if (drm_edid && !drm_edid_valid(drm_edid)) {
+ drm_edid_free(drm_edid);
+ drm_edid = NULL;
+ }
+ }
+ }
+ kfree(edid);
+
+ if (!drm_edid)
+ drm_dbg_kms(dev, "Invalid EDID from ACPI _DDC\n");
+ return drm_edid;
}
bool nouveau_acpi_video_backlight_use_native(void)
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.h b/drivers/gpu/drm/nouveau/nouveau_acpi.h
index e39dd8b94b8b..bc3ccca7b338 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.h
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.h
@@ -4,13 +4,15 @@
#define ROM_BIOS_PAGE 4096
+struct drm_edid;
+
#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
bool nouveau_is_optimus(void);
bool nouveau_is_v1_dsm(void);
void nouveau_register_dsm_handler(void);
void nouveau_unregister_dsm_handler(void);
void nouveau_switcheroo_optimus_dsm(void);
-void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
+const struct drm_edid *nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
bool nouveau_acpi_video_backlight_use_native(void);
void nouveau_acpi_video_register_backlight(void);
#else
@@ -19,7 +21,11 @@ static inline bool nouveau_is_v1_dsm(void) { return false; };
static inline void nouveau_register_dsm_handler(void) {}
static inline void nouveau_unregister_dsm_handler(void) {}
static inline void nouveau_switcheroo_optimus_dsm(void) {}
-static inline void *nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return NULL; }
+static inline const struct drm_edid *
+nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
+{
+ return NULL;
+}
static inline bool nouveau_acpi_video_backlight_use_native(void) { return true; }
static inline void nouveau_acpi_video_register_backlight(void) {}
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index b0b0ad9a0c24..cc3caf8de64b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -397,7 +397,8 @@ nouveau_connector_destroy(struct drm_connector *connector)
struct nouveau_connector *nv_connector = nouveau_connector(connector);
nvif_event_dtor(&nv_connector->irq);
nvif_event_dtor(&nv_connector->hpd);
- kfree(nv_connector->edid);
+ cancel_work_sync(&nv_connector->irq_work);
+ drm_edid_free(nv_connector->drm_edid);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
if (nv_connector->aux.transfer)
@@ -469,6 +470,36 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
return found;
}
+static void
+nouveau_connector_set_edid(struct nouveau_connector *nv_connector,
+ const struct drm_edid *drm_edid)
+{
+ if (nv_connector->drm_edid == drm_edid)
+ return;
+
+ /* Updates the EDID property and display_info with HF-EEODB-aware
+ * sizing. The legacy helpers truncate both to what EDID byte 126
+ * admits, hiding the DisplayID extension blocks that carry the
+ * high-refresh timings.
+ */
+ drm_edid_connector_update(&nv_connector->base, drm_edid);
+
+ drm_edid_free(nv_connector->drm_edid);
+ nv_connector->drm_edid = drm_edid;
+
+ /* The SPWG link-count byte lives in a vendor descriptor drm has no
+ * accessor for. Peek at it once here so nothing else needs the raw
+ * EDID.
+ */
+ nv_connector->spwg_links = 0;
+ if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
+ const u8 *raw = (const u8 *)drm_edid_raw(drm_edid);
+
+ if (raw)
+ nv_connector->spwg_links = raw[121] == 2 ? 2 : 1;
+ }
+}
+
static struct nouveau_encoder *
nouveau_connector_of_detect(struct drm_connector *connector)
{
@@ -490,8 +521,17 @@ nouveau_connector_of_detect(struct drm_connector *connector)
int idx = name ? name[strlen(name) - 1] - 'A' : 0;
if (nv_encoder->dcb->i2c_index == idx && edid) {
- nv_connector->edid =
- kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
+ const struct drm_edid *drm_edid =
+ drm_edid_alloc(edid, EDID_LENGTH);
+
+ /* Firmware-provided, so validate it like the DDC
+ * readers would.
+ */
+ if (drm_edid && !drm_edid_valid(drm_edid)) {
+ drm_edid_free(drm_edid);
+ drm_edid = NULL;
+ }
+ nouveau_connector_set_edid(nv_connector, drm_edid);
return nv_encoder;
}
}
@@ -546,17 +586,23 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
}
}
-static void
-nouveau_connector_set_edid(struct nouveau_connector *nv_connector,
- struct edid *edid)
+struct nouveau_rm_edid {
+ u8 *data;
+ size_t size;
+};
+
+static int
+nouveau_connector_rm_edid_block(void *context, u8 *buf, unsigned int block,
+ size_t len)
{
- if (nv_connector->edid != edid) {
- struct edid *old_edid = nv_connector->edid;
+ struct nouveau_rm_edid *rm = context;
+ size_t offset = (size_t)block * EDID_LENGTH;
- drm_connector_update_edid_property(&nv_connector->base, edid);
- kfree(old_edid);
- nv_connector->edid = edid;
- }
+ if (offset + len > rm->size)
+ return -EINVAL;
+
+ memcpy(buf, rm->data + offset, len);
+ return 0;
}
static enum drm_connector_status
@@ -590,22 +636,37 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
nv_encoder = nouveau_connector_ddc_detect(connector);
if (nv_encoder) {
- struct edid *new_edid = NULL;
+ const struct drm_edid *new_edid = NULL;
if (nv_encoder->i2c) {
if ((vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC) &&
nv_connector->type == DCB_CONNECTOR_LVDS)
- new_edid = drm_get_edid_switcheroo(connector, nv_encoder->i2c);
+ new_edid = drm_edid_read_switcheroo(connector, nv_encoder->i2c);
else
- new_edid = drm_get_edid(connector, nv_encoder->i2c);
+ new_edid = drm_edid_read_ddc(connector, nv_encoder->i2c);
} else {
- ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid);
- if (ret < 0)
- return connector_status_disconnected;
+ struct nouveau_rm_edid rm = {};
+
+ /* RM (which owns the DDC pads on GSP boards) reads the
+ * EDID whole and returns its true size, which for an
+ * HF-EEODB EDID exceeds what byte 126 admits. Serve it
+ * through drm's block reader so EEODB sizing, block
+ * validation, and the debugfs EDID override all apply.
+ * A failed read is treated like an empty DDC read,
+ * which releases the runtime-PM reference.
+ */
+ ret = nvif_outp_edid_get(&nv_encoder->outp, &rm.data);
+ if (ret >= 0) {
+ rm.size = ret;
+ new_edid = drm_edid_read_custom(connector,
+ nouveau_connector_rm_edid_block,
+ &rm);
+ kfree(rm.data);
+ }
}
nouveau_connector_set_edid(nv_connector, new_edid);
- if (!nv_connector->edid) {
+ if (!nv_connector->drm_edid) {
NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
connector->name);
goto detect_analog;
@@ -626,7 +687,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
nv_partner->dcb->type == DCB_OUTPUT_TMDS) ||
(nv_encoder->dcb->type == DCB_OUTPUT_TMDS &&
nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) {
- if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
+ if (drm_edid_is_digital(nv_connector->drm_edid))
type = DCB_OUTPUT_TMDS;
else
type = DCB_OUTPUT_ANALOG;
@@ -638,7 +699,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
conn_status = connector_status_connected;
if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
- drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid);
+ drm_dp_cec_attach(&nv_connector->aux,
+ connector->display_info.source_physical_address);
goto out;
} else {
@@ -670,7 +732,7 @@ detect_analog:
}
out:
- if (!nv_connector->edid)
+ if (!nv_connector->drm_edid)
drm_dp_cec_unset_edid(&nv_connector->aux);
pm_runtime_mark_last_busy(dev->dev);
@@ -686,7 +748,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = NULL;
- struct edid *edid = NULL;
+ const struct drm_edid *edid = NULL;
enum drm_connector_status status = connector_status_disconnected;
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
@@ -697,7 +759,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
if (!drm->vbios.fp_no_ddc) {
status = nouveau_connector_detect(connector, force);
if (status == connector_status_connected) {
- edid = nv_connector->edid;
+ edid = nv_connector->drm_edid;
goto out;
}
}
@@ -733,11 +795,20 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
* stored for the panel stored in them.
*/
if (!drm->vbios.fp_no_ddc) {
- edid = (struct edid *)nouveau_bios_embedded_edid(dev);
- if (edid) {
- edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
+ const void *embedded = nouveau_bios_embedded_edid(dev);
+
+ if (embedded) {
+ edid = drm_edid_alloc(embedded, EDID_LENGTH);
+ /* The panel is there either way, so report it
+ * connected and let the probe helper fall back to a
+ * default mode if the EDID itself is unusable.
+ */
if (edid)
status = connector_status_connected;
+ if (edid && !drm_edid_valid(edid)) {
+ drm_edid_free(edid);
+ edid = NULL;
+ }
}
}
@@ -886,7 +957,7 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
bool duallink;
/* if the edid is feeling nice enough to provide this info, use it */
- if (nv_connector->edid && connector->display_info.bpc)
+ if (nv_connector->drm_edid && connector->display_info.bpc)
return;
/* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */
@@ -913,9 +984,8 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
/* LVDS: DDC panel, need to first determine the number of links to
* know which if_is_24bit flag to check...
*/
- if (nv_connector->edid &&
- nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
- duallink = ((u8 *)nv_connector->edid)[121] == 2;
+ if (nv_connector->spwg_links)
+ duallink = nv_connector->spwg_links == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
@@ -973,12 +1043,17 @@ nouveau_connector_get_modes(struct drm_connector *connector)
nv_connector->native_mode = NULL;
}
- if (nv_connector->edid)
- ret = drm_add_edid_modes(connector, nv_connector->edid);
- else
- if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS &&
- (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
- drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) {
+ if (nv_connector->drm_edid) {
+ /* The probe helper clears the property and display_info for
+ * a forced-off connector without calling detect(). Re-sync
+ * from our copy then, since add_modes() reads the property.
+ */
+ if (!connector->edid_blob_ptr)
+ drm_edid_connector_update(connector, nv_connector->drm_edid);
+ ret = drm_edid_connector_add_modes(connector);
+ } else if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS &&
+ (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
+ drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) {
struct drm_display_mode mode;
nouveau_bios_fp_mode(dev, &mode);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 0608cabed058..eb292d2ba4bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -43,7 +43,7 @@
struct nvkm_i2c_port;
struct dcb_output;
-struct edid;
+struct drm_edid;
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
struct nouveau_backlight {
@@ -121,6 +121,13 @@ struct nouveau_connector {
struct drm_connector base;
enum dcb_connector_type type;
u8 index;
+ /* LVDS_SPWG panels state their link count in EDID descriptor 4 (SPWG
+ * byte 0x79), cached by nouveau_connector_set_edid() so nothing else
+ * needs the raw EDID. 0 = unknown (not an SPWG panel, or no EDID) and
+ * callers fall back to their transition-clock/VBIOS heuristics;
+ * 1 = single link; 2 = dual link.
+ */
+ u8 spwg_links;
struct nvif_conn conn;
u64 hpd_pending;
@@ -137,7 +144,8 @@ struct nouveau_connector {
int scaling_mode;
struct nouveau_encoder *detected_encoder;
- struct edid *edid;
+ /* Owner of the sink's EDID, HF-EEODB-complete. */
+ const struct drm_edid *drm_edid;
struct drm_display_mode *native_mode;
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
struct nouveau_backlight *backlight;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 55691ec44aba..738802358d85 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -486,7 +486,7 @@ nouveau_dp_irq(struct work_struct *work)
container_of(work, typeof(*nv_connector), irq_work);
struct drm_connector *connector = &nv_connector->base;
struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP);
- struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev);
+ struct nouveau_drm *drm;
struct nv50_mstm *mstm;
u64 hpd = 0;
int ret;
@@ -494,6 +494,8 @@ nouveau_dp_irq(struct work_struct *work)
if (!outp)
return;
+ drm = nouveau_drm(outp->base.base.dev);
+
mstm = outp->dp.mstm;
NV_DEBUG(drm, "service %s\n", connector->name);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 4d1ad718e09b..b0f9fb10a74d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -103,17 +103,17 @@ MODULE_PARM_DESC(modeset, "enable driver (default: auto, "
int nouveau_modeset = -1;
module_param_named(modeset, nouveau_modeset, int, 0400);
-MODULE_PARM_DESC(atomic, "Expose atomic ioctl (default: disabled)");
-static int nouveau_atomic = 0;
+MODULE_PARM_DESC(atomic, "Expose atomic ioctl (default: auto, "
+ "0 = disabled, 1 = enabled)");
+static int nouveau_atomic = -1;
module_param_named(atomic, nouveau_atomic, int, 0400);
MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)");
static int nouveau_runtime_pm = -1;
module_param_named(runpm, nouveau_runtime_pm, int, 0400);
-static struct drm_driver driver_stub;
-static struct drm_driver driver_pci;
-static struct drm_driver driver_platform;
+static const struct drm_driver driver_legacy_kms;
+static const struct drm_driver driver_atomic_kms;
#ifdef CONFIG_DEBUG_FS
struct dentry *nouveau_debugfs_root;
@@ -727,8 +727,7 @@ nouveau_drm_device_del(struct nouveau_drm *drm)
}
static struct nouveau_drm *
-nouveau_drm_device_new(const struct drm_driver *drm_driver, struct device *parent,
- struct nvkm_device *device)
+nouveau_drm_device_new(struct device *parent, struct nvkm_device *device)
{
static const struct nvif_mclass
mmus[] = {
@@ -737,19 +736,32 @@ nouveau_drm_device_new(const struct drm_driver *drm_driver, struct device *paren
{ NVIF_CLASS_MMU_NV04 , -1 },
{}
};
+ const struct drm_driver *driver;
struct nouveau_drm *drm;
int ret;
+ if (device->card_type >= NV_50) {
+ if (nouveau_atomic)
+ driver = &driver_atomic_kms;
+ else
+ driver = &driver_legacy_kms;
+ } else {
+ if (nouveau_atomic == 1)
+ dev_warn(parent, "Atomic modesetting not supported (needs nv50+)\n");
+
+ driver = &driver_legacy_kms;
+ }
+
drm = kzalloc_obj(*drm);
if (!drm)
return ERR_PTR(-ENOMEM);
drm->nvkm = device;
-
- drm->dev = drm_dev_alloc(drm_driver, parent);
+ drm->dev = drm_dev_alloc(driver, parent);
if (IS_ERR(drm->dev)) {
ret = PTR_ERR(drm->dev);
- goto done;
+ kfree(drm);
+ return ERR_PTR(ret);
}
drm->dev->dev_private = drm;
@@ -873,16 +885,13 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
return ret;
/* Remove conflicting drivers (vesafb, efifb etc). */
- ret = aperture_remove_conflicting_pci_devices(pdev, driver_pci.name);
+ ret = aperture_remove_conflicting_pci_devices(pdev, DRIVER_NAME);
if (ret)
goto fail_nvkm;
pci_set_master(pdev);
- if (nouveau_atomic)
- driver_pci.driver_features |= DRIVER_ATOMIC;
-
- drm = nouveau_drm_device_new(&driver_pci, &pdev->dev, device);
+ drm = nouveau_drm_device_new(&pdev->dev, device);
if (IS_ERR(drm)) {
ret = PTR_ERR(drm);
goto fail_nvkm;
@@ -1359,35 +1368,54 @@ nouveau_driver_fops = {
.fop_flags = FOP_UNSIGNED_OFFSET,
};
-static struct drm_driver
-driver_stub = {
- .driver_features = DRIVER_GEM |
- DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE |
- DRIVER_MODESET |
- DRIVER_RENDER,
- .open = nouveau_drm_open,
- .postclose = nouveau_drm_postclose,
-
-#if defined(CONFIG_DEBUG_FS)
- .debugfs_init = nouveau_drm_debugfs_init,
+#ifdef CONFIG_DEBUG_FS
+#define NOUVEAU_DEBUGFS_OPS .debugfs_init = nouveau_drm_debugfs_init,
+#else
+#define NOUVEAU_DEBUGFS_OPS
#endif
- .ioctls = nouveau_ioctls,
- .num_ioctls = ARRAY_SIZE(nouveau_ioctls),
- .fops = &nouveau_driver_fops,
-
- .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,
-
- .dumb_create = nouveau_display_dumb_create,
- .dumb_map_offset = drm_gem_ttm_dumb_map_offset,
-
- DRM_FBDEV_TTM_DRIVER_OPS,
+#define NOUVEAU_DRIVER_OPS \
+ .open = nouveau_drm_open, \
+ .postclose = nouveau_drm_postclose, \
+ \
+ NOUVEAU_DEBUGFS_OPS \
+ \
+ .ioctls = nouveau_ioctls, \
+ .num_ioctls = ARRAY_SIZE(nouveau_ioctls), \
+ .fops = &nouveau_driver_fops, \
+ \
+ .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, \
+ \
+ .dumb_create = nouveau_display_dumb_create, \
+ .dumb_map_offset = drm_gem_ttm_dumb_map_offset, \
+ \
+ DRM_FBDEV_TTM_DRIVER_OPS, \
+ \
+ .name = DRIVER_NAME, \
+ .desc = DRIVER_DESC, \
+ .major = DRIVER_MAJOR, \
+ .minor = DRIVER_MINOR, \
+ .patchlevel = DRIVER_PATCHLEVEL
+
+static const struct drm_driver
+driver_legacy_kms = {
+ .driver_features = DRIVER_GEM
+ | DRIVER_SYNCOBJ
+ | DRIVER_SYNCOBJ_TIMELINE
+ | DRIVER_MODESET
+ | DRIVER_RENDER,
+ NOUVEAU_DRIVER_OPS,
+};
- .name = DRIVER_NAME,
- .desc = DRIVER_DESC,
- .major = DRIVER_MAJOR,
- .minor = DRIVER_MINOR,
- .patchlevel = DRIVER_PATCHLEVEL,
+static const struct drm_driver
+driver_atomic_kms = {
+ .driver_features = DRIVER_GEM
+ | DRIVER_SYNCOBJ
+ | DRIVER_SYNCOBJ_TIMELINE
+ | DRIVER_MODESET
+ | DRIVER_RENDER
+ | DRIVER_ATOMIC,
+ NOUVEAU_DRIVER_OPS,
};
static struct pci_device_id
@@ -1416,6 +1444,7 @@ static void nouveau_display_options(void)
DRM_DEBUG_DRIVER("... debug : %s\n", nouveau_debug);
DRM_DEBUG_DRIVER("... noaccel : %d\n", nouveau_noaccel);
DRM_DEBUG_DRIVER("... modeset : %d\n", nouveau_modeset);
+ DRM_DEBUG_DRIVER("... atomic : %d\n", nouveau_atomic);
DRM_DEBUG_DRIVER("... runpm : %d\n", nouveau_runtime_pm);
DRM_DEBUG_DRIVER("... vram_pushbuf : %d\n", nouveau_vram_pushbuf);
DRM_DEBUG_DRIVER("... hdmimhz : %d\n", nouveau_hdmimhz);
@@ -1455,7 +1484,7 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func,
if (err)
goto err_free;
- drm = nouveau_drm_device_new(&driver_platform, &pdev->dev, *pdevice);
+ drm = nouveau_drm_device_new(&pdev->dev, *pdevice);
if (IS_ERR(drm)) {
err = PTR_ERR(drm);
goto err_free;
@@ -1480,9 +1509,6 @@ nouveau_drm_init(void)
{
int ret;
- driver_pci = driver_stub;
- driver_platform = driver_stub;
-
nouveau_display_options();
if (nouveau_modeset == -1) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index edbe9e08ba0f..11e95c37ce50 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -96,7 +96,7 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error)
void
nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
{
- cancel_work_sync(&fctx->uevent_work);
+ disable_work_sync(&fctx->uevent_work);
nouveau_fence_context_kill(fctx, 0);
nvif_event_dtor(&fctx->event);
fctx->dead = 1;
diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c
index 58735446d783..c519c28164fa 100644
--- a/drivers/gpu/drm/nouveau/nouveau_svm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_svm.c
@@ -304,6 +304,19 @@ nouveau_svmm_fini(struct nouveau_svmm **psvmm)
{
struct nouveau_svmm *svmm = *psvmm;
if (svmm) {
+ struct nouveau_svm *svm = svmm->vmm->cli->drm->svm;
+
+ /* The fault handler caches svmm pointers under svm->mutex and
+ * dereferences them after dropping it, across blocking faults.
+ * The instance is already unlinked (nouveau_svmm_part), so drain
+ * the handler before the free to release any in-flight reference.
+ *
+ * On device teardown nouveau_svm_fini() ran first and freed
+ * drm->svm (NULL) after draining the buffer; nothing to flush.
+ */
+ if (svm)
+ flush_work(&svm->buffer[0].work);
+
mutex_lock(&svmm->mutex);
svmm->vmm = NULL;
mutex_unlock(&svmm->mutex);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index 96c8a5b29999..a2ed8c4fe52a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -1237,7 +1237,7 @@ nvac_chipset = {
.mc = { 0x00000001, g98_mc_new },
.mmu = { 0x00000001, mcp77_mmu_new },
.mxm = { 0x00000001, nv50_mxm_new },
- .pci = { 0x00000001, g94_pci_new },
+ .pci = { 0x00000001, mcp79_pci_new },
.therm = { 0x00000001, g84_therm_new },
.timer = { 0x00000001, nv41_timer_new },
.volt = { 0x00000001, nv40_volt_new },
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h
index ea5b0bab4cce..7a93f9f40715 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h
@@ -27,7 +27,7 @@
#define KHZ (1000)
#define MHZ (KHZ * 1000)
-#define MASK(w) ((1 << (w)) - 1)
+#define MASK(w) ((1U << (w)) - 1)
#define GK20A_CLK_GPC_MDIV 1000
@@ -79,7 +79,7 @@
#define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250)
#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1
#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31
-#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1
+#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1U
#define GPC2CLK_OUT_VCODIV_WIDTH 6
#define GPC2CLK_OUT_VCODIV_SHIFT 8
#define GPC2CLK_OUT_VCODIV1 0
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c
index f128330f30d7..40bf83ea33ac 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c
@@ -208,7 +208,7 @@ r535_fbsr_resume(struct nvkm_gsp *gsp)
}
static int
-r535_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
+r535_fbsr_suspend(struct nvkm_gsp *gsp)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvkm_device *device = subdev->device;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c
index f544afa12b6b..94925f1590ea 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c
@@ -1749,7 +1749,7 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
sr->sysmemAddrOfSuspendResumeData = gsp->sr.radix3.lvl0.addr;
sr->sizeOfSuspendResumeData = len;
- ret = rm->api->fbsr->suspend(gsp, suspend == NVKM_RUNTIME_SUSPEND);
+ ret = rm->api->fbsr->suspend(gsp);
if (ret) {
nvkm_gsp_mem_dtor(&gsp->sr.meta);
nvkm_gsp_radix3_dtor(gsp, &gsp->sr.radix3);
@@ -1761,8 +1761,12 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
* TODO: Debug the GSP firmware / RPC handling to find out why
* without this Turing (but none of the other architectures)
* ends up resetting all channels after resume.
+ * Additionally, runtime suspend on other architectures quickly
+ * becomes unreliable without this sleep. If you're experiencing
+ * issues with runtime suspend, try bumping this delay up and
+ * sending a patch if it fixes your GPU.
*/
- msleep(50);
+ msleep(200);
}
ret = r535_gsp_rpc_unloading_guest_driver(gsp, suspend);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c
index 8ef8b4f65588..af5aa5065c3d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c
@@ -62,7 +62,7 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
}
static int
-r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtime)
+r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size)
{
NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS *ctrl;
struct nvkm_gsp_object memlist;
@@ -81,7 +81,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim
ctrl->hClient = gsp->internal.client.object.handle;
ctrl->hSysMem = memlist.handle;
ctrl->sysmemAddrOfSuspendResumeData = gsp->sr.meta.addr;
- ctrl->bEnteringGcoffState = runtime ? 1 : 0;
+ ctrl->bEnteringGcoffState = 0;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (ret)
@@ -92,7 +92,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim
}
static int
-r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
+r570_fbsr_suspend(struct nvkm_gsp *gsp)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvkm_device *device = subdev->device;
@@ -133,7 +133,7 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
return ret;
/* Initialise FBSR on RM. */
- ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size, runtime);
+ ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size);
if (ret) {
nvkm_gsp_sg_free(device, &gsp->sr.fbsr);
return ret;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c
index 1488771c63fc..b45781cd0dfd 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c
@@ -207,7 +207,8 @@ r570_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume)
args->srInitArguments.bInPMTransition = 0;
} else {
args->srInitArguments.oldLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3;
- args->srInitArguments.flags = 0;
+ args->srInitArguments.flags =
+ GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION;
args->srInitArguments.bInPMTransition = 1;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h
index b6075021e74f..c458569af9d7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h
@@ -523,6 +523,14 @@ typedef struct
#define NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3 (0x00000003U)
+#define GPU_STATE_FLAGS_PRESERVING BIT(0) // GPU state is preserved
+#define GPU_STATE_FLAGS_VGA_TRANSITION BIT(1) // To be used with GPU_STATE_FLAGS_PRESERVING.
+#define GPU_STATE_FLAGS_PM_TRANSITION BIT(2) // To be used with GPU_STATE_FLAGS_PRESERVING.
+#define GPU_STATE_FLAGS_PM_SUSPEND BIT(3)
+#define GPU_STATE_FLAGS_PM_HIBERNATE BIT(4)
+#define GPU_STATE_FLAGS_GC6_TRANSITION BIT(5) // To be used with GPU_STATE_FLAGS_PRESERVING.
+#define GPU_STATE_FLAGS_FAST_UNLOAD BIT(6) // Used during windows restart, skips stateDestroy steps
+
typedef struct
{
// Magic for verification by secure ucode
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h
index fcd0221dcea1..e9ac47d86b69 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h
@@ -79,7 +79,7 @@ struct nvkm_rm_api {
} *device;
const struct nvkm_rm_api_fbsr {
- int (*suspend)(struct nvkm_gsp *, bool runtime);
+ int (*suspend)(struct nvkm_gsp *);
void (*resume)(struct nvkm_gsp *);
} *fbsr;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
index a14ea0f7b1c8..90f03baeb22a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
@@ -9,6 +9,7 @@ nvkm-y += nvkm/subdev/pci/nv4c.o
nvkm-y += nvkm/subdev/pci/g84.o
nvkm-y += nvkm/subdev/pci/g92.o
nvkm-y += nvkm/subdev/pci/g94.o
+nvkm-y += nvkm/subdev/pci/mcp79.o
nvkm-y += nvkm/subdev/pci/gf100.o
nvkm-y += nvkm/subdev/pci/gf106.o
nvkm-y += nvkm/subdev/pci/gk104.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c
new file mode 100644
index 000000000000..e2ae242f1aa7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Marek Czernohous
+ *
+ * MCP79/MCP7A (NVAC): like g94, but MSI re-arm goes through real PCI
+ * config space. The MMIO-mirror re-arm is unreliable on this IGP and a
+ * missed re-arm kills the interrupt line (see the nv46 comment; g92
+ * already re-arms through config space for the same reason).
+ */
+#include "priv.h"
+
+static const struct nvkm_pci_func
+mcp79_pci_func = {
+ .cfg = { .addr = 0x088000, .size = 0x1000 },
+
+ .init = g84_pci_init,
+ .msi_rearm = nv46_pci_msi_rearm,
+
+ .pcie.init = g84_pcie_init,
+ .pcie.set_link = g84_pcie_set_link,
+
+ .pcie.max_speed = g84_pcie_max_speed,
+ .pcie.cur_speed = g84_pcie_cur_speed,
+
+ .pcie.set_version = g84_pcie_set_version,
+ .pcie.version = g84_pcie_version,
+ .pcie.version_supported = g92_pcie_version_supported,
+};
+
+int
+mcp79_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
+ struct nvkm_pci **ppci)
+{
+ return nvkm_pci_new_(&mcp79_pci_func, device, type, inst, ppci);
+}
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c
index 6da720e8732d..a57f5df5c830 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.c
@@ -59,9 +59,6 @@ static void dsi_uninit_dispc(struct dsi_data *dsi);
static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel);
-static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc,
- const struct mipi_dsi_msg *msg);
-
#ifdef DSI_PERF_MEASURE
static bool dsi_perf;
module_param(dsi_perf, bool, 0644);
@@ -2190,28 +2187,45 @@ static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel)
static int dsi_vc_write_common(struct omap_dss_device *dssdev, int vc,
const struct mipi_dsi_msg *msg)
{
+ DECLARE_COMPLETION_ONSTACK(completion);
struct dsi_data *dsi = to_dsi_data(dssdev);
+ u32 err;
int r;
+ /* wait for IRQ for packet transmission confirmation */
+ r = dsi_register_isr_vc(dsi, vc, dsi_completion_handler,
+ &completion, DSI_VC_IRQ_PACKET_SENT);
+ if (r)
+ return r;
+
if (mipi_dsi_packet_format_is_short(msg->type))
r = dsi_vc_send_short(dsi, vc, msg);
else
r = dsi_vc_send_long(dsi, vc, msg);
- if (r < 0)
+ if ((!r) && wait_for_completion_timeout(&completion,
+ msecs_to_jiffies(500)) == 0)
+ r = -EIO;
+
+ dsi_unregister_isr_vc(dsi, vc, dsi_completion_handler,
+ &completion, DSI_VC_IRQ_PACKET_SENT);
+ if (r)
return r;
- /*
- * TODO: we do not always have to do the BTA sync, for example
- * we can improve performance by setting the update window
- * information without sending BTA sync between the commands.
- * In that case we can return early.
- */
+ /* TODO: find out if more needs to be done for MIPI_DSI_MSG_REQ_ACK */
- r = dsi_vc_send_bta_sync(dssdev, vc);
- if (r) {
- DSSERR("bta sync failed\n");
- return r;
+ if (msg->flags & MIPI_DSI_MSG_REQ_ACK) {
+ r = dsi_vc_send_bta_sync(dssdev, vc);
+ if (r) {
+ DSSERR("bta sync failed\n");
+ return r;
+ }
+ } else {
+ err = dsi_get_errors(dsi);
+ if (err) {
+ DSSERR("Error while sending: %x\n", err);
+ return -EIO;
+ }
}
/* RX_FIFO_NOT_EMPTY */
@@ -3229,21 +3243,6 @@ static int _dsi_update(struct dsi_data *dsi)
return 0;
}
-static int _dsi_send_nop(struct dsi_data *dsi, int vc, int channel)
-{
- const u8 payload[] = { MIPI_DCS_NOP };
- const struct mipi_dsi_msg msg = {
- .channel = channel,
- .type = MIPI_DSI_DCS_SHORT_WRITE,
- .tx_len = 1,
- .tx_buf = payload,
- };
-
- WARN_ON(!dsi_bus_is_locked(dsi));
-
- return _omap_dsi_host_transfer(dsi, vc, &msg);
-}
-
static int dsi_update_channel(struct omap_dss_device *dssdev, int vc)
{
struct dsi_data *dsi = to_dsi_data(dssdev);
@@ -3264,13 +3263,13 @@ static int dsi_update_channel(struct omap_dss_device *dssdev, int vc)
DSSDBG("dsi_update_channel: %d", vc);
/*
- * Send NOP between the frames. If we don't send something here, the
+ * Transition to LP here. If we don't send something here, the
* updates stop working. This is probably related to DSI spec stating
* that the DSI host should transition to LP at least once per frame.
*/
- r = _dsi_send_nop(dsi, VC_CMD, dsi->dsidev->channel);
+ r = dsi_vc_send_bta_sync(dssdev, VC_CMD);
if (r < 0) {
- DSSWARN("failed to send nop between frames: %d\n", r);
+ DSSWARN("failed to send bta sync between frames: %d\n", r);
goto err;
}
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/hdmi.h
index c4a4e07f0b99..a79d4590611f 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi.h
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h
@@ -15,6 +15,7 @@
#include <sound/omap-hdmi-audio.h>
#include <media/cec.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
#include "omapdss.h"
#include "dss.h"
@@ -368,6 +369,8 @@ struct omap_hdmi {
struct drm_bridge bridge;
struct platform_device *audio_pdev;
+ struct mutex audio_lock;
+
void (*audio_abort_cb)(struct device *dev);
int wp_idlemode;
@@ -380,6 +383,9 @@ struct omap_hdmi {
bool display_enabled;
};
+void hdmi_audio_hpd_notify(struct omap_hdmi *hdmi,
+ enum drm_connector_status status);
+
#define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge)
#endif
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 61cfd003569c..bd6c4d2dcdc8 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -433,8 +433,15 @@ static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
- if (status == connector_status_disconnected)
+ hdmi_audio_hpd_notify(hdmi, status);
+
+ if (status == connector_status_disconnected) {
+ if (hdmi_runtime_get(hdmi))
+ return;
+
hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
+ hdmi_runtime_put(hdmi);
+ }
}
static const struct drm_edid *hdmi4_bridge_edid_read(struct drm_bridge *bridge,
@@ -626,12 +633,17 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi)
.ops = &hdmi_audio_ops,
};
+ guard(mutex)(&hdmi->audio_lock);
hdmi->audio_pdev = platform_device_register_data(
&hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
&pdata, sizeof(pdata));
- if (IS_ERR(hdmi->audio_pdev))
- return PTR_ERR(hdmi->audio_pdev);
+ if (IS_ERR(hdmi->audio_pdev)) {
+ int err = PTR_ERR(hdmi->audio_pdev);
+
+ hdmi->audio_pdev = NULL;
+ return err;
+ }
return 0;
}
@@ -688,8 +700,14 @@ static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
dss_debugfs_remove_file(hdmi->debugfs);
- if (hdmi->audio_pdev)
- platform_device_unregister(hdmi->audio_pdev);
+ scoped_guard(mutex, &hdmi->audio_lock) {
+ if (hdmi->audio_pdev) {
+ struct platform_device *pdev = hdmi->audio_pdev;
+
+ hdmi->audio_pdev = NULL;
+ platform_device_unregister(pdev);
+ }
+ }
hdmi4_cec_uninit(&hdmi->core);
hdmi_pll_uninit(&hdmi->pll);
@@ -770,6 +788,7 @@ static int hdmi4_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, hdmi);
mutex_init(&hdmi->lock);
+ mutex_init(&hdmi->audio_lock);
spin_lock_init(&hdmi->audio_playing_lock);
r = hdmi4_probe_of(hdmi);
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index 1ee3bbe20583..c542ba6ce7d3 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -425,6 +425,15 @@ static void hdmi5_bridge_disable(struct drm_bridge *bridge,
mutex_unlock(&hdmi->lock);
}
+static void hdmi5_bridge_hpd_notify(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+ hdmi_audio_hpd_notify(hdmi, status);
+}
+
static const struct drm_edid *hdmi5_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
@@ -475,6 +484,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
.atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = hdmi5_bridge_enable,
.atomic_disable = hdmi5_bridge_disable,
+ .hpd_notify = hdmi5_bridge_hpd_notify,
.edid_read = hdmi5_bridge_edid_read,
};
@@ -601,12 +611,18 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi)
.ops = &hdmi_audio_ops,
};
- hdmi->audio_pdev = platform_device_register_data(
- &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
- &pdata, sizeof(pdata));
+ scoped_guard(mutex, &hdmi->audio_lock) {
+ hdmi->audio_pdev = platform_device_register_data(
+ &hdmi->pdev->dev, "omap-hdmi-audio",
+ PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata));
- if (IS_ERR(hdmi->audio_pdev))
- return PTR_ERR(hdmi->audio_pdev);
+ if (IS_ERR(hdmi->audio_pdev)) {
+ int err = PTR_ERR(hdmi->audio_pdev);
+
+ hdmi->audio_pdev = NULL;
+ return err;
+ }
+ }
hdmi_runtime_get(hdmi);
hdmi->wp_idlemode =
@@ -654,8 +670,14 @@ static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
dss_debugfs_remove_file(hdmi->debugfs);
- if (hdmi->audio_pdev)
- platform_device_unregister(hdmi->audio_pdev);
+ scoped_guard(mutex, &hdmi->audio_lock) {
+ if (hdmi->audio_pdev) {
+ struct platform_device *pdev = hdmi->audio_pdev;
+
+ hdmi->audio_pdev = NULL;
+ platform_device_unregister(pdev);
+ }
+ }
hdmi_pll_uninit(&hdmi->pll);
}
@@ -735,6 +757,7 @@ static int hdmi5_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, hdmi);
mutex_init(&hdmi->lock);
+ mutex_init(&hdmi->audio_lock);
spin_lock_init(&hdmi->audio_playing_lock);
r = hdmi5_probe_of(hdmi);
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c
index 3ecde23ac604..e9adc5775913 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c
@@ -147,3 +147,17 @@ int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts)
return 0;
}
+
+void hdmi_audio_hpd_notify(struct omap_hdmi *hdmi,
+ enum drm_connector_status status)
+{
+ guard(mutex)(&hdmi->audio_lock);
+
+ if (hdmi->audio_pdev) {
+ struct device *dev = &hdmi->audio_pdev->dev;
+ struct omap_hdmi_audio_pdata *ha = dev_get_platdata(dev);
+
+ if (ha->audio_hpd)
+ ha->audio_hpd(dev, status == connector_status_connected);
+ }
+}
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index c1b41e010a00..a9f6a76ebb5e 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -707,18 +707,17 @@ static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
return 0;
}
-static void omap_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *omap_crtc_create_state(struct drm_crtc *crtc)
{
struct omap_crtc_state *state;
- if (crtc->state)
- __drm_atomic_helper_crtc_destroy_state(crtc->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- kfree(crtc->state);
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
- state = kzalloc_obj(*state);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ return &state->base;
}
static struct drm_crtc_state *
@@ -745,7 +744,7 @@ omap_crtc_duplicate_state(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs omap_crtc_funcs = {
- .reset = omap_crtc_reset,
+ .atomic_create_state = omap_crtc_create_state,
.set_config = drm_atomic_helper_set_config,
.destroy = omap_crtc_destroy,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index cbdf7b8f7f7a..747f47347521 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -298,6 +298,17 @@ config DRM_PANEL_ILITEK_ILI7807S
If M is selected the module will be called panel-ilitek-ili7807s.
+config DRM_PANEL_ILITEK_ILI7836A
+ tristate "Ilitek ILI7836A-based panels"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y if you want to enable support for panels based on the
+ Ilitek ILI7836A DDIC.
+
+ If M is selected the module will be called panel-ilitek-ili7836a.
+
config DRM_PANEL_ILITEK_IL9322
tristate "Ilitek ILI9322 320x240 QVGA panels"
depends on OF && SPI
@@ -664,6 +675,20 @@ config DRM_PANEL_NOVATEK_NT36523
around the Novatek NT36523 display controller, such as some
Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets.
+config DRM_PANEL_NOVATEK_NT36532
+ tristate "Novatek NT36532-based MIPI-DSI panels"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_DSC_HELPER
+ select DRM_DISPLAY_HELPER
+ select DRM_KMS_HELPER
+ help
+ Say Y here if you want to enable support for the panels built
+ around the Novatek NT36532 display controller, such as some
+ CSOT panels used in OnePlus Pad 2 tablets. These panels are
+ typically dual-DSI and may use DSC (Display Stream Compression).
+
config DRM_PANEL_NOVATEK_NT36536
tristate "Novatek NT36536 panel driver"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 3b523cf37833..f2c9c80a218f 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o
+obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7836A) += panel-ilitek-ili7836a.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9488) += panel-ilitek-ili9488.o
@@ -64,6 +65,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35532) += panel-novatek-nt35532.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36532) += panel-novatek-nt36532.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36536) += panel-novatek-nt36536.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 2b455369ed70..bfb49b647b96 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -2060,6 +2060,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0af2, &delay_200_500_e50_p2e80, "NE140QDM-NX2"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b1e, &delay_200_500_e80, "NE140QDM-N6A"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b21, &delay_200_500_e80, "QV116FHB-N81"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80_d50, "NV122WUM-N41"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b43, &delay_200_500_e200, "NV140FHM-T09"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"),
@@ -2143,6 +2144,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x147c, &delay_200_500_e50_d100, "MNE007QB3-1"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1486, &delay_200_500_e50_d100, "MNE001BS6-2"),
+ EDP_PANEL_ENTRY('C', 'S', 'W', 0x1487, &delay_200_500_e50, "MNE007QS3-F"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1529, &delay_200_500_e80_d50, "MNF307QS3-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x153f, &delay_200_500_e200_d50, "MNF601BS4-1"),
@@ -2203,6 +2205,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('T', 'M', 'A', 0x0811, &delay_200_500_e80_d50, "TM140VDXP01-04"),
EDP_PANEL_ENTRY('T', 'M', 'A', 0x2094, &delay_200_500_e50_d100, "TL140VDMS03-01"),
+ EDP_PANEL_ENTRY('T', 'M', 'A', 0x2137, &delay_200_500_e50, "TM140VDXP15"),
EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e80_pu100, "TM156VDXP25"),
{ /* sentinal */ }
diff --git a/drivers/gpu/drm/panel/panel-himax-hx83102.c b/drivers/gpu/drm/panel/panel-himax-hx83102.c
index 6a0851ccf9bb..3ef052571ce0 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx83102.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx83102.c
@@ -1284,9 +1284,7 @@ static int hx83102_panel_add(struct hx83102 *ctx)
ctx->base.funcs = &hx83102_drm_funcs;
ctx->base.dev = &ctx->dsi->dev;
- drm_panel_add(&ctx->base);
-
- return 0;
+ return devm_drm_panel_add(dev, &ctx->base);
}
static int hx83102_probe(struct mipi_dsi_device *dsi)
@@ -1318,24 +1316,7 @@ static int hx83102_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, ctx);
- ret = mipi_dsi_attach(dsi);
- if (ret)
- drm_panel_remove(&ctx->base);
-
- return ret;
-}
-
-static void hx83102_remove(struct mipi_dsi_device *dsi)
-{
- struct hx83102 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
-
- if (ctx->base.dev)
- drm_panel_remove(&ctx->base);
+ return devm_mipi_dsi_attach(&dsi->dev, dsi);
}
static const struct of_device_id hx83102_of_match[] = {
@@ -1369,7 +1350,6 @@ MODULE_DEVICE_TABLE(of, hx83102_of_match);
static struct mipi_dsi_driver hx83102_driver = {
.probe = hx83102_probe,
- .remove = hx83102_remove,
.driver = {
.name = "panel-himax-hx83102",
.of_match_table = hx83102_of_match,
diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112a.c b/drivers/gpu/drm/panel/panel-himax-hx83112a.c
index 142cb1cc067a..36ebfad7a3e9 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx83112a.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx83112a.c
@@ -303,28 +303,15 @@ static int hx83112a_probe(struct mipi_dsi_device *dsi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
- drm_panel_add(&ctx->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
- drm_panel_remove(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
return ret;
- }
- return 0;
-}
-
-static void hx83112a_remove(struct mipi_dsi_device *dsi)
-{
- struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+ return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
- drm_panel_remove(&ctx->panel);
+ return 0;
}
static const struct of_device_id hx83112a_of_match[] = {
@@ -335,7 +322,6 @@ MODULE_DEVICE_TABLE(of, hx83112a_of_match);
static struct mipi_dsi_driver hx83112a_driver = {
.probe = hx83112a_probe,
- .remove = hx83112a_remove,
.driver = {
.name = "panel-himax-hx83112a",
.of_match_table = hx83112a_of_match,
diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112b.c b/drivers/gpu/drm/panel/panel-himax-hx83112b.c
index 41f21f8c1373..569ed2dec87c 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx83112b.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx83112b.c
@@ -386,29 +386,17 @@ static int hx83112b_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- drm_panel_remove(&ctx->panel);
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
- }
return 0;
}
-static void hx83112b_remove(struct mipi_dsi_device *dsi)
-{
- struct hx83112b_panel *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id hx83112b_of_match[] = {
{ .compatible = "djn,98-03057-6598b-i" },
{ /* sentinel */ }
@@ -417,7 +405,6 @@ MODULE_DEVICE_TABLE(of, hx83112b_of_match);
static struct mipi_dsi_driver hx83112b_driver = {
.probe = hx83112b_probe,
- .remove = hx83112b_remove,
.driver = {
.name = "panel-himax-hx83112b",
.of_match_table = hx83112b_of_match,
diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c
index 09124610fdc8..4180d4aadf5e 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx8394.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c
@@ -1023,14 +1023,13 @@ static int hx8394_probe(struct mipi_dsi_device *dsi)
ctx->panel.prepare_prev_first = true;
- drm_panel_add(&ctx->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- dev_err_probe(dev, ret, "mipi_dsi_attach failed\n");
- drm_panel_remove(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
return ret;
- }
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "mipi_dsi_attach failed\n");
dev_dbg(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
@@ -1040,18 +1039,6 @@ static int hx8394_probe(struct mipi_dsi_device *dsi)
return 0;
}
-static void hx8394_remove(struct mipi_dsi_device *dsi)
-{
- struct hx8394 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id hx8394_of_match[] = {
{ .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc },
{ .compatible = "huiling,hl055fhav028c", .data = &hl055fhav028c_desc },
@@ -1065,7 +1052,6 @@ MODULE_DEVICE_TABLE(of, hx8394_of_match);
static struct mipi_dsi_driver hx8394_driver = {
.probe = hx8394_probe,
- .remove = hx8394_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = hx8394_of_match,
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili7836a.c b/drivers/gpu/drm/panel/panel-ilitek-ili7836a.c
new file mode 100644
index 000000000000..f6331129dadf
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili7836a.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include <video/mipi_display.h>
+
+static const struct regulator_bulk_data ili7836a_supplies[] = {
+ { .supply = "vddio", },
+ { .supply = "avdd", },
+};
+
+struct ili7836a_panel {
+ struct drm_panel panel;
+ struct drm_connector *connector;
+ struct mipi_dsi_device *dsi;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+ struct ili7836a_desc *desc;
+ enum drm_panel_orientation orientation;
+};
+
+struct ili7836a_desc {
+ unsigned int width_mm;
+ unsigned int height_mm;
+ unsigned int bpc;
+
+ const struct drm_display_mode *modes;
+ unsigned int num_modes;
+};
+
+static inline struct ili7836a_panel *to_ili7836a_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct ili7836a_panel, panel);
+}
+
+static void ili7836a_reset(struct ili7836a_panel *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ msleep(20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ msleep(20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ msleep(20);
+}
+
+static int ili7836a_on(struct ili7836a_panel *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc8, 0x62);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x21);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa4, 0x38);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x15);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x20);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x22);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x01);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0xa5, 0x00);
+
+ return dsi_ctx.accum_err;
+}
+
+static int ili7836a_disable(struct drm_panel *panel)
+{
+ struct ili7836a_panel *ctx = to_ili7836a_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ return dsi_ctx.accum_err;
+}
+
+static int ili7836a_prepare(struct drm_panel *panel)
+{
+ struct ili7836a_panel *ctx = to_ili7836a_panel(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ili7836a_supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ili7836a_reset(ctx);
+
+ ret = ili7836a_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(ili7836a_supplies), ctx->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ili7836a_unprepare(struct drm_panel *panel)
+{
+ struct ili7836a_panel *ctx = to_ili7836a_panel(panel);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(ili7836a_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static const struct drm_display_mode nova_modes[] = {
+ {
+ /* 120Hz */
+ .clock = (1280 + 12 + 2 + 8) * (960 + 12 + 2 + 24) * 120 / 1000,
+ .hdisplay = 1280,
+ .hsync_start = 1280 + 12,
+ .hsync_end = 1280 + 12 + 2,
+ .htotal = 1280 + 12 + 2 + 8,
+ .vdisplay = 960,
+ .vsync_start = 960 + 12,
+ .vsync_end = 960 + 12 + 2,
+ .vtotal = 960 + 12 + 2 + 24,
+ }
+};
+
+static struct ili7836a_desc nova_desc = {
+ .modes = nova_modes,
+ .num_modes = ARRAY_SIZE(nova_modes),
+ .width_mm = 91,
+ .height_mm = 68,
+ .bpc = 8,
+};
+
+static int ili7836a_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ili7836a_panel *ctx = to_ili7836a_panel(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector, ctx->desc->modes);
+}
+
+static enum drm_panel_orientation ili7836a_get_orientation(struct drm_panel *panel)
+{
+ struct ili7836a_panel *ctx = to_ili7836a_panel(panel);
+
+ return ctx->orientation;
+}
+
+static const struct drm_panel_funcs ili7836a_panel_funcs = {
+ .prepare = ili7836a_prepare,
+ .unprepare = ili7836a_unprepare,
+ .disable = ili7836a_disable,
+ .get_modes = ili7836a_get_modes,
+ .get_orientation = ili7836a_get_orientation,
+};
+
+static int ili7836a_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static const struct backlight_ops ili7836a_bl_ops = {
+ .update_status = ili7836a_bl_update_status,
+};
+
+static struct backlight_device *
+ili7836a_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 3445,
+ .max_brightness = 3445,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &ili7836a_bl_ops, &props);
+}
+
+static int ili7836a_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct ili7836a_panel *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, __typeof(*ctx), panel,
+ &ili7836a_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->desc = (struct ili7836a_desc *)of_device_get_match_data(dev);
+ if (!ctx->desc)
+ return -ENODEV;
+
+ ret = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(ili7836a_supplies),
+ ili7836a_supplies,
+ &ctx->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
+ if (ret < 0) {
+ dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
+ return ret;
+ }
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE |
+ MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ctx->panel.backlight = ili7836a_create_backlight(dsi);
+ if (IS_ERR(ctx->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
+ "Failed to create backlight\n");
+
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
+
+ return devm_mipi_dsi_attach(dev, dsi);
+}
+
+static const struct of_device_id ili7836a_of_match[] = {
+ { .compatible = "retroidpocket,nova-panel", .data = &nova_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ili7836a_of_match);
+
+static struct mipi_dsi_driver ili7836a_driver = {
+ .probe = ili7836a_probe,
+ .driver = {
+ .name = "panel-ili7836a-amoled",
+ .of_match_table = ili7836a_of_match,
+ },
+};
+module_mipi_dsi_driver(ili7836a_driver);
+
+MODULE_DESCRIPTION("DRM driver for ILI7836A DSI panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c
index 5610a225f77b..976305991b7f 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c
@@ -305,31 +305,19 @@ static int ili9805_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(&dsi->dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(&dsi->dev, dsi);
if (ret < 0) {
dev_err(&dsi->dev, "mipi_dsi_attach failed: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void ili9805_dsi_remove(struct mipi_dsi_device *dsi)
-{
- struct ili9805 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
- ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct ili9805_desc gpm1780a0_desc = {
.init = gpm1780a0_init,
.mode = &gpm1780a0_timing,
@@ -353,7 +341,6 @@ MODULE_DEVICE_TABLE(of, ili9805_of_match);
static struct mipi_dsi_driver ili9805_dsi_driver = {
.probe = ili9805_dsi_probe,
- .remove = ili9805_dsi_remove,
.driver = {
.name = "ili9805-dsi",
.of_match_table = ili9805_of_match,
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
index 53e25d1086db..81d48c402aec 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
@@ -112,21 +112,11 @@ int ili9806e_probe(struct device *dev, void *transport,
if (set_prepare_prev_first)
ctx->panel.prepare_prev_first = true;
- drm_panel_add(&ctx->panel);
-
- return 0;
+ return devm_drm_panel_add(dev, &ctx->panel);
}
EXPORT_SYMBOL_GPL(ili9806e_probe);
-void ili9806e_remove(struct device *dev)
-{
- struct ili9806e *ctx = dev_get_drvdata(dev);
-
- drm_panel_remove(&ctx->panel);
-}
-EXPORT_SYMBOL_GPL(ili9806e_remove);
-
MODULE_AUTHOR("Dario Binacchi <dario.binacchi@amarulasolutions.com>");
MODULE_AUTHOR("Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>");
MODULE_AUTHOR("Michael Walle <mwalle@kernel.org>");
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h
index dddece62cf42..fded4b6793e7 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h
@@ -10,6 +10,5 @@ int ili9806e_power_on(struct device *dev);
int ili9806e_probe(struct device *dev, void *transport,
const struct drm_panel_funcs *funcs,
int connector_type);
-void ili9806e_remove(struct device *dev);
#endif /* _PANEL_ILITEK_ILI9806E_CORE_H */
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
index 0fb06c425545..89915d58f69d 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
@@ -141,20 +141,7 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
- ili9806e_remove(dev);
- return ret;
- }
-
- return 0;
-}
-
-static void ili9806e_dsi_remove(struct mipi_dsi_device *dsi)
-{
- mipi_dsi_detach(dsi);
- ili9806e_remove(&dsi->dev);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static void com35h3p70ulc_init(struct mipi_dsi_multi_context *ctx)
@@ -490,7 +477,6 @@ static struct mipi_dsi_driver ili9806e_dsi_driver = {
.of_match_table = ili9806e_dsi_of_match,
},
.probe = ili9806e_dsi_probe,
- .remove = ili9806e_dsi_remove,
};
module_mipi_dsi_driver(ili9806e_dsi_driver);
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
index 9d10b0d28f52..187bcb084dd9 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
@@ -124,11 +124,6 @@ static int ili9806e_spi_probe(struct spi_device *spi)
DRM_MODE_CONNECTOR_DPI);
}
-static void ili9806e_spi_remove(struct spi_device *spi)
-{
- ili9806e_remove(&spi->dev);
-}
-
static void rk050hr345_ct106a_init(struct ili9806e_spi_panel *ctx)
{
struct mipi_dbi *dbi = &ctx->dbi;
@@ -313,7 +308,6 @@ static struct spi_driver ili9806e_spi_driver = {
.of_match_table = ili9806e_spi_of_match,
},
.probe = ili9806e_spi_probe,
- .remove = ili9806e_spi_remove,
.id_table = ili9806e_spi_ids,
};
module_spi_driver(ili9806e_spi_driver);
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
index c3d5ab67121d..7bf38e96678a 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
@@ -2594,21 +2594,15 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(&dsi->dev, &ctx->panel);
+ if (ret)
+ return ret;
dsi->mode_flags = ctx->desc->mode_flags;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = ctx->desc->lanes;
- return mipi_dsi_attach(dsi);
-}
-
-static void ili9881c_dsi_remove(struct mipi_dsi_device *dsi)
-{
- struct ili9881c *ctx = mipi_dsi_get_drvdata(dsi);
-
- mipi_dsi_detach(dsi);
- drm_panel_remove(&ctx->panel);
+ return devm_mipi_dsi_attach(&dsi->dev, dsi);
}
static const struct ili9881c_desc lhr050h41_desc = {
@@ -2722,7 +2716,6 @@ MODULE_DEVICE_TABLE(of, ili9881c_of_match);
static struct mipi_dsi_driver ili9881c_dsi_driver = {
.probe = ili9881c_dsi_probe,
- .remove = ili9881c_dsi_remove,
.driver = {
.name = "ili9881c-dsi",
.of_match_table = ili9881c_of_match,
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c
index 6d07fe901357..d4b383138488 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c
@@ -809,9 +809,7 @@ static int ili9882t_add(struct ili9882t *ili)
ili->base.funcs = &ili9882t_funcs;
ili->base.dev = &ili->dsi->dev;
- drm_panel_add(&ili->base);
-
- return 0;
+ return devm_drm_panel_add(dev, &ili->base);
}
static int ili9882t_probe(struct mipi_dsi_device *dsi)
@@ -844,24 +842,7 @@ static int ili9882t_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, ili);
- ret = mipi_dsi_attach(dsi);
- if (ret)
- drm_panel_remove(&ili->base);
-
- return ret;
-}
-
-static void ili9882t_remove(struct mipi_dsi_device *dsi)
-{
- struct ili9882t *ili = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
-
- if (ili->base.dev)
- drm_panel_remove(&ili->base);
+ return devm_mipi_dsi_attach(&dsi->dev, dsi);
}
static const struct of_device_id ili9882t_of_match[] = {
@@ -881,7 +862,6 @@ static struct mipi_dsi_driver ili9882t_driver = {
.of_match_table = ili9882t_of_match,
},
.probe = ili9882t_probe,
- .remove = ili9882t_remove,
};
module_mipi_dsi_driver(ili9882t_driver);
diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c
index cbe354b51bce..caf18d309849 100644
--- a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c
+++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c
@@ -200,9 +200,11 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
return ret;
@@ -211,18 +213,6 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi)
return 0;
}
-static void jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi)
-{
- struct jdi_fhd_r63452 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id jdi_fhd_r63452_of_match[] = {
{ .compatible = "jdi,fhd-r63452" },
{ /* sentinel */ }
@@ -231,7 +221,6 @@ MODULE_DEVICE_TABLE(of, jdi_fhd_r63452_of_match);
static struct mipi_dsi_driver jdi_fhd_r63452_driver = {
.probe = jdi_fhd_r63452_probe,
- .remove = jdi_fhd_r63452_remove,
.driver = {
.name = "panel-jdi-fhd-r63452",
.of_match_table = jdi_fhd_r63452_of_match,
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
index 01bd748aecec..e91b41743bec 100644
--- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -345,15 +345,7 @@ static int jdi_panel_add(struct jdi_panel *jdi)
return dev_err_probe(dev, PTR_ERR(jdi->backlight),
"failed to register backlight %d\n", ret);
- drm_panel_add(&jdi->base);
-
- return 0;
-}
-
-static void jdi_panel_del(struct jdi_panel *jdi)
-{
- if (jdi->base.dev)
- drm_panel_remove(&jdi->base);
+ return devm_drm_panel_add(dev, &jdi->base);
}
static int jdi_panel_probe(struct mipi_dsi_device *dsi)
@@ -380,26 +372,7 @@ static int jdi_panel_probe(struct mipi_dsi_device *dsi)
if (ret < 0)
return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- jdi_panel_del(jdi);
- return ret;
- }
-
- return 0;
-}
-
-static void jdi_panel_remove(struct mipi_dsi_device *dsi)
-{
- struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
- ret);
-
- jdi_panel_del(jdi);
+ return devm_mipi_dsi_attach(&dsi->dev, dsi);
}
static struct mipi_dsi_driver jdi_panel_driver = {
@@ -408,7 +381,6 @@ static struct mipi_dsi_driver jdi_panel_driver = {
.of_match_table = jdi_of_match,
},
.probe = jdi_panel_probe,
- .remove = jdi_panel_remove,
};
module_mipi_dsi_driver(jdi_panel_driver);
diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
index 0856df5a6ee2..8a02e92061f5 100644
--- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
+++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
@@ -583,30 +583,19 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
-{
- struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id ltk050h3146w_of_match[] = {
{
.compatible = "leadtek,ltk050h3146w",
@@ -630,7 +619,6 @@ static struct mipi_dsi_driver ltk050h3146w_driver = {
.of_match_table = ltk050h3146w_of_match,
},
.probe = ltk050h3146w_probe,
- .remove = ltk050h3146w_remove,
};
module_mipi_dsi_driver(ltk050h3146w_driver);
diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
index 7f19fd5b8060..ff35b62a327e 100644
--- a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
+++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
@@ -649,30 +649,19 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void ltk500hd1829_remove(struct mipi_dsi_device *dsi)
-{
- struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id ltk500hd1829_of_match[] = {
{
.compatible = "leadtek,ltk101b4029w",
@@ -692,7 +681,6 @@ static struct mipi_dsi_driver ltk500hd1829_driver = {
.of_match_table = ltk500hd1829_of_match,
},
.probe = ltk500hd1829_probe,
- .remove = ltk500hd1829_remove,
};
module_mipi_dsi_driver(ltk500hd1829_driver);
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36532.c b/drivers/gpu/drm/panel/panel-novatek-nt36532.c
new file mode 100644
index 000000000000..41d2721e0513
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36532.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Novatek NT36532 DriverIC panels driver
+ * Based on the template generated by linux-mdss-dsi-panel-driver-generator
+ *
+ * Copyright (c) 2025, 2026 Junjie Cao <junjie.cao@linux.dev>
+ */
+
+#include <linux/delay.h>
+#include <linux/device-id/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dsc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct nt36532 {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi[2];
+ const struct panel_info *panel_info;
+ struct drm_dsc_config dsc;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *supplies;
+};
+
+struct panel_info {
+ unsigned int lanes;
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+
+ const struct drm_display_mode display_mode;
+
+ const struct drm_dsc_config *dsc_cfg;
+
+ int (*init_sequence)(struct nt36532 *ctx);
+
+ bool is_dual_dsi;
+};
+
+static const struct regulator_bulk_data nt36532_supplies[] = {
+ { .supply = "vddio" }, /* 1.8v */
+};
+
+static inline struct nt36532 *to_nt36532(struct drm_panel *panel)
+{
+ return container_of(panel, struct nt36532, panel);
+}
+
+static inline struct mipi_dsi_device *to_primary_dsi(struct nt36532 *ctx)
+{
+ /* Sync on DSI1 for dual dsi */
+ return ctx->panel_info->is_dual_dsi ? ctx->dsi[1] : ctx->dsi[0];
+}
+
+static void nt36532_reset(struct nt36532 *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 10100);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(5000, 5100);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(15000, 15100);
+}
+
+static int nt36532_prepare(struct drm_panel *panel)
+{
+ struct nt36532 *ctx = to_nt36532(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(nt36532_supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ nt36532_reset(ctx);
+
+ ret = ctx->panel_info->init_sequence(ctx);
+ if (ret < 0) {
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(nt36532_supplies),
+ ctx->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nt36532_off(struct nt36532 *ctx)
+{
+ struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 10100);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 65);
+
+ return dsi_ctx.accum_err;
+}
+
+static int nt36532_unprepare(struct drm_panel *panel)
+{
+ struct nt36532 *ctx = to_nt36532(panel);
+ struct device *dev = &ctx->dsi[0]->dev;
+ int ret;
+
+ ret = nt36532_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(nt36532_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static int nt36532_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct nt36532 *ctx = to_nt36532(panel);
+ const struct panel_info *panel_info = ctx->panel_info;
+
+ return drm_connector_helper_get_modes_fixed(connector,
+ &panel_info->display_mode);
+}
+
+static const struct drm_panel_funcs nt36532_panel_funcs = {
+ .prepare = nt36532_prepare,
+ .unprepare = nt36532_unprepare,
+ .get_modes = nt36532_get_modes,
+};
+
+static int csot_init_sequence(struct nt36532 *ctx)
+{
+ struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
+ struct drm_dsc_picture_parameter_set pps;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x22);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x24);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc,
+ 0x00, 0x00, 0x03, 0x22, 0x00, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x60);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x0c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0xe1);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0xa9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x16);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x8e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x8e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x8e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x0c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x1c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x24);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x2c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x34);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x3c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0xfd);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0xfc);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0xfa);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0xf8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0xf6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0xf4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0xf2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0xee);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0xec);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0xea);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xe9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xe8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0xe7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xe6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0xf9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0xf6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0xf2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0xec);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0xe8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0xe4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0xe0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0xde);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0xd9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0xd6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0xd4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0xc2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0xcd);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xf6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0xeb);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0xe8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0xe5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0xe3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0xe0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0xde);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0xda);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0xd7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0xd4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0xd2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0xcc);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xc8);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x27);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x2a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd0, 0x31);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd1, 0x54);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xde, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x16);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0xaa, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbb, 0xa0, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x27);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x8c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+ 0x07, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ 0x2c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b,
+ 0x03, 0xd2, 0x1a, 0x04, 0x04, 0x00);
+
+ /* Enable DSC */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x03);
+ drm_dsc_pps_payload_pack(&pps, &ctx->dsc);
+ mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9d, 0x01);
+
+ /* Program refresh rate control registers */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x91);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x40);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 10100);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct drm_display_mode csot_display_mode = {
+ /* 120Hz, the typical value */
+ .clock = (1500 + 118 + 20 + 100) * 2 * (2120 + 26 + 2 + 208) * 120 / 1000,
+ .hdisplay = 1500 * 2,
+ .hsync_start = (1500 + 118) * 2,
+ .hsync_end = (1500 + 118 + 20) * 2,
+ .htotal = (1500 + 118 + 20 + 100) * 2,
+ .vdisplay = 2120,
+ .vsync_start = 2120 + 26,
+ .vsync_end = 2120 + 26 + 2,
+ .vtotal = 2120 + 26 + 2 + 208,
+ .width_mm = 250,
+ .height_mm = 177,
+};
+
+static const struct drm_dsc_config csot_dsc_cfg = {
+ .dsc_version_major = 1,
+ .dsc_version_minor = 2,
+ .slice_height = 20,
+ .slice_width = 750,
+ .slice_count = 2,
+ .bits_per_component = 8,
+ .bits_per_pixel = 8 << 4,
+ .block_pred_enable = true,
+};
+
+static int nt36532_probe(struct mipi_dsi_device *dsi)
+{
+ struct mipi_dsi_device_info dsi_info = {"nt36532-secondary", 0, NULL};
+ const struct panel_info *panel_info;
+ struct mipi_dsi_host *dsi1_host;
+ struct device *dev = &dsi->dev;
+ struct device_node *dsi1;
+ struct nt36532 *ctx;
+ int num_dsi = 1;
+ int ret, i;
+
+ ctx = devm_drm_panel_alloc(dev, struct nt36532, panel,
+ &nt36532_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = devm_regulator_bulk_get_const(&dsi->dev,
+ ARRAY_SIZE(nt36532_supplies),
+ nt36532_supplies, &ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->panel_info = of_device_get_match_data(dev);
+ panel_info = ctx->panel_info;
+ if (!panel_info)
+ return -ENODEV;
+
+ ctx->dsc = *panel_info->dsc_cfg;
+
+ if (panel_info->is_dual_dsi) {
+ num_dsi = 2;
+ dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
+ if (!dsi1) {
+ dev_err(dev, "cannot get secondary DSI node.\n");
+ return -ENODEV;
+ }
+
+ dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
+ of_node_put(dsi1);
+ if (!dsi1_host)
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "cannot get secondary DSI host\n");
+
+ ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host,
+ &dsi_info);
+ if (IS_ERR(ctx->dsi[1])) {
+ dev_err(dev, "cannot get secondary DSI device\n");
+ return PTR_ERR(ctx->dsi[1]);
+ }
+
+ mipi_dsi_set_drvdata(ctx->dsi[1], ctx);
+ }
+
+ ctx->dsi[0] = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ ctx->panel.prepare_prev_first = true;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add panel\n");
+
+ for (i = 0; i < num_dsi; i++) {
+ ctx->dsi[i]->lanes = panel_info->lanes;
+ ctx->dsi[i]->format = panel_info->format;
+ ctx->dsi[i]->mode_flags = panel_info->mode_flags;
+ ctx->dsi[i]->dsc = &ctx->dsc;
+ ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
+ if (ret < 0) {
+ return dev_err_probe(dev, ret,
+ "Failed to attach to DSI host\n");
+ }
+ }
+
+ return 0;
+}
+
+static const struct panel_info csot_panel_info = {
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT,
+ .display_mode = csot_display_mode,
+ .dsc_cfg = &csot_dsc_cfg,
+ .init_sequence = csot_init_sequence,
+ .is_dual_dsi = true,
+};
+
+static const struct of_device_id nt36532_of_match[] = {
+ { .compatible = "csot,ppc100hb1-1", .data = &csot_panel_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nt36532_of_match);
+
+static struct mipi_dsi_driver nt36532_driver = {
+ .probe = nt36532_probe,
+ .driver = {
+ .name = "panel-novatek-nt36532",
+ .of_match_table = nt36532_of_match,
+ },
+};
+module_mipi_dsi_driver(nt36532_driver);
+
+MODULE_AUTHOR("Junjie Cao <junjie.cao@linux.dev>");
+MODULE_DESCRIPTION("DRM driver for Novatek NT36532 based MIPI DSI panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
index 54a65abf7e89..e6ad1e876070 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
@@ -48,14 +48,10 @@ static int s6d16d0_unprepare(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- /* Enter sleep mode */
- ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
- if (ret) {
- dev_err(s6->dev, "failed to enter sleep mode (%d)\n", ret);
- return ret;
- }
+ /* Enter sleep mode; error does not prevent power-off */
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
/* Assert RESET */
gpiod_set_value_cansleep(s6->reset_gpio, 1);
@@ -68,6 +64,7 @@ static int s6d16d0_prepare(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
int ret;
ret = regulator_enable(s6->supply);
@@ -83,57 +80,39 @@ static int s6d16d0_prepare(struct drm_panel *panel)
gpiod_set_value_cansleep(s6->reset_gpio, 0);
msleep(120);
- /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
- ret = mipi_dsi_dcs_set_tear_on(dsi,
+ /* Enable tearing mode: send TE (tearing effect) at VBLANK */
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx,
MIPI_DSI_DCS_TEAR_MODE_VBLANK);
- if (ret) {
- dev_err(s6->dev, "failed to enable vblank TE (%d)\n", ret);
- goto err_power_off;
- }
/* Exit sleep mode and power on */
- ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
- if (ret) {
- dev_err(s6->dev, "failed to exit sleep mode (%d)\n", ret);
- goto err_power_off;
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ if (dsi_ctx.accum_err) {
+ gpiod_set_value_cansleep(s6->reset_gpio, 1);
+ regulator_disable(s6->supply);
}
- return 0;
-
-err_power_off:
- gpiod_set_value_cansleep(s6->reset_gpio, 1);
- regulator_disable(s6->supply);
-
- return ret;
+ return dsi_ctx.accum_err;
}
static int s6d16d0_enable(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- ret = mipi_dsi_dcs_set_display_on(dsi);
- if (ret) {
- dev_err(s6->dev, "failed to turn display on (%d)\n", ret);
- return ret;
- }
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
- return 0;
+ return dsi_ctx.accum_err;
}
static int s6d16d0_disable(struct drm_panel *panel)
{
struct s6d16d0 *s6 = panel_to_s6d16d0(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev);
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- ret = mipi_dsi_dcs_set_display_off(dsi);
- if (ret) {
- dev_err(s6->dev, "failed to turn display off (%d)\n", ret);
- return ret;
- }
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
- return 0;
+ return dsi_ctx.accum_err;
}
static int s6d16d0_get_modes(struct drm_panel *panel,
@@ -207,21 +186,11 @@ static int s6d16d0_probe(struct mipi_dsi_device *dsi)
return ret;
}
- drm_panel_add(&s6->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0)
- drm_panel_remove(&s6->panel);
-
- return ret;
-}
-
-static void s6d16d0_remove(struct mipi_dsi_device *dsi)
-{
- struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi);
+ ret = devm_drm_panel_add(dev, &s6->panel);
+ if (ret)
+ return ret;
- mipi_dsi_detach(dsi);
- drm_panel_remove(&s6->panel);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id s6d16d0_of_match[] = {
@@ -232,7 +201,6 @@ MODULE_DEVICE_TABLE(of, s6d16d0_of_match);
static struct mipi_dsi_driver s6d16d0_driver = {
.probe = s6d16d0_probe,
- .remove = s6d16d0_remove,
.driver = {
.name = "panel-samsung-s6d16d0",
.of_match_table = s6d16d0_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c
index 692020081524..5a4cfb21e2fd 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c
@@ -436,30 +436,19 @@ static int s6d7aa0_probe(struct mipi_dsi_device *dsi)
"Failed to create backlight\n");
}
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void s6d7aa0_remove(struct mipi_dsi_device *dsi)
-{
- struct s6d7aa0 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id s6d7aa0_of_match[] = {
{
.compatible = "samsung,lsl080al02",
@@ -479,7 +468,6 @@ MODULE_DEVICE_TABLE(of, s6d7aa0_of_match);
static struct mipi_dsi_driver s6d7aa0_driver = {
.probe = s6d7aa0_probe,
- .remove = s6d7aa0_remove,
.driver = {
.name = "panel-samsung-s6d7aa0",
.of_match_table = s6d7aa0_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c
index f4d75eca3cdf..9cd92d7a823b 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c
@@ -211,30 +211,19 @@ static int s6e3fa7_panel_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void s6e3fa7_panel_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e3fa7_panel *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id s6e3fa7_panel_of_match[] = {
{ .compatible = "samsung,s6e3fa7-ams559nk06" },
{ /* sentinel */ }
@@ -243,7 +232,6 @@ MODULE_DEVICE_TABLE(of, s6e3fa7_panel_of_match);
static struct mipi_dsi_driver s6e3fa7_panel_driver = {
.probe = s6e3fa7_panel_probe,
- .remove = s6e3fa7_panel_remove,
.driver = {
.name = "panel-samsung-s6e3fa7",
.of_match_table = s6e3fa7_panel_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
index e63080204af7..8dfe654f36ec 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
@@ -340,30 +340,19 @@ static int s6e3fc2x01_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void s6e3fc2x01_remove(struct mipi_dsi_device *dsi)
-{
- struct samsung_s6e3fc2x01 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id s6e3fc2x01_of_match[] = {
{ .compatible = "samsung,s6e3fc2x01-ams641rw", .data = &ams641rw_mode },
{ /* sentinel */ }
@@ -372,7 +361,6 @@ MODULE_DEVICE_TABLE(of, s6e3fc2x01_of_match);
static struct mipi_dsi_driver s6e3fc2x01_driver = {
.probe = s6e3fc2x01_probe,
- .remove = s6e3fc2x01_remove,
.driver = {
.name = "panel-samsung-s6e3fc2x01",
.of_match_table = s6e3fc2x01_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
index 1db0c63b1131..cede897ff69d 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
@@ -722,8 +722,8 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(ctx->enable_gpio);
}
- ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
- &s6e3ha2_bl_ops, NULL);
+ ctx->bl_dev = devm_backlight_device_register(dev, "s6e3ha2", dev, ctx,
+ &s6e3ha2_bl_ops, NULL);
if (IS_ERR(ctx->bl_dev)) {
dev_err(dev, "failed to register backlight device\n");
return PTR_ERR(ctx->bl_dev);
@@ -735,28 +735,11 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
ctx->panel.prepare_prev_first = true;
- drm_panel_add(&ctx->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0)
- goto remove_panel;
-
- return ret;
-
-remove_panel:
- drm_panel_remove(&ctx->panel);
- backlight_device_unregister(ctx->bl_dev);
-
- return ret;
-}
-
-static void s6e3ha2_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- mipi_dsi_detach(dsi);
- drm_panel_remove(&ctx->panel);
- backlight_device_unregister(ctx->bl_dev);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id s6e3ha2_of_match[] = {
@@ -768,7 +751,6 @@ MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
static struct mipi_dsi_driver s6e3ha2_driver = {
.probe = s6e3ha2_probe,
- .remove = s6e3ha2_remove,
.driver = {
.name = "panel-samsung-s6e3ha2",
.of_match_table = s6e3ha2_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
index 550e9ef9bb71..c09ff71b9c39 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
@@ -283,7 +283,9 @@ static int s6e3ha8_amb577px01_wqhd_probe(struct mipi_dsi_device *dsi)
priv->panel.prepare_prev_first = true;
- drm_panel_add(&priv->panel);
+ ret = devm_drm_panel_add(dev, &priv->panel);
+ if (ret)
+ return ret;
/* This panel only supports DSC; unconditionally enable it */
dsi->dsc = &priv->dsc;
@@ -299,28 +301,15 @@ static int s6e3ha8_amb577px01_wqhd_probe(struct mipi_dsi_device *dsi)
priv->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */
priv->dsc.block_pred_enable = true;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&priv->panel);
return ret;
}
return 0;
}
-static void s6e3ha8_amb577px01_wqhd_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e3ha8 *priv = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&priv->panel);
-}
-
static const struct of_device_id s6e3ha8_amb577px01_wqhd_of_match[] = {
{ .compatible = "samsung,s6e3ha8" },
{ /* sentinel */ }
@@ -329,7 +318,6 @@ MODULE_DEVICE_TABLE(of, s6e3ha8_amb577px01_wqhd_of_match);
static struct mipi_dsi_driver s6e3ha8_amb577px01_wqhd_driver = {
.probe = s6e3ha8_amb577px01_wqhd_probe,
- .remove = s6e3ha8_amb577px01_wqhd_remove,
.driver = {
.name = "panel-s6e3ha8",
.of_match_table = s6e3ha8_amb577px01_wqhd_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
index e05199ce14ee..1f825d995686 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
@@ -465,8 +465,8 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
ctx->panel.prepare_prev_first = true;
- ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
- &s6e63j0x03_bl_ops, NULL);
+ ctx->bl_dev = devm_backlight_device_register(dev, "s6e63j0x03", dev, ctx,
+ &s6e63j0x03_bl_ops, NULL);
if (IS_ERR(ctx->bl_dev))
return dev_err_probe(dev, PTR_ERR(ctx->bl_dev),
"failed to register backlight device\n");
@@ -475,29 +475,11 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
ctx->bl_dev->props.power = BACKLIGHT_POWER_OFF;
- drm_panel_add(&ctx->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0)
- goto remove_panel;
-
- return ret;
-
-remove_panel:
- drm_panel_remove(&ctx->panel);
- backlight_device_unregister(ctx->bl_dev);
-
- return ret;
-}
-
-static void s6e63j0x03_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
-
- mipi_dsi_detach(dsi);
- drm_panel_remove(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- backlight_device_unregister(ctx->bl_dev);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id s6e63j0x03_of_match[] = {
@@ -508,7 +490,6 @@ MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
static struct mipi_dsi_driver s6e63j0x03_driver = {
.probe = s6e63j0x03_probe,
- .remove = s6e63j0x03_remove,
.driver = {
.name = "panel_samsung_s6e63j0x03",
.of_match_table = s6e63j0x03_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
index 2630975c111b..c3842449c895 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
@@ -105,17 +105,7 @@ static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0)
- s6e63m0_remove(dev);
-
- return ret;
-}
-
-static void s6e63m0_dsi_remove(struct mipi_dsi_device *dsi)
-{
- mipi_dsi_detach(dsi);
- s6e63m0_remove(&dsi->dev);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id s6e63m0_dsi_of_match[] = {
@@ -126,7 +116,6 @@ MODULE_DEVICE_TABLE(of, s6e63m0_dsi_of_match);
static struct mipi_dsi_driver s6e63m0_dsi_driver = {
.probe = s6e63m0_dsi_probe,
- .remove = s6e63m0_dsi_remove,
.driver = {
.name = "panel-samsung-s6e63m0",
.of_match_table = s6e63m0_dsi_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
index d99afcc672ca..c8bec2b1e7c1 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
@@ -62,11 +62,6 @@ static int s6e63m0_spi_probe(struct spi_device *spi)
s6e63m0_spi_dcs_write, false);
}
-static void s6e63m0_spi_remove(struct spi_device *spi)
-{
- s6e63m0_remove(&spi->dev);
-}
-
static const struct of_device_id s6e63m0_spi_of_match[] = {
{ .compatible = "samsung,s6e63m0" },
{ /* sentinel */ }
@@ -75,7 +70,6 @@ MODULE_DEVICE_TABLE(of, s6e63m0_spi_of_match);
static struct spi_driver s6e63m0_spi_driver = {
.probe = s6e63m0_spi_probe,
- .remove = s6e63m0_spi_remove,
.driver = {
.name = "panel-samsung-s6e63m0",
.of_match_table = s6e63m0_spi_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
index 997115b1e541..1990a7c23219 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
@@ -720,20 +720,10 @@ int s6e63m0_probe(struct device *dev, void *trsp,
if (ret < 0)
return ret;
- drm_panel_add(&ctx->panel);
-
- return 0;
+ return devm_drm_panel_add(dev, &ctx->panel);
}
EXPORT_SYMBOL_GPL(s6e63m0_probe);
-void s6e63m0_remove(struct device *dev)
-{
- struct s6e63m0 *ctx = dev_get_drvdata(dev);
-
- drm_panel_remove(&ctx->panel);
-}
-EXPORT_SYMBOL_GPL(s6e63m0_remove);
-
MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
MODULE_DESCRIPTION("s6e63m0 LCD Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
index c926eca1c817..7dae6772899c 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
@@ -35,6 +35,5 @@ int s6e63m0_probe(struct device *dev, void *trsp,
const u8 *data,
size_t len),
bool dsi_mode);
-void s6e63m0_remove(struct device *dev);
#endif /* _PANEL_SAMSUNG_S6E63M0_H */
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c
index 77fee36dbb55..b48a2f9578e7 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c
@@ -722,30 +722,19 @@ static int s6e88a0_ams427ap24_probe(struct mipi_dsi_device *dsi)
if (ret < 0)
return ret;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void s6e88a0_ams427ap24_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e88a0_ams427ap24 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id s6e88a0_ams427ap24_of_match[] = {
{ .compatible = "samsung,s6e88a0-ams427ap24" },
{ /* sentinel */ },
@@ -754,7 +743,6 @@ MODULE_DEVICE_TABLE(of, s6e88a0_ams427ap24_of_match);
static struct mipi_dsi_driver s6e88a0_ams427ap24_driver = {
.probe = s6e88a0_ams427ap24_probe,
- .remove = s6e88a0_ams427ap24_remove,
.driver = {
.name = "panel-s6e88a0-ams427ap24",
.of_match_table = s6e88a0_ams427ap24_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c
index ca5cad41ff1d..bbb86ea86d5e 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c
@@ -194,30 +194,19 @@ static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi)
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e88a0_ams452ef01 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id s6e88a0_ams452ef01_of_match[] = {
{ .compatible = "samsung,s6e88a0-ams452ef01" },
{ /* sentinel */ },
@@ -226,7 +215,6 @@ MODULE_DEVICE_TABLE(of, s6e88a0_ams452ef01_of_match);
static struct mipi_dsi_driver s6e88a0_ams452ef01_driver = {
.probe = s6e88a0_ams452ef01_probe,
- .remove = s6e88a0_ams452ef01_remove,
.driver = {
.name = "panel-s6e88a0-ams452ef01",
.of_match_table = s6e88a0_ams452ef01_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
index 1b5c500d4f4e..8f160ca0a94e 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
@@ -1018,21 +1018,11 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
ctx->panel.prepare_prev_first = true;
- drm_panel_add(&ctx->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0)
- drm_panel_remove(&ctx->panel);
-
- return ret;
-}
-
-static void s6e8aa0_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e8aa0 *ctx = mipi_dsi_get_drvdata(dsi);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- mipi_dsi_detach(dsi);
- drm_panel_remove(&ctx->panel);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id s6e8aa0_of_match[] = {
@@ -1043,7 +1033,6 @@ MODULE_DEVICE_TABLE(of, s6e8aa0_of_match);
static struct mipi_dsi_driver s6e8aa0_driver = {
.probe = s6e8aa0_probe,
- .remove = s6e8aa0_remove,
.driver = {
.name = "panel-samsung-s6e8aa0",
.of_match_table = s6e8aa0_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c b/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c
index 2fae0dc6c424..0e797a2ec14f 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c
@@ -256,29 +256,17 @@ static int s6e8fc0_m1906f9_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- drm_panel_remove(&ctx->panel);
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
- }
return 0;
}
-static void s6e8fc0_remove(struct mipi_dsi_device *dsi)
-{
- struct s6e8fc0_ctx *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id samsung_s6e8fc0_of_match[] = {
{ .compatible = "samsung,s6e8fc0-m1906f9" },
{ /* sentinel */ }
@@ -287,7 +275,6 @@ MODULE_DEVICE_TABLE(of, samsung_s6e8fc0_of_match);
static struct mipi_dsi_driver s6e8fc0_driver = {
.probe = s6e8fc0_m1906f9_probe,
- .remove = s6e8fc0_remove,
.driver = {
.name = "panel-samsung-s6e8fc0-m1906f9",
.of_match_table = samsung_s6e8fc0_of_match,
diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
index f07d6533ed69..da02955f5f42 100644
--- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
@@ -250,30 +250,19 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void sofef00_panel_remove(struct mipi_dsi_device *dsi)
-{
- struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id sofef00_panel_of_match[] = {
{ .compatible = "samsung,sofef00" }, /* legacy */
{ .compatible = "samsung,sofef00-ams628nw01" },
@@ -283,7 +272,6 @@ MODULE_DEVICE_TABLE(of, sofef00_panel_of_match);
static struct mipi_dsi_driver sofef00_panel_driver = {
.probe = sofef00_panel_probe,
- .remove = sofef00_panel_remove,
.driver = {
.name = "panel-samsung-sofef00",
.of_match_table = sofef00_panel_of_match,
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
index 989b030ea22d..5077893dab7c 100644
--- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
+++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
@@ -205,15 +205,7 @@ static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
if (ret)
return ret;
- drm_panel_add(&sharp_nt->base);
-
- return 0;
-}
-
-static void sharp_nt_panel_del(struct sharp_nt_panel *sharp_nt)
-{
- if (sharp_nt->base.dev)
- drm_panel_remove(&sharp_nt->base);
+ return devm_drm_panel_add(dev, &sharp_nt->base);
}
static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
@@ -244,25 +236,7 @@ static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi)
if (ret < 0)
return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- sharp_nt_panel_del(sharp_nt);
- return ret;
- }
-
- return 0;
-}
-
-static void sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
-{
- struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
-
- sharp_nt_panel_del(sharp_nt);
+ return devm_mipi_dsi_attach(&dsi->dev, dsi);
}
static const struct of_device_id sharp_nt_of_match[] = {
@@ -277,7 +251,6 @@ static struct mipi_dsi_driver sharp_nt_panel_driver = {
.of_match_table = sharp_nt_of_match,
},
.probe = sharp_nt_panel_probe,
- .remove = sharp_nt_panel_remove,
};
module_mipi_dsi_driver(sharp_nt_panel_driver);
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c
index 0456f3d705e7..e59d9d32b1e0 100644
--- a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c
+++ b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c
@@ -233,30 +233,19 @@ static int sharp_ls060_probe(struct mipi_dsi_device *dsi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void sharp_ls060_remove(struct mipi_dsi_device *dsi)
-{
- struct sharp_ls060 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id sharp_ls060t1sx01_of_match[] = {
{ .compatible = "sharp,ls060t1sx01" },
{ /* sentinel */ }
@@ -265,7 +254,6 @@ MODULE_DEVICE_TABLE(of, sharp_ls060t1sx01_of_match);
static struct mipi_dsi_driver sharp_ls060_driver = {
.probe = sharp_ls060_probe,
- .remove = sharp_ls060_remove,
.driver = {
.name = "panel-sharp-ls060t1sx01",
.of_match_table = sharp_ls060t1sx01_of_match,
diff --git a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c
index a14c86c60d19..285f0d033efd 100644
--- a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c
+++ b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c
@@ -214,30 +214,19 @@ static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi)
ctx->panel.prepare_prev_first = true;
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi)
-{
- struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id sony_td4353_jdi_of_match[] = {
{ .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ },
{ /* sentinel */ }
@@ -246,7 +235,6 @@ MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match);
static struct mipi_dsi_driver sony_td4353_jdi_driver = {
.probe = sony_td4353_jdi_probe,
- .remove = sony_td4353_jdi_remove,
.driver = {
.name = "panel-sony-td4353-jdi",
.of_match_table = sony_td4353_jdi_of_match,
diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
index 216a6ad8696e..0ebd9836364e 100644
--- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
+++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
@@ -472,30 +472,19 @@ static int truly_nt35521_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
-static void truly_nt35521_remove(struct mipi_dsi_device *dsi)
-{
- struct truly_nt35521 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id truly_nt35521_of_match[] = {
{ .compatible = "sony,tulip-truly-nt35521" },
{ /* sentinel */ }
@@ -504,7 +493,6 @@ MODULE_DEVICE_TABLE(of, truly_nt35521_of_match);
static struct mipi_dsi_driver truly_nt35521_driver = {
.probe = truly_nt35521_probe,
- .remove = truly_nt35521_remove,
.driver = {
.name = "panel-truly-nt35521",
.of_match_table = truly_nt35521_of_match,
diff --git a/drivers/gpu/drm/panel/panel-visionox-r66451.c b/drivers/gpu/drm/panel/panel-visionox-r66451.c
index 690cccedd438..1efb139bb711 100644
--- a/drivers/gpu/drm/panel/panel-visionox-r66451.c
+++ b/drivers/gpu/drm/panel/panel-visionox-r66451.c
@@ -304,29 +304,17 @@ static int visionox_r66451_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
- }
return ret;
}
-static void visionox_r66451_remove(struct mipi_dsi_device *dsi)
-{
- struct visionox_r66451 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach DSI host: %d\n", ret);
-
- drm_panel_remove(&ctx->panel);
-}
-
static const struct of_device_id visionox_r66451_of_match[] = {
{.compatible = "visionox,r66451"},
{ /*sentinel*/ }
@@ -335,7 +323,6 @@ MODULE_DEVICE_TABLE(of, visionox_r66451_of_match);
static struct mipi_dsi_driver visionox_r66451_driver = {
.probe = visionox_r66451_probe,
- .remove = visionox_r66451_remove,
.driver = {
.name = "panel-visionox-r66451",
.of_match_table = visionox_r66451_of_match,
diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c
index 50f8a84537ca..4309e04c35c4 100644
--- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c
+++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c
@@ -382,31 +382,21 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
- ret = mipi_dsi_attach(dsi);
+ ret = devm_mipi_dsi_attach(dev, dsi);
if (ret < 0) {
dev_err(dev, "dsi attach failed ret = %d\n", ret);
- goto err_dsi_attach;
+ return ret;
}
return 0;
-
-err_dsi_attach:
- drm_panel_remove(&ctx->panel);
- return ret;
-}
-
-static void visionox_rm69299_remove(struct mipi_dsi_device *dsi)
-{
- struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi);
-
- mipi_dsi_detach(ctx->dsi);
- drm_panel_remove(&ctx->panel);
}
const struct visionox_rm69299_panel_desc visionox_rm69299_1080p_display_desc = {
@@ -438,7 +428,6 @@ static struct mipi_dsi_driver visionox_rm69299_driver = {
.of_match_table = visionox_rm69299_of_match,
},
.probe = visionox_rm69299_probe,
- .remove = visionox_rm69299_remove,
};
module_mipi_dsi_driver(visionox_rm69299_driver);
diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
index b51c30778811..0fe81d259274 100644
--- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c
+++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
@@ -78,7 +78,7 @@ static int panfrost_devfreq_get_dev_status(struct device *dev,
dev_dbg(pfdev->base.dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
status->busy_time, status->total_time,
- status->busy_time / (status->total_time / 100),
+ status->busy_time * 100 / MAX(status->total_time, 1),
status->current_frequency / 1000 / 1000);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index d0a23c51be39..485349faf251 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -11,7 +11,6 @@
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
-#include "panfrost_features.h"
#include "panfrost_gem.h"
#include "panfrost_issues.h"
#include "panfrost_gpu.h"
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 36cc2e67a308..96a66d673ba6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -662,7 +662,7 @@ static int panfrost_ioctl_query_bo_info(struct drm_device *dev, void *data,
if (bo->is_heap)
args->create_flags |= PANFROST_BO_HEAP;
- if (!bo->base.map_wc)
+ if (bo->wb_mmap)
args->create_flags |= PANFROST_BO_WB_MMAP;
}
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 0fda64fbe5f2..b55a3f9edd41 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -92,17 +92,21 @@ struct panthor_irq {
u32 mask;
/**
- * @mask_lock: protects modifications to _INT_MASK and @mask.
+ * @lock: protects modifications to _INT_MASK, @mask and @state.
*
* In paths where _INT_MASK is updated based on a state
* transition/check, it's crucial for the state update/check to be
* inside the locked section, otherwise it introduces a race window
* leading to potential _INT_MASK inconsistencies.
*/
- spinlock_t mask_lock;
+ spinlock_t lock;
- /** @state: one of &enum panthor_irq_state reflecting the current state. */
- atomic_t state;
+ /**
+ * @state: one of &enum panthor_irq_state reflecting the current state.
+ *
+ * Must be accessed with lock held.
+ */
+ enum panthor_irq_state state;
};
/**
@@ -491,150 +495,6 @@ panthor_exception_is_fault(u32 exception_code)
const char *panthor_exception_name(struct panthor_device *ptdev,
u32 exception_code);
-#define INT_RAWSTAT 0x0
-#define INT_CLEAR 0x4
-#define INT_MASK 0x8
-#define INT_STAT 0xc
-
-/**
- * PANTHOR_IRQ_HANDLER() - Define interrupt handlers and the interrupt
- * registration function.
- *
- * The boiler-plate to gracefully deal with shared interrupts is
- * auto-generated. All you have to do is call PANTHOR_IRQ_HANDLER()
- * just after the actual handler. The handler prototype is:
- *
- * void (*handler)(struct panthor_device *, u32 status);
- */
-#define PANTHOR_IRQ_HANDLER(__name, __handler) \
-static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) \
-{ \
- struct panthor_irq *pirq = data; \
- enum panthor_irq_state old_state; \
- \
- guard(spinlock_irqsave)(&pirq->mask_lock); \
- old_state = atomic_cmpxchg(&pirq->state, \
- PANTHOR_IRQ_STATE_ACTIVE, \
- PANTHOR_IRQ_STATE_PROCESSING); \
- if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \
- return IRQ_NONE; \
- \
- if (!gpu_read(pirq->iomem, INT_STAT)) { \
- atomic_cmpxchg(&pirq->state, \
- PANTHOR_IRQ_STATE_PROCESSING, \
- PANTHOR_IRQ_STATE_ACTIVE); \
- return IRQ_NONE; \
- } \
- \
- gpu_write(pirq->iomem, INT_MASK, 0); \
- return IRQ_WAKE_THREAD; \
-} \
- \
-static irqreturn_t panthor_ ## __name ## _irq_threaded_handler(int irq, void *data) \
-{ \
- struct panthor_irq *pirq = data; \
- struct panthor_device *ptdev = pirq->ptdev; \
- irqreturn_t ret = IRQ_NONE; \
- \
- while (true) { \
- /* It's safe to access pirq->mask without the lock held here. If a new \
- * event gets added to the mask and the corresponding IRQ is pending, \
- * we'll process it right away instead of adding an extra raw -> threaded \
- * round trip. If an event is removed and the status bit is set, it will \
- * be ignored, just like it would have been if the mask had been adjusted \
- * right before the HW event kicks in. TLDR; it's all expected races we're \
- * covered for. \
- */ \
- u32 status = gpu_read(pirq->iomem, INT_RAWSTAT) & pirq->mask; \
- \
- if (!status) \
- break; \
- \
- __handler(ptdev, status); \
- ret = IRQ_HANDLED; \
- } \
- \
- scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \
- enum panthor_irq_state old_state; \
- \
- old_state = atomic_cmpxchg(&pirq->state, \
- PANTHOR_IRQ_STATE_PROCESSING, \
- PANTHOR_IRQ_STATE_ACTIVE); \
- if (old_state == PANTHOR_IRQ_STATE_PROCESSING) \
- gpu_write(pirq->iomem, INT_MASK, pirq->mask); \
- } \
- \
- return ret; \
-} \
- \
-static inline void panthor_ ## __name ## _irq_suspend(struct panthor_irq *pirq) \
-{ \
- scoped_guard(spinlock_irqsave, &pirq->mask_lock) { \
- atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); \
- gpu_write(pirq->iomem, INT_MASK, 0); \
- } \
- synchronize_irq(pirq->irq); \
- atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \
-} \
- \
-static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) \
-{ \
- guard(spinlock_irqsave)(&pirq->mask_lock); \
- \
- atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); \
- gpu_write(pirq->iomem, INT_CLEAR, pirq->mask); \
- gpu_write(pirq->iomem, INT_MASK, pirq->mask); \
-} \
- \
-static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \
- struct panthor_irq *pirq, \
- int irq, void __iomem *iomem) \
-{ \
- pirq->ptdev = ptdev; \
- pirq->irq = irq; \
- pirq->mask = 0; \
- pirq->iomem = iomem; \
- spin_lock_init(&pirq->mask_lock); \
- atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \
- gpu_write(pirq->iomem, INT_MASK, 0); \
- \
- return devm_request_threaded_irq(ptdev->base.dev, irq, \
- panthor_ ## __name ## _irq_raw_handler, \
- panthor_ ## __name ## _irq_threaded_handler, \
- IRQF_SHARED, KBUILD_MODNAME "-" # __name, \
- pirq); \
-} \
- \
-static inline void panthor_ ## __name ## _irq_enable_events(struct panthor_irq *pirq, u32 mask) \
-{ \
- guard(spinlock_irqsave)(&pirq->mask_lock); \
- pirq->mask |= mask; \
- \
- /* The only situation where we need to write the new mask is if the IRQ is active. \
- * If it's being processed, the mask will be restored for us in _irq_threaded_handler() \
- * on the PROCESSING -> ACTIVE transition. \
- * If the IRQ is suspended/suspending, the mask is restored at resume time. \
- */ \
- if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \
- gpu_write(pirq->iomem, INT_MASK, pirq->mask); \
-} \
- \
-static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq *pirq, u32 mask)\
-{ \
- guard(spinlock_irqsave)(&pirq->mask_lock); \
- pirq->mask &= ~mask; \
- \
- /* The only situation where we need to write the new mask is if the IRQ is active. \
- * If it's being processed, the mask will be restored for us in _irq_threaded_handler() \
- * on the PROCESSING -> ACTIVE transition. \
- * If the IRQ is suspended/suspending, the mask is restored at resume time. \
- */ \
- if (atomic_read(&pirq->state) == PANTHOR_IRQ_STATE_ACTIVE) \
- gpu_write(pirq->iomem, INT_MASK, pirq->mask); \
-}
-
-extern struct workqueue_struct *panthor_cleanup_wq;
-
static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
{
writel(data, iomem + reg);
@@ -706,4 +566,134 @@ static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
false, iomem, reg)
+#define INT_RAWSTAT 0x0
+#define INT_CLEAR 0x4
+#define INT_MASK 0x8
+#define INT_STAT 0xc
+
+static inline irqreturn_t panthor_irq_default_raw_handler(int irq, void *data)
+{
+ struct panthor_irq *pirq = data;
+
+ guard(spinlock_irqsave)(&pirq->lock);
+ if (pirq->state != PANTHOR_IRQ_STATE_ACTIVE)
+ return IRQ_NONE;
+
+ if (!gpu_read(pirq->iomem, INT_STAT))
+ return IRQ_NONE;
+
+ pirq->state = PANTHOR_IRQ_STATE_PROCESSING;
+ gpu_write(pirq->iomem, INT_MASK, 0);
+ return IRQ_WAKE_THREAD;
+}
+
+static __always_inline irqreturn_t
+panthor_irq_default_threaded_handler(void *data,
+ void (*slow_handler)(struct panthor_irq *, u32))
+{
+ struct panthor_irq *pirq = data;
+ irqreturn_t ret = IRQ_NONE;
+
+ while (true) {
+ /* It's safe to access pirq->mask without the lock held here. If a new
+ * event gets added to the mask and the corresponding IRQ is pending,
+ * we'll process it right away instead of adding an extra raw -> threaded
+ * round trip. If an event is removed and the status bit is set, it will
+ * be ignored, just like it would have been if the mask had been adjusted
+ * right before the HW event kicks in. TLDR; it's all expected races we're
+ * covered for.
+ */
+ u32 status = gpu_read(pirq->iomem, INT_RAWSTAT) & pirq->mask;
+
+ if (!status)
+ break;
+
+ slow_handler(pirq, status);
+ ret = IRQ_HANDLED;
+ }
+
+ scoped_guard(spinlock_irqsave, &pirq->lock) {
+ if (pirq->state == PANTHOR_IRQ_STATE_PROCESSING) {
+ pirq->state = PANTHOR_IRQ_STATE_ACTIVE;
+ gpu_write(pirq->iomem, INT_MASK, pirq->mask);
+ }
+ }
+
+ return ret;
+}
+
+static inline void panthor_irq_suspend(struct panthor_irq *pirq)
+{
+ scoped_guard(spinlock_irqsave, &pirq->lock) {
+ pirq->state = PANTHOR_IRQ_STATE_SUSPENDING;
+ gpu_write(pirq->iomem, INT_MASK, 0);
+ }
+ synchronize_irq(pirq->irq);
+ scoped_guard(spinlock_irqsave, &pirq->lock)
+ pirq->state = PANTHOR_IRQ_STATE_SUSPENDED;
+}
+
+static inline void panthor_irq_resume(struct panthor_irq *pirq)
+{
+ guard(spinlock_irqsave)(&pirq->lock);
+ pirq->state = PANTHOR_IRQ_STATE_ACTIVE;
+ gpu_write(pirq->iomem, INT_CLEAR, pirq->mask);
+ gpu_write(pirq->iomem, INT_MASK, pirq->mask);
+}
+
+static inline void panthor_irq_enable_events(struct panthor_irq *pirq, u32 mask)
+{
+ guard(spinlock_irqsave)(&pirq->lock);
+ pirq->mask |= mask;
+
+ /* The only situation where we need to write the new mask is if the IRQ is active.
+ * If it's being processed, the mask will be restored for us in _irq_threaded_handler()
+ * on the PROCESSING -> ACTIVE transition.
+ * If the IRQ is suspended/suspending, the mask is restored at resume time.
+ */
+ if (pirq->state == PANTHOR_IRQ_STATE_ACTIVE)
+ gpu_write(pirq->iomem, INT_MASK, pirq->mask);
+}
+
+static inline void panthor_irq_disable_events(struct panthor_irq *pirq, u32 mask)
+{
+ guard(spinlock_irqsave)(&pirq->lock);
+ pirq->mask &= ~mask;
+
+ /* The only situation where we need to write the new mask is if the IRQ is active.
+ * If it's being processed, the mask will be restored for us in _irq_threaded_handler()
+ * on the PROCESSING -> ACTIVE transition.
+ * If the IRQ is suspended/suspending, the mask is restored at resume time.
+ */
+ if (pirq->state == PANTHOR_IRQ_STATE_ACTIVE)
+ gpu_write(pirq->iomem, INT_MASK, pirq->mask);
+}
+
+static inline int
+panthor_irq_request(struct panthor_device *ptdev, struct panthor_irq *pirq,
+ int irq, void __iomem *iomem, const char *name,
+ irqreturn_t (*threaded_handler)(int, void *data))
+{
+ const char *full_name;
+
+ pirq->ptdev = ptdev;
+ pirq->irq = irq;
+ pirq->mask = 0;
+ pirq->iomem = iomem;
+ spin_lock_init(&pirq->lock);
+ pirq->state = PANTHOR_IRQ_STATE_SUSPENDED;
+
+ full_name = devm_kasprintf(ptdev->base.dev, GFP_KERNEL, KBUILD_MODNAME "-%s", name);
+ if (!full_name)
+ return -ENOMEM;
+
+ gpu_write(pirq->iomem, INT_MASK, 0);
+ return devm_request_threaded_irq(ptdev->base.dev, irq,
+ panthor_irq_default_raw_handler,
+ threaded_handler,
+ IRQF_SHARED, full_name, pirq);
+}
+
+extern struct workqueue_struct *panthor_cleanup_wq;
+
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 10b7e12c1f1e..4f1fab66a13b 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1095,8 +1095,9 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev)
msecs_to_jiffies(PING_INTERVAL_MS));
}
-static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status)
+static void panthor_job_irq_handler(struct panthor_irq *pirq, u32 status)
{
+ struct panthor_device *ptdev = pirq->ptdev;
u32 duration;
u64 start = 0;
@@ -1122,7 +1123,11 @@ static void panthor_job_irq_handler(struct panthor_device *ptdev, u32 status)
trace_gpu_job_irq(ptdev->base.dev, status, duration);
}
}
-PANTHOR_IRQ_HANDLER(job, panthor_job_irq_handler);
+
+static irqreturn_t panthor_job_irq_threaded_handler(int irq, void *data)
+{
+ return panthor_irq_default_threaded_handler(data, panthor_job_irq_handler);
+}
static int panthor_fw_start(struct panthor_device *ptdev)
{
@@ -1130,8 +1135,8 @@ static int panthor_fw_start(struct panthor_device *ptdev)
bool timedout = false;
ptdev->fw->booted = false;
- panthor_job_irq_enable_events(&ptdev->fw->irq, ~0);
- panthor_job_irq_resume(&ptdev->fw->irq);
+ panthor_irq_enable_events(&ptdev->fw->irq, ~0);
+ panthor_irq_resume(&ptdev->fw->irq);
gpu_write(fw->iomem, MCU_CONTROL, MCU_CONTROL_AUTO);
if (!wait_event_timeout(ptdev->fw->req_waitqueue,
@@ -1241,7 +1246,7 @@ void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang)
ptdev->reset.fast = true;
}
- panthor_job_irq_suspend(&ptdev->fw->irq);
+ panthor_irq_suspend(&ptdev->fw->irq);
panthor_fw_stop(ptdev);
}
@@ -1310,7 +1315,7 @@ void panthor_fw_unplug(struct panthor_device *ptdev)
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) {
/* Make sure the IRQ handler cannot be called after that point. */
- panthor_job_irq_suspend(&ptdev->fw->irq);
+ panthor_irq_suspend(&ptdev->fw->irq);
panthor_fw_stop(ptdev);
}
@@ -1505,8 +1510,9 @@ int panthor_fw_init(struct panthor_device *ptdev)
if (irq <= 0)
return -ENODEV;
- ret = panthor_request_job_irq(ptdev, &fw->irq, irq,
- ptdev->iomem + JOB_INT_BASE);
+ ret = panthor_irq_request(ptdev, &fw->irq, irq,
+ ptdev->iomem + JOB_INT_BASE, "job",
+ panthor_job_irq_threaded_handler);
if (ret) {
drm_err(&ptdev->base, "failed to request job irq");
return ret;
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index c013d6bf9a59..a383b04f101e 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -86,8 +86,9 @@ static void panthor_gpu_l2_config_set(struct panthor_device *ptdev)
gpu_write(gpu->iomem, GPU_L2_CONFIG, l2_config);
}
-static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status)
+static void panthor_gpu_irq_handler(struct panthor_irq *pirq, u32 status)
{
+ struct panthor_device *ptdev = pirq->ptdev;
struct panthor_gpu *gpu = ptdev->gpu;
gpu_write(gpu->irq.iomem, INT_CLEAR, status);
@@ -116,7 +117,11 @@ static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status)
}
spin_unlock(&ptdev->gpu->reqs_lock);
}
-PANTHOR_IRQ_HANDLER(gpu, panthor_gpu_irq_handler);
+
+static irqreturn_t panthor_gpu_irq_threaded_handler(int irq, void *data)
+{
+ return panthor_irq_default_threaded_handler(data, panthor_gpu_irq_handler);
+}
/**
* panthor_gpu_unplug() - Called when the GPU is unplugged.
@@ -128,7 +133,7 @@ void panthor_gpu_unplug(struct panthor_device *ptdev)
/* Make sure the IRQ handler is not running after that point. */
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev))
- panthor_gpu_irq_suspend(&ptdev->gpu->irq);
+ panthor_irq_suspend(&ptdev->gpu->irq);
/* Wake-up all waiters. */
spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
@@ -169,13 +174,14 @@ int panthor_gpu_init(struct panthor_device *ptdev)
if (irq < 0)
return irq;
- ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq,
- ptdev->iomem + GPU_INT_BASE);
+ ret = panthor_irq_request(ptdev, &ptdev->gpu->irq, irq,
+ ptdev->iomem + GPU_INT_BASE, "gpu",
+ panthor_gpu_irq_threaded_handler);
if (ret)
return ret;
- panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK);
- panthor_gpu_irq_resume(&ptdev->gpu->irq);
+ panthor_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK);
+ panthor_irq_resume(&ptdev->gpu->irq);
return 0;
}
@@ -183,7 +189,7 @@ int panthor_gpu_power_changed_on(struct panthor_device *ptdev)
{
guard(pm_runtime_active)(ptdev->base.dev);
- panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK);
+ panthor_irq_enable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK);
return 0;
}
@@ -192,7 +198,7 @@ void panthor_gpu_power_changed_off(struct panthor_device *ptdev)
{
guard(pm_runtime_active)(ptdev->base.dev);
- panthor_gpu_irq_disable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK);
+ panthor_irq_disable_events(&ptdev->gpu->irq, GPU_POWER_INTERRUPTS_MASK);
}
/**
@@ -317,6 +323,21 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev)
return panthor_gpu_power_on(ptdev, L2, 1, 20000);
}
+static inline void panthor_gpu_emit_flush_caches_tp(struct panthor_device *ptdev,
+ u64 start, u32 l2, u32 lsc,
+ u32 other, int ret)
+{
+ u32 duration;
+
+ if (!tracepoint_enabled(gpu_cache_flush) || !start)
+ return;
+
+ if (check_sub_overflow(ktime_get_ns(), start, &duration))
+ duration = U32_MAX;
+
+ trace_gpu_cache_flush(ptdev->base.dev, l2, lsc, other, duration, ret);
+}
+
/**
* panthor_gpu_flush_caches() - Flush caches
* @ptdev: Device.
@@ -330,36 +351,40 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
u32 l2, u32 lsc, u32 other)
{
struct panthor_gpu *gpu = ptdev->gpu;
- unsigned long flags;
+ u64 start = 0;
int ret = 0;
/* Serialize cache flush operations. */
guard(mutex)(&ptdev->gpu->cache_flush_lock);
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
- if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) {
- ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED;
- gpu_write(gpu->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other));
- } else {
- ret = -EIO;
+ scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
+ if (tracepoint_enabled(gpu_cache_flush))
+ start = ktime_get_ns();
+
+ if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) {
+ ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED;
+ gpu_write(gpu->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other));
+ } else {
+ panthor_gpu_emit_flush_caches_tp(ptdev, start, l2, lsc,
+ other, -EIO);
+ return -EIO;
+ }
}
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
-
- if (ret)
- return ret;
if (!wait_event_timeout(ptdev->gpu->reqs_acked,
!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED),
msecs_to_jiffies(100))) {
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
- if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 &&
- !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED))
- ret = -ETIMEDOUT;
- else
- ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
+ scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
+ if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 &&
+ !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED))
+ ret = -ETIMEDOUT;
+ else
+ ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
+ }
}
+ panthor_gpu_emit_flush_caches_tp(ptdev, start, l2, lsc, other, ret);
+
if (ret) {
panthor_device_schedule_reset(ptdev);
drm_err(&ptdev->base, "Flush caches timeout");
@@ -378,27 +403,26 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev)
{
struct panthor_gpu *gpu = ptdev->gpu;
bool timedout = false;
- unsigned long flags;
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
- if (!drm_WARN_ON(&ptdev->base,
- ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) {
- ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED;
- gpu_write(gpu->irq.iomem, INT_CLEAR, GPU_IRQ_RESET_COMPLETED);
- gpu_write(gpu->iomem, GPU_CMD, GPU_SOFT_RESET);
+ scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
+ if (!drm_WARN_ON(&ptdev->base,
+ ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) {
+ ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED;
+ gpu_write(gpu->irq.iomem, INT_CLEAR, GPU_IRQ_RESET_COMPLETED);
+ gpu_write(gpu->iomem, GPU_CMD, GPU_SOFT_RESET);
+ }
}
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
if (!wait_event_timeout(ptdev->gpu->reqs_acked,
!(ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED),
msecs_to_jiffies(100))) {
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
- if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 &&
- !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED))
- timedout = true;
- else
- ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED;
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
+ scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
+ if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 &&
+ !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED))
+ timedout = true;
+ else
+ ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED;
+ }
}
if (timedout) {
@@ -406,7 +430,10 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev)
return -ETIMEDOUT;
}
- ptdev->gpu->pending_reqs = 0;
+ scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
+ ptdev->gpu->pending_reqs = 0;
+ }
+
return 0;
}
@@ -425,7 +452,7 @@ void panthor_gpu_suspend(struct panthor_device *ptdev)
else
panthor_hw_l2_power_off(ptdev);
- panthor_gpu_irq_suspend(&ptdev->gpu->irq);
+ panthor_irq_suspend(&ptdev->gpu->irq);
}
/**
@@ -437,7 +464,7 @@ void panthor_gpu_suspend(struct panthor_device *ptdev)
*/
void panthor_gpu_resume(struct panthor_device *ptdev)
{
- panthor_gpu_irq_resume(&ptdev->gpu->irq);
+ panthor_irq_resume(&ptdev->gpu->irq);
panthor_hw_l2_power_on(ptdev);
}
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 9f63a048df61..6f7b83dca6a5 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -601,17 +601,13 @@ static u32 panthor_mmu_as_fault_mask(struct panthor_device *ptdev, u32 as)
return BIT(as);
}
-/* Forward declaration to call helpers within as_enable/disable */
-static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status);
-PANTHOR_IRQ_HANDLER(mmu, panthor_mmu_irq_handler);
-
static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
u64 transtab, u64 transcfg, u64 memattr)
{
struct panthor_mmu *mmu = ptdev->mmu;
- panthor_mmu_irq_enable_events(&ptdev->mmu->irq,
- panthor_mmu_as_fault_mask(ptdev, as_nr));
+ panthor_irq_enable_events(&ptdev->mmu->irq,
+ panthor_mmu_as_fault_mask(ptdev, as_nr));
gpu_write64(mmu->iomem, AS_TRANSTAB(as_nr), transtab);
gpu_write64(mmu->iomem, AS_MEMATTR(as_nr), memattr);
@@ -629,8 +625,8 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
lockdep_assert_held(&ptdev->mmu->as.slots_lock);
- panthor_mmu_irq_disable_events(&ptdev->mmu->irq,
- panthor_mmu_as_fault_mask(ptdev, as_nr));
+ panthor_irq_disable_events(&ptdev->mmu->irq,
+ panthor_mmu_as_fault_mask(ptdev, as_nr));
/* Flush+invalidate RW caches, invalidate RO ones. */
ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV,
@@ -1859,8 +1855,9 @@ static void panthor_vm_unlock_region(struct panthor_vm *vm)
mutex_unlock(&ptdev->mmu->as.slots_lock);
}
-static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
+static void panthor_mmu_irq_handler(struct panthor_irq *pirq, u32 status)
{
+ struct panthor_device *ptdev = pirq->ptdev;
struct panthor_mmu *mmu = ptdev->mmu;
bool has_unhandled_faults = false;
@@ -1923,6 +1920,11 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
panthor_sched_report_mmu_fault(ptdev);
}
+static irqreturn_t panthor_mmu_irq_threaded_handler(int irq, void *data)
+{
+ return panthor_irq_default_threaded_handler(data, panthor_mmu_irq_handler);
+}
+
/**
* panthor_mmu_suspend() - Suspend the MMU logic
* @ptdev: Device.
@@ -1947,7 +1949,7 @@ void panthor_mmu_suspend(struct panthor_device *ptdev)
}
mutex_unlock(&ptdev->mmu->as.slots_lock);
- panthor_mmu_irq_suspend(&ptdev->mmu->irq);
+ panthor_irq_suspend(&ptdev->mmu->irq);
}
/**
@@ -1966,7 +1968,7 @@ void panthor_mmu_resume(struct panthor_device *ptdev)
ptdev->mmu->as.faulty_mask = 0;
mutex_unlock(&ptdev->mmu->as.slots_lock);
- panthor_mmu_irq_resume(&ptdev->mmu->irq);
+ panthor_irq_resume(&ptdev->mmu->irq);
}
/**
@@ -1983,7 +1985,7 @@ void panthor_mmu_pre_reset(struct panthor_device *ptdev)
{
struct panthor_vm *vm;
- panthor_mmu_irq_suspend(&ptdev->mmu->irq);
+ panthor_irq_suspend(&ptdev->mmu->irq);
mutex_lock(&ptdev->mmu->vm.lock);
ptdev->mmu->vm.reset_in_progress = true;
@@ -2020,7 +2022,7 @@ void panthor_mmu_post_reset(struct panthor_device *ptdev)
mutex_unlock(&ptdev->mmu->as.slots_lock);
- panthor_mmu_irq_resume(&ptdev->mmu->irq);
+ panthor_irq_resume(&ptdev->mmu->irq);
/* Restart the VM_BIND queues. */
mutex_lock(&ptdev->mmu->vm.lock);
@@ -3353,7 +3355,7 @@ panthor_mmu_reclaim_priv_bos(struct panthor_device *ptdev,
void panthor_mmu_unplug(struct panthor_device *ptdev)
{
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev))
- panthor_mmu_irq_suspend(&ptdev->mmu->irq);
+ panthor_irq_suspend(&ptdev->mmu->irq);
mutex_lock(&ptdev->mmu->as.slots_lock);
for (u32 i = 0; i < ARRAY_SIZE(ptdev->mmu->as.slots); i++) {
@@ -3414,8 +3416,9 @@ int panthor_mmu_init(struct panthor_device *ptdev)
if (irq <= 0)
return -ENODEV;
- ret = panthor_request_mmu_irq(ptdev, &mmu->irq, irq,
- ptdev->iomem + MMU_INT_BASE);
+ ret = panthor_irq_request(ptdev, &mmu->irq, irq,
+ ptdev->iomem + MMU_INT_BASE, "mmu",
+ panthor_mmu_irq_threaded_handler);
if (ret)
return ret;
@@ -3436,8 +3439,8 @@ int panthor_mmu_init(struct panthor_device *ptdev)
if (ret)
return ret;
- panthor_mmu_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0));
- panthor_mmu_irq_resume(&mmu->irq);
+ panthor_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0));
+ panthor_irq_resume(&mmu->irq);
return 0;
}
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
index f2c2c3000590..dd7b6ef8ea20 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr.c
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -56,8 +56,9 @@ struct panthor_pwr {
wait_queue_head_t reqs_acked;
};
-static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status)
+static void panthor_pwr_irq_handler(struct panthor_irq *pirq, u32 status)
{
+ struct panthor_device *ptdev = pirq->ptdev;
struct panthor_pwr *pwr = ptdev->pwr;
spin_lock(&ptdev->pwr->reqs_lock);
@@ -75,7 +76,11 @@ static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status)
}
spin_unlock(&ptdev->pwr->reqs_lock);
}
-PANTHOR_IRQ_HANDLER(pwr, panthor_pwr_irq_handler);
+
+static irqreturn_t panthor_pwr_irq_threaded_handler(int irq, void *data)
+{
+ return panthor_irq_default_threaded_handler(data, panthor_pwr_irq_handler);
+}
static void panthor_pwr_write_command(struct panthor_device *ptdev, u32 command, u64 args)
{
@@ -454,7 +459,7 @@ void panthor_pwr_unplug(struct panthor_device *ptdev)
/* Make sure the IRQ handler is not running after that point. */
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev))
- panthor_pwr_irq_suspend(&ptdev->pwr->irq);
+ panthor_irq_suspend(&ptdev->pwr->irq);
/* Wake-up all waiters. */
spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags);
@@ -484,13 +489,14 @@ int panthor_pwr_init(struct panthor_device *ptdev)
if (irq < 0)
return irq;
- err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq,
- pwr->iomem + PWR_INT_BASE);
+ err = panthor_irq_request(ptdev, &pwr->irq, irq,
+ pwr->iomem + PWR_INT_BASE, "pwr",
+ panthor_pwr_irq_threaded_handler);
if (err)
return err;
- panthor_pwr_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK);
- panthor_pwr_irq_resume(&pwr->irq);
+ panthor_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK);
+ panthor_irq_resume(&pwr->irq);
return 0;
}
@@ -566,7 +572,7 @@ void panthor_pwr_suspend(struct panthor_device *ptdev)
if (!ptdev->pwr)
return;
- panthor_pwr_irq_suspend(&ptdev->pwr->irq);
+ panthor_irq_suspend(&ptdev->pwr->irq);
}
void panthor_pwr_resume(struct panthor_device *ptdev)
@@ -574,5 +580,5 @@ void panthor_pwr_resume(struct panthor_device *ptdev)
if (!ptdev->pwr)
return;
- panthor_pwr_irq_resume(&ptdev->pwr->irq);
+ panthor_irq_resume(&ptdev->pwr->irq);
}
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 5778e23e20a0..60b2417deb81 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -178,23 +178,6 @@ struct panthor_scheduler {
struct work_struct sync_upd_work;
/**
- * @fw_events_work: Work used to process FW events outside the interrupt path.
- *
- * Even if the interrupt is threaded, we need any event processing
- * that require taking the panthor_scheduler::lock to be processed
- * outside the interrupt path so we don't block the tick logic when
- * it calls panthor_fw_{csg,wait}_wait_acks(). Since most of the
- * event processing requires taking this lock, we just delegate all
- * FW event processing to the scheduler workqueue.
- */
- struct work_struct fw_events_work;
-
- /**
- * @fw_events: Bitmask encoding pending FW events.
- */
- atomic_t fw_events;
-
- /**
* @resched_target: When the next tick should occur.
*
* Expressed in jiffies.
@@ -255,7 +238,21 @@ struct panthor_scheduler {
} groups;
/**
+ * @events_lock: Lock taken when processing events.
+ *
+ * This also needs to be taken when csg_slots are updated, to make sure
+ * the event processing logic doesn't touch groups that have left the CSG
+ * slot.
+ */
+ spinlock_t events_lock;
+
+ /**
* @csg_slots: FW command stream group slots.
+ *
+ * Updates to the group binding must happen with both
+ * panthor_scheduler::lock and panthor_scheduler::events_lock held.
+ * The group binding may therefore be read while holding either lock.
+ * Other slot fields are protected by panthor_scheduler::lock.
*/
struct panthor_csg_slot csg_slots[MAX_CSGS];
@@ -565,11 +562,13 @@ struct panthor_group {
/** @idle_queues: Bitmask reflecting the idle queues. */
u32 idle_queues;
- /** @fatal_lock: Lock used to protect access to fatal fields. */
- spinlock_t fatal_lock;
-
- /** @fatal_queues: Bitmask reflecting the queues that hit a fatal exception. */
- u32 fatal_queues;
+ /**
+ * @fatal_queues: Bitmask reflecting the queues that hit a fatal exception.
+ *
+ * This is an atomic because we don't want to acquire the events_lock
+ * every time we need to check the group state.
+ */
+ atomic_t fatal_queues;
/** @tiler_oom: Mask of queues that have a tiler OOM event to process. */
atomic_t tiler_oom;
@@ -605,8 +604,14 @@ struct panthor_group {
* any timeout situation is unrecoverable, and the group becomes useless. We
* simply wait for all references to be dropped so we can release the group
* object.
+ *
+ * This is an atomic because it can be set from both a scheduling context
+ * (protected with panthor_scheduler::lock) and an event processing context
+ * (protected with panthor_scheduler::events_lock). We could protect access
+ * with the events_lock, but this is simpler to make it an atomic since the
+ * only allowed transition is false -> true.
*/
- bool timedout;
+ atomic_t timedout;
/**
* @innocent: True when the group becomes unusable because the group suspension
@@ -676,9 +681,6 @@ struct panthor_group {
*/
struct panthor_kernel_bo *protm_suspend_buf;
- /** @sync_upd_work: Work used to check/signal job fences. */
- struct work_struct sync_upd_work;
-
/** @tiler_oom_work: Work used to process tiler OOM events happening on this group. */
struct work_struct tiler_oom_work;
@@ -988,87 +990,6 @@ group_get(struct panthor_group *group)
return group;
}
-/**
- * group_bind_locked() - Bind a group to a group slot
- * @group: Group.
- * @csg_id: Slot.
- *
- * Return: 0 on success, a negative error code otherwise.
- */
-static int
-group_bind_locked(struct panthor_group *group, u32 csg_id)
-{
- struct panthor_device *ptdev = group->ptdev;
- struct panthor_csg_slot *csg_slot;
- int ret;
-
- lockdep_assert_held(&ptdev->scheduler->lock);
-
- if (drm_WARN_ON(&ptdev->base, group->csg_id != -1 || csg_id >= MAX_CSGS ||
- ptdev->scheduler->csg_slots[csg_id].group))
- return -EINVAL;
-
- ret = panthor_vm_active(group->vm);
- if (ret)
- return ret;
-
- csg_slot = &ptdev->scheduler->csg_slots[csg_id];
- group_get(group);
- group->csg_id = csg_id;
-
- /* Dummy doorbell allocation: doorbell is assigned to the group and
- * all queues use the same doorbell.
- *
- * TODO: Implement LRU-based doorbell assignment, so the most often
- * updated queues get their own doorbell, thus avoiding useless checks
- * on queues belonging to the same group that are rarely updated.
- */
- for (u32 i = 0; i < group->queue_count; i++)
- group->queues[i]->doorbell_id = csg_id + 1;
-
- csg_slot->group = group;
-
- return 0;
-}
-
-/**
- * group_unbind_locked() - Unbind a group from a slot.
- * @group: Group to unbind.
- *
- * Return: 0 on success, a negative error code otherwise.
- */
-static int
-group_unbind_locked(struct panthor_group *group)
-{
- struct panthor_device *ptdev = group->ptdev;
- struct panthor_csg_slot *slot;
-
- lockdep_assert_held(&ptdev->scheduler->lock);
-
- if (drm_WARN_ON(&ptdev->base, group->csg_id < 0 || group->csg_id >= MAX_CSGS))
- return -EINVAL;
-
- if (drm_WARN_ON(&ptdev->base, group->state == PANTHOR_CS_GROUP_ACTIVE))
- return -EINVAL;
-
- slot = &ptdev->scheduler->csg_slots[group->csg_id];
- panthor_vm_idle(group->vm);
- group->csg_id = -1;
-
- /* Tiler OOM events will be re-issued next time the group is scheduled. */
- atomic_set(&group->tiler_oom, 0);
- if (cancel_work(&group->tiler_oom_work))
- group_put(group);
-
- for (u32 i = 0; i < group->queue_count; i++)
- group->queues[i]->doorbell_id = -1;
-
- slot->group = NULL;
-
- group_put(group);
- return 0;
-}
-
static bool
group_is_idle(struct panthor_group *group)
{
@@ -1082,8 +1003,9 @@ group_can_run(struct panthor_group *group)
{
return group->state != PANTHOR_CS_GROUP_TERMINATED &&
group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
- !group->destroyed && group->fatal_queues == 0 &&
- !group->timedout;
+ !group->destroyed &&
+ !atomic_read(&group->fatal_queues) &&
+ !atomic_read(&group->timedout);
}
static bool
@@ -1482,7 +1404,7 @@ cs_slot_process_fatal_event_locked(struct panthor_device *ptdev,
u32 fatal;
u64 info;
- lockdep_assert_held(&sched->lock);
+ lockdep_assert_held(&sched->events_lock);
cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id);
fatal = cs_iface->output->fatal;
@@ -1492,7 +1414,7 @@ cs_slot_process_fatal_event_locked(struct panthor_device *ptdev,
drm_warn(&ptdev->base, "CS_FATAL: pid=%d, comm=%s\n",
group->task_info.pid, group->task_info.comm);
- group->fatal_queues |= BIT(cs_id);
+ atomic_or(BIT(cs_id), &group->fatal_queues);
}
if (CS_EXCEPTION_TYPE(fatal) == DRM_PANTHOR_EXCEPTION_CS_UNRECOVERABLE) {
@@ -1530,7 +1452,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev,
u32 fault;
u64 info;
- lockdep_assert_held(&sched->lock);
+ lockdep_assert_held(&sched->events_lock);
cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id);
fault = cs_iface->output->fault;
@@ -1622,7 +1544,7 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id)
*/
if (ret && ret != -ENOMEM) {
drm_warn(&ptdev->base, "Failed to extend the tiler heap\n");
- group->fatal_queues |= BIT(cs_id);
+ atomic_or(BIT(cs_id), &group->fatal_queues);
sched_queue_delayed_work(sched, tick, 0);
goto out_put_heap_pool;
}
@@ -1682,7 +1604,7 @@ cs_slot_process_tiler_oom_event_locked(struct panthor_device *ptdev,
struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id];
struct panthor_group *group = csg_slot->group;
- lockdep_assert_held(&sched->lock);
+ lockdep_assert_held(&sched->events_lock);
if (drm_WARN_ON(&ptdev->base, !group))
return;
@@ -1703,7 +1625,7 @@ static bool cs_slot_process_irq_locked(struct panthor_device *ptdev,
struct panthor_fw_cs_iface *cs_iface;
u32 req, ack, events;
- lockdep_assert_held(&ptdev->scheduler->lock);
+ lockdep_assert_held(&ptdev->scheduler->events_lock);
cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id);
req = cs_iface->input->req;
@@ -1731,9 +1653,7 @@ static void csg_slot_process_idle_event_locked(struct panthor_device *ptdev, u32
{
struct panthor_scheduler *sched = ptdev->scheduler;
- lockdep_assert_held(&sched->lock);
-
- sched->might_have_idle_groups = true;
+ lockdep_assert_held(&sched->events_lock);
/* Schedule a tick so we can evict idle groups and schedule non-idle
* ones. This will also update runtime PM and devfreq busy/idle states,
@@ -1742,16 +1662,102 @@ static void csg_slot_process_idle_event_locked(struct panthor_device *ptdev, u32
sched_queue_delayed_work(sched, tick, 0);
}
+static void update_fdinfo_stats(struct panthor_job *job)
+{
+ struct panthor_group *group = job->group;
+ struct panthor_queue *queue = group->queues[job->queue_idx];
+ struct panthor_gpu_usage *fdinfo = &group->fdinfo.data;
+ struct panthor_job_profiling_data *slots = queue->profiling.slots->kmap;
+ struct panthor_job_profiling_data *data = &slots[job->profiling.slot];
+
+ scoped_guard(spinlock, &group->fdinfo.lock) {
+ if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_CYCLES)
+ fdinfo->cycles += data->cycles.after - data->cycles.before;
+ if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP)
+ fdinfo->time += data->time.after - data->time.before;
+ }
+}
+
+static bool queue_check_job_completion(struct panthor_queue *queue)
+{
+ struct panthor_syncobj_64b *syncobj = NULL;
+ struct panthor_job *job, *job_tmp;
+ bool cookie, progress = false;
+ LIST_HEAD(done_jobs);
+
+ cookie = dma_fence_begin_signalling();
+ scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) {
+ list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
+ if (!syncobj) {
+ struct panthor_group *group = job->group;
+
+ syncobj = group->syncobjs->kmap +
+ (job->queue_idx * sizeof(*syncobj));
+ }
+
+ if (syncobj->seqno < job->done_fence->seqno)
+ break;
+
+ list_move_tail(&job->node, &done_jobs);
+ dma_fence_signal_locked(job->done_fence);
+ }
+
+ if (list_empty(&queue->fence_ctx.in_flight_jobs)) {
+ /* If we have no job left, we cancel the timer, and reset remaining
+ * time to its default so it can be restarted next time
+ * queue_resume_timeout() is called.
+ */
+ queue_suspend_timeout_locked(queue);
+
+ /* If there's no job pending, we consider it progress to avoid a
+ * spurious timeout if the timeout handler and the sync update
+ * handler raced.
+ */
+ progress = true;
+ } else if (!list_empty(&done_jobs)) {
+ queue_reset_timeout_locked(queue);
+ progress = true;
+ }
+ }
+ dma_fence_end_signalling(cookie);
+
+ list_for_each_entry_safe(job, job_tmp, &done_jobs, node) {
+ if (job->profiling.mask)
+ update_fdinfo_stats(job);
+ list_del_init(&job->node);
+ panthor_job_put(&job->base);
+ }
+
+ return progress;
+}
+
+static void group_check_job_completion(struct panthor_group *group)
+{
+ u32 queue_idx;
+ bool cookie;
+
+ cookie = dma_fence_begin_signalling();
+ for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) {
+ struct panthor_queue *queue = group->queues[queue_idx];
+
+ if (!queue)
+ continue;
+
+ queue_check_job_completion(queue);
+ }
+ dma_fence_end_signalling(cookie);
+}
+
static void csg_slot_sync_update_locked(struct panthor_device *ptdev,
u32 csg_id)
{
struct panthor_csg_slot *csg_slot = &ptdev->scheduler->csg_slots[csg_id];
struct panthor_group *group = csg_slot->group;
- lockdep_assert_held(&ptdev->scheduler->lock);
+ lockdep_assert_held(&ptdev->scheduler->events_lock);
if (group)
- group_queue_work(group, sync_upd);
+ group_check_job_completion(group);
sched_queue_work(ptdev->scheduler, sync_upd);
}
@@ -1763,14 +1769,14 @@ csg_slot_process_progress_timer_event_locked(struct panthor_device *ptdev, u32 c
struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id];
struct panthor_group *group = csg_slot->group;
- lockdep_assert_held(&sched->lock);
+ lockdep_assert_held(&sched->events_lock);
group = csg_slot->group;
if (!drm_WARN_ON(&ptdev->base, !group)) {
drm_warn(&ptdev->base, "CSG_PROGRESS_TIMER_EVENT: pid=%d, comm=%s\n",
group->task_info.pid, group->task_info.comm);
- group->timedout = true;
+ atomic_set(&group->timedout, true);
}
drm_warn(&ptdev->base, "CSG slot %d progress timeout\n", csg_id);
@@ -1784,7 +1790,7 @@ static void sched_process_csg_irq_locked(struct panthor_device *ptdev, u32 csg_i
struct panthor_fw_csg_iface *csg_iface;
u32 ring_cs_db_mask = 0;
- lockdep_assert_held(&ptdev->scheduler->lock);
+ lockdep_assert_held(&ptdev->scheduler->events_lock);
if (drm_WARN_ON(&ptdev->base, csg_id >= ptdev->scheduler->csg_slot_count))
return;
@@ -1842,7 +1848,7 @@ static void sched_process_idle_event_locked(struct panthor_device *ptdev)
{
struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
- lockdep_assert_held(&ptdev->scheduler->lock);
+ lockdep_assert_held(&ptdev->scheduler->events_lock);
/* Acknowledge the idle event and schedule a tick. */
panthor_fw_update_reqs(glb_iface, req, glb_iface->output->ack, GLB_IDLE);
@@ -1858,7 +1864,7 @@ static void sched_process_global_irq_locked(struct panthor_device *ptdev)
struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
u32 req, ack, evts;
- lockdep_assert_held(&ptdev->scheduler->lock);
+ lockdep_assert_held(&ptdev->scheduler->events_lock);
req = READ_ONCE(glb_iface->input->req);
ack = READ_ONCE(glb_iface->output->ack);
@@ -1868,14 +1874,17 @@ static void sched_process_global_irq_locked(struct panthor_device *ptdev)
sched_process_idle_event_locked(ptdev);
}
-static void process_fw_events_work(struct work_struct *work)
+/**
+ * panthor_sched_report_fw_events() - Report FW events to the scheduler.
+ * @ptdev: Device.
+ * @events: Bitmask of pending FW events to report.
+ */
+void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events)
{
- struct panthor_scheduler *sched = container_of(work, struct panthor_scheduler,
- fw_events_work);
- u32 events = atomic_xchg(&sched->fw_events, 0);
- struct panthor_device *ptdev = sched->ptdev;
+ if (!ptdev->scheduler)
+ return;
- mutex_lock(&sched->lock);
+ guard(spinlock)(&ptdev->scheduler->events_lock);
if (events & JOB_INT_GLOBAL_IF) {
sched_process_global_irq_locked(ptdev);
@@ -1888,22 +1897,89 @@ static void process_fw_events_work(struct work_struct *work)
sched_process_csg_irq_locked(ptdev, csg_id);
events &= ~BIT(csg_id);
}
+}
- mutex_unlock(&sched->lock);
+/**
+ * group_bind_locked() - Bind a group to a group slot
+ * @group: Group.
+ * @csg_id: Slot.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+static int
+group_bind_locked(struct panthor_group *group, u32 csg_id)
+{
+ struct panthor_device *ptdev = group->ptdev;
+ int ret;
+
+ lockdep_assert_held(&ptdev->scheduler->lock);
+
+ if (drm_WARN_ON(&ptdev->base, group->csg_id != -1 || csg_id >= MAX_CSGS ||
+ ptdev->scheduler->csg_slots[csg_id].group))
+ return -EINVAL;
+
+ ret = panthor_vm_active(group->vm);
+ if (ret)
+ return ret;
+
+ group_get(group);
+
+ /* Dummy doorbell allocation: doorbell is assigned to the group and
+ * all queues use the same doorbell.
+ *
+ * TODO: Implement LRU-based doorbell assignment, so the most often
+ * updated queues get their own doorbell, thus avoiding useless checks
+ * on queues belonging to the same group that are rarely updated.
+ */
+ for (u32 i = 0; i < group->queue_count; i++)
+ group->queues[i]->doorbell_id = csg_id + 1;
+
+ scoped_guard(spinlock, &ptdev->scheduler->events_lock) {
+ ptdev->scheduler->csg_slots[csg_id].group = group;
+ group->csg_id = csg_id;
+ }
+
+ return 0;
}
/**
- * panthor_sched_report_fw_events() - Report FW events to the scheduler.
- * @ptdev: Device.
- * @events: Bitmask of pending FW events to report.
+ * group_unbind_locked() - Unbind a group from a slot.
+ * @group: Group to unbind.
+ *
+ * Return: 0 on success, a negative error code otherwise.
*/
-void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events)
+static int
+group_unbind_locked(struct panthor_group *group)
{
- if (!ptdev->scheduler)
- return;
+ struct panthor_device *ptdev = group->ptdev;
+
+ lockdep_assert_held(&ptdev->scheduler->lock);
+
+ if (drm_WARN_ON(&ptdev->base, group->csg_id < 0 || group->csg_id >= MAX_CSGS))
+ return -EINVAL;
- atomic_or(events, &ptdev->scheduler->fw_events);
- sched_queue_work(ptdev->scheduler, fw_events);
+ if (drm_WARN_ON(&ptdev->base, group->state == PANTHOR_CS_GROUP_ACTIVE))
+ return -EINVAL;
+
+ scoped_guard(spinlock, &ptdev->scheduler->events_lock) {
+ /* Process all pending IRQs before returning the slot. */
+ sched_process_csg_irq_locked(ptdev, group->csg_id);
+ ptdev->scheduler->csg_slots[group->csg_id].group = NULL;
+ group->csg_id = -1;
+ }
+
+ panthor_vm_idle(group->vm);
+
+ /* Tiler OOM events will be re-issued next time the group is scheduled. */
+ atomic_set(&group->tiler_oom, 0);
+ if (cancel_work(&group->tiler_oom_work))
+ group_put(group);
+
+ for (u32 i = 0; i < group->queue_count; i++)
+ group->queues[i]->doorbell_id = -1;
+
+ group_put(group);
+ return 0;
}
static const char *fence_get_driver_name(struct dma_fence *fence)
@@ -2136,11 +2212,12 @@ tick_ctx_init(struct panthor_scheduler *sched,
* CSG IRQs, so we can flag the faulty queue.
*/
if (panthor_vm_has_unhandled_faults(group->vm)) {
- sched_process_csg_irq_locked(ptdev, i);
+ scoped_guard(spinlock, &sched->events_lock)
+ sched_process_csg_irq_locked(ptdev, i);
/* No fatal fault reported, flag all queues as faulty. */
- if (!group->fatal_queues)
- group->fatal_queues |= GENMASK(group->queue_count - 1, 0);
+ atomic_cmpxchg(&group->fatal_queues, 0,
+ GENMASK(group->queue_count - 1, 0));
}
tick_ctx_insert_old_group(sched, ctx, group);
@@ -2173,9 +2250,9 @@ group_term_post_processing(struct panthor_group *group)
struct panthor_syncobj_64b *syncobj;
int err;
- if (group->fatal_queues & BIT(i))
+ if (atomic_read(&group->fatal_queues) & BIT(i))
err = -EINVAL;
- else if (group->timedout)
+ else if (atomic_read(&group->timedout))
err = -ETIMEDOUT;
else
err = -ECANCELED;
@@ -2331,16 +2408,8 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
/* Unbind evicted groups. */
for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) {
- list_for_each_entry(group, &ctx->old_groups[prio], run_node) {
- /* This group is gone. Process interrupts to clear
- * any pending interrupts before we start the new
- * group.
- */
- if (group->csg_id >= 0)
- sched_process_csg_irq_locked(ptdev, group->csg_id);
-
+ list_for_each_entry(group, &ctx->old_groups[prio], run_node)
group_unbind_locked(group);
- }
}
for (i = 0; i < sched->csg_slot_count; i++) {
@@ -2866,7 +2935,7 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
/* We consider group suspension failures as fatal and flag the
* group as unusable by setting timedout=true.
*/
- csg_slot->group->timedout = true;
+ atomic_set(&csg_slot->group->timedout, true);
csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
CSG_STATE_TERMINATE,
@@ -2915,10 +2984,12 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
u32 csg_id = ffs(slot_mask) - 1;
struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id];
- if (flush_caches_failed)
+ if (flush_caches_failed) {
csg_slot->group->state = PANTHOR_CS_GROUP_TERMINATED;
- else
+ } else {
+ guard(spinlock)(&sched->events_lock);
csg_slot_sync_update_locked(ptdev, csg_id);
+ }
slot_mask &= ~BIT(csg_id);
}
@@ -2932,10 +3003,6 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
continue;
group_get(group);
-
- if (group->csg_id >= 0)
- sched_process_csg_irq_locked(ptdev, group->csg_id);
-
group_unbind_locked(group);
drm_WARN_ON(&group->ptdev->base, !list_empty(&group->run_node));
@@ -3018,22 +3085,6 @@ void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed)
}
}
-static void update_fdinfo_stats(struct panthor_job *job)
-{
- struct panthor_group *group = job->group;
- struct panthor_queue *queue = group->queues[job->queue_idx];
- struct panthor_gpu_usage *fdinfo = &group->fdinfo.data;
- struct panthor_job_profiling_data *slots = queue->profiling.slots->kmap;
- struct panthor_job_profiling_data *data = &slots[job->profiling.slot];
-
- scoped_guard(spinlock, &group->fdinfo.lock) {
- if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_CYCLES)
- fdinfo->cycles += data->cycles.after - data->cycles.before;
- if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP)
- fdinfo->time += data->time.after - data->time.before;
- }
-}
-
void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
{
struct panthor_group_pool *gpool = pfile->groups;
@@ -3054,80 +3105,6 @@ void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
xa_unlock(&gpool->xa);
}
-static bool queue_check_job_completion(struct panthor_queue *queue)
-{
- struct panthor_syncobj_64b *syncobj = NULL;
- struct panthor_job *job, *job_tmp;
- bool cookie, progress = false;
- LIST_HEAD(done_jobs);
-
- cookie = dma_fence_begin_signalling();
- scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) {
- list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
- if (!syncobj) {
- struct panthor_group *group = job->group;
-
- syncobj = group->syncobjs->kmap +
- (job->queue_idx * sizeof(*syncobj));
- }
-
- if (syncobj->seqno < job->done_fence->seqno)
- break;
-
- list_move_tail(&job->node, &done_jobs);
- dma_fence_signal_locked(job->done_fence);
- }
-
- if (list_empty(&queue->fence_ctx.in_flight_jobs)) {
- /* If we have no job left, we cancel the timer, and reset remaining
- * time to its default so it can be restarted next time
- * queue_resume_timeout() is called.
- */
- queue_suspend_timeout_locked(queue);
-
- /* If there's no job pending, we consider it progress to avoid a
- * spurious timeout if the timeout handler and the sync update
- * handler raced.
- */
- progress = true;
- } else if (!list_empty(&done_jobs)) {
- queue_reset_timeout_locked(queue);
- progress = true;
- }
- }
- dma_fence_end_signalling(cookie);
-
- list_for_each_entry_safe(job, job_tmp, &done_jobs, node) {
- if (job->profiling.mask)
- update_fdinfo_stats(job);
- list_del_init(&job->node);
- panthor_job_put(&job->base);
- }
-
- return progress;
-}
-
-static void group_sync_upd_work(struct work_struct *work)
-{
- struct panthor_group *group =
- container_of(work, struct panthor_group, sync_upd_work);
- u32 queue_idx;
- bool cookie;
-
- cookie = dma_fence_begin_signalling();
- for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) {
- struct panthor_queue *queue = group->queues[queue_idx];
-
- if (!queue)
- continue;
-
- queue_check_job_completion(queue);
- }
- dma_fence_end_signalling(cookie);
-
- group_put(group);
-}
-
struct panthor_job_ringbuf_instrs {
u64 buffer[MAX_INSTRS_PER_JOB];
u32 count;
@@ -3426,7 +3403,7 @@ queue_timedout_job(struct drm_sched_job *sched_job)
queue_stop(queue, job);
mutex_lock(&sched->lock);
- group->timedout = true;
+ atomic_set(&group->timedout, true);
if (group->csg_id >= 0) {
sched_queue_delayed_work(ptdev->scheduler, tick, 0);
} else {
@@ -3678,7 +3655,6 @@ int panthor_group_create(struct panthor_file *pfile,
if (!group)
return -ENOMEM;
- spin_lock_init(&group->fatal_lock);
kref_init(&group->refcount);
group->state = PANTHOR_CS_GROUP_CREATED;
group->csg_id = -1;
@@ -3695,7 +3671,6 @@ int panthor_group_create(struct panthor_file *pfile,
INIT_LIST_HEAD(&group->wait_node);
INIT_LIST_HEAD(&group->run_node);
INIT_WORK(&group->term_work, group_term_work);
- INIT_WORK(&group->sync_upd_work, group_sync_upd_work);
INIT_WORK(&group->tiler_oom_work, group_tiler_oom_work);
INIT_WORK(&group->release_work, group_release_work);
@@ -3850,12 +3825,13 @@ int panthor_group_get_state(struct panthor_file *pfile,
memset(get_state, 0, sizeof(*get_state));
mutex_lock(&sched->lock);
- if (group->timedout)
+ if (atomic_read(&group->timedout))
get_state->state |= DRM_PANTHOR_GROUP_STATE_TIMEDOUT;
- if (group->fatal_queues) {
+
+ get_state->fatal_queues = atomic_read(&group->fatal_queues);
+ if (get_state->fatal_queues)
get_state->state |= DRM_PANTHOR_GROUP_STATE_FATAL_FAULT;
- get_state->fatal_queues = group->fatal_queues;
- }
+
if (group->innocent)
get_state->state |= DRM_PANTHOR_GROUP_STATE_INNOCENT;
mutex_unlock(&sched->lock);
@@ -4066,7 +4042,6 @@ void panthor_sched_unplug(struct panthor_device *ptdev)
struct panthor_scheduler *sched = ptdev->scheduler;
disable_delayed_work_sync(&sched->tick_work);
- disable_work_sync(&sched->fw_events_work);
disable_work_sync(&sched->sync_upd_work);
mutex_lock(&sched->lock);
@@ -4151,7 +4126,8 @@ int panthor_sched_init(struct panthor_device *ptdev)
sched->tick_period = msecs_to_jiffies(10);
INIT_DELAYED_WORK(&sched->tick_work, tick_work);
INIT_WORK(&sched->sync_upd_work, sync_upd_work);
- INIT_WORK(&sched->fw_events_work, process_fw_events_work);
+
+ spin_lock_init(&sched->events_lock);
ret = drmm_mutex_init(&ptdev->base, &sched->lock);
if (ret)
diff --git a/drivers/gpu/drm/panthor/panthor_trace.h b/drivers/gpu/drm/panthor/panthor_trace.h
index 6ffeb4fe6599..bd8652549ab4 100644
--- a/drivers/gpu/drm/panthor/panthor_trace.h
+++ b/drivers/gpu/drm/panthor/panthor_trace.h
@@ -76,6 +76,44 @@ TRACE_EVENT(gpu_job_irq,
__entry->events, __entry->duration_ns)
);
+/**
+ * gpu_cache_flush - emitted after cache flush completes
+ * @dev: pointer to the &struct device, for printing the device name
+ * @l2: "l2" flush flags
+ * @lsc: "lsc" flush flags
+ * @other: "other" flush flags
+ * @duration_ns: how long the cache flush operation took, in nanoseconds
+ * @ret: return status, 0 == success, negative errno on error
+ *
+ * Begins measuring after any initial lock contention around the locks needed
+ * for flushing caches, but before the actual cache flush is requested. Stops
+ * measuring and is emitted after flush operation is over.
+ */
+TRACE_EVENT(gpu_cache_flush,
+ TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other,
+ u32 duration_ns, int ret),
+ TP_ARGS(dev, l2, lsc, other, duration_ns, ret),
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name(dev))
+ __field(u32, l2)
+ __field(u32, lsc)
+ __field(u32, other)
+ __field(u32, duration_ns)
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ __assign_str(dev_name);
+ __entry->l2 = l2;
+ __entry->lsc = lsc;
+ __entry->other = other;
+ __entry->duration_ns = duration_ns;
+ __entry->ret = ret;
+ ),
+ TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x duration_ns=%u ret=%d",
+ __get_str(dev_name), __entry->l2, __entry->lsc,
+ __entry->other, __entry->duration_ns, __entry->ret)
+);
+
#endif /* __PANTHOR_TRACE_H__ */
#undef TRACE_INCLUDE_PATH
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 2fc41fb90aaa..f657fe24fc88 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -418,7 +418,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.destroy = qxl_crtc_destroy,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
DRM_CRTC_VBLANK_TIMER_FUNCS,
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
index 6c07c406f725..59c2a4b76137 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
@@ -997,23 +997,20 @@ static void rcar_du_crtc_cleanup(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
-static void rcar_du_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *rcar_du_crtc_create_state(struct drm_crtc *crtc)
{
struct rcar_du_crtc_state *state;
- if (crtc->state) {
- rcar_du_crtc_atomic_destroy_state(crtc, crtc->state);
- crtc->state = NULL;
- }
-
state = kzalloc_obj(*state);
if (state == NULL)
- return;
+ return ERR_PTR(-ENOMEM);
state->crc.source = VSP1_DU_CRC_NONE;
state->crc.index = 0;
- __drm_atomic_helper_crtc_reset(crtc, &state->state);
+ __drm_atomic_helper_crtc_state_init(&state->state, crtc);
+
+ return &state->state;
}
static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -1155,7 +1152,7 @@ unlock:
}
static const struct drm_crtc_funcs crtc_funcs_gen2 = {
- .reset = rcar_du_crtc_reset,
+ .atomic_create_state = rcar_du_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@@ -1166,7 +1163,7 @@ static const struct drm_crtc_funcs crtc_funcs_gen2 = {
};
static const struct drm_crtc_funcs crtc_funcs_gen3 = {
- .reset = rcar_du_crtc_reset,
+ .atomic_create_state = rcar_du_crtc_create_state,
.destroy = rcar_du_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
index d0f01aa642a7..ab2f24cc7683 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
@@ -332,20 +332,17 @@ static void rzg2l_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,
kfree(to_rzg2l_crtc_state(state));
}
-static void rzg2l_du_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *rzg2l_du_crtc_create_state(struct drm_crtc *crtc)
{
struct rzg2l_du_crtc_state *state;
- if (crtc->state) {
- rzg2l_du_crtc_atomic_destroy_state(crtc, crtc->state);
- crtc->state = NULL;
- }
-
state = kzalloc_obj(*state);
if (!state)
- return;
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(&state->state, crtc);
- __drm_atomic_helper_crtc_reset(crtc, &state->state);
+ return &state->state;
}
static int rzg2l_du_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -365,7 +362,7 @@ static void rzg2l_du_crtc_disable_vblank(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs crtc_funcs_rz = {
- .reset = rzg2l_du_crtc_reset,
+ .atomic_create_state = rzg2l_du_crtc_create_state,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = rzg2l_du_crtc_atomic_duplicate_state,
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c
index 1a2b9b68af6f..328a36215e39 100644
--- a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c
@@ -21,6 +21,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@@ -29,7 +30,6 @@
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <video/videomode.h>
@@ -364,7 +364,7 @@ static void shmob_drm_disable_vblank(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = shmob_drm_crtc_page_flip,
@@ -436,6 +436,10 @@ static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.mode_fixup = shmob_drm_encoder_mode_fixup,
};
+static const struct drm_encoder_funcs shmob_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
/* -----------------------------------------------------------------------------
* Encoder
*/
@@ -448,8 +452,8 @@ int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
encoder->possible_crtcs = 1;
- ret = drm_simple_encoder_init(&sdev->ddev, encoder,
- DRM_MODE_ENCODER_DPI);
+ ret = drm_encoder_init(&sdev->ddev, encoder, &shmob_encoder_funcs,
+ DRM_MODE_ENCODER_DPI, NULL);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 0090d8ff0c79..bd02597dc2d7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1656,17 +1656,17 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc,
kfree(s);
}
-static void vop_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *vop_crtc_create_state(struct drm_crtc *crtc)
{
- struct rockchip_crtc_state *crtc_state = kzalloc_obj(*crtc_state);
+ struct rockchip_crtc_state *crtc_state;
- if (crtc->state)
- vop_crtc_destroy_state(crtc, crtc->state);
+ crtc_state = kzalloc_obj(*crtc_state);
+ if (!crtc_state)
+ return ERR_PTR(-ENOMEM);
- if (crtc_state)
- __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ __drm_atomic_helper_crtc_state_init(&crtc_state->base, crtc);
+
+ return &crtc_state->base;
}
#ifdef CONFIG_DRM_ANALOGIX_DP
@@ -1738,7 +1738,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.destroy = drm_crtc_cleanup,
- .reset = vop_crtc_reset,
+ .atomic_create_state = vop_crtc_create_state,
.atomic_duplicate_state = vop_crtc_duplicate_state,
.atomic_destroy_state = vop_crtc_destroy_state,
.enable_vblank = vop_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 4cce3e336f5b..b2f3a579bbe5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -2300,24 +2300,24 @@ static void vop2_crtc_destroy_state(struct drm_crtc *crtc,
kfree(vcstate);
}
-static void vop2_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *vop2_crtc_create_state(struct drm_crtc *crtc)
{
- struct rockchip_crtc_state *vcstate = kzalloc_obj(*vcstate);
+ struct rockchip_crtc_state *vcstate;
- if (crtc->state)
- vop2_crtc_destroy_state(crtc, crtc->state);
+ vcstate = kzalloc_obj(*vcstate);
+ if (!vcstate)
+ return ERR_PTR(-ENOMEM);
- if (vcstate)
- __drm_atomic_helper_crtc_reset(crtc, &vcstate->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ __drm_atomic_helper_crtc_state_init(&vcstate->base, crtc);
+
+ return &vcstate->base;
}
static const struct drm_crtc_funcs vop2_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.destroy = drm_crtc_cleanup,
- .reset = vop2_crtc_reset,
+ .atomic_create_state = vop2_crtc_create_state,
.atomic_duplicate_state = vop2_crtc_duplicate_state,
.atomic_destroy_state = vop2_crtc_destroy_state,
.enable_vblank = vop2_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index a4a7efdbf229..bf97508a45b9 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -207,14 +207,18 @@ EXPORT_SYMBOL(drm_sched_entity_modify_sched);
static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity)
{
- rmb(); /* for list_empty to work without lock */
+ bool idle = false;
+
+ spin_lock(&entity->lock);
if (list_empty(&entity->list) ||
spsc_queue_count(&entity->job_queue) == 0 ||
entity->stopped)
- return true;
+ idle = true;
- return false;
+ spin_unlock(&entity->lock);
+
+ return idle;
}
/**
diff --git a/drivers/gpu/drm/scheduler/tests/tests_basic.c b/drivers/gpu/drm/scheduler/tests/tests_basic.c
index da5c4952780f..34bbb2c27913 100644
--- a/drivers/gpu/drm/scheduler/tests/tests_basic.c
+++ b/drivers/gpu/drm/scheduler/tests/tests_basic.c
@@ -90,7 +90,7 @@ static const struct drm_sched_basic_params drm_sched_basic_cases[] = {
.num_entities = 1,
},
{
- .description = "A chain of dependent jobs across multiple entities",
+ .description = "A chain of dependent jobs in a single entity",
.queue_depth = 100,
.job_us = 1000,
.num_entities = 1,
diff --git a/drivers/gpu/drm/sitronix/st7571.c b/drivers/gpu/drm/sitronix/st7571.c
index 20954c33eca9..fc3ba5e8600f 100644
--- a/drivers/gpu/drm/sitronix/st7571.c
+++ b/drivers/gpu/drm/sitronix/st7571.c
@@ -424,7 +424,7 @@ static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs st7571_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/sitronix/st7920.c b/drivers/gpu/drm/sitronix/st7920.c
index d320391801f3..0a75e9319080 100644
--- a/drivers/gpu/drm/sitronix/st7920.c
+++ b/drivers/gpu/drm/sitronix/st7920.c
@@ -453,17 +453,17 @@ static void st7920_primary_plane_atomic_disable(struct drm_plane *plane,
}
/* Called during init to allocate the plane's atomic state. */
-static void st7920_primary_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *st7920_primary_plane_create_state(struct drm_plane *plane)
{
struct st7920_plane_state *st7920_state;
- drm_WARN_ON_ONCE(plane->dev, plane->state);
-
st7920_state = kzalloc_obj(*st7920_state);
if (!st7920_state)
- return;
+ return ERR_PTR(-ENOMEM);
+
+ __drm_gem_shadow_plane_state_init(plane, &st7920_state->base);
- __drm_gem_reset_shadow_plane(plane, &st7920_state->base);
+ return &(&st7920_state->base)->base;
}
static struct drm_plane_state *st7920_primary_plane_duplicate_state(struct drm_plane *plane)
@@ -507,7 +507,7 @@ static const struct drm_plane_helper_funcs st7920_primary_plane_helper_funcs = {
static const struct drm_plane_funcs st7920_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = st7920_primary_plane_reset,
+ .atomic_create_state = st7920_primary_plane_create_state,
.atomic_duplicate_state = st7920_primary_plane_duplicate_state,
.atomic_destroy_state = st7920_primary_plane_destroy_state,
.destroy = drm_plane_cleanup,
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 0b0fc6fe3df2..feec4933e3e6 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -1310,17 +1310,17 @@ static void ssd133x_primary_plane_atomic_disable(struct drm_plane *plane,
}
/* Called during init to allocate the plane's atomic state. */
-static void ssd130x_primary_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *ssd130x_primary_plane_create_state(struct drm_plane *plane)
{
struct ssd130x_plane_state *ssd130x_state;
- drm_WARN_ON_ONCE(plane->dev, plane->state);
-
ssd130x_state = kzalloc_obj(*ssd130x_state);
if (!ssd130x_state)
- return;
+ return ERR_PTR(-ENOMEM);
+
+ __drm_gem_shadow_plane_state_init(plane, &ssd130x_state->base);
- __drm_gem_reset_shadow_plane(plane, &ssd130x_state->base);
+ return &(&ssd130x_state->base)->base;
}
static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane)
@@ -1383,7 +1383,7 @@ static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[]
static const struct drm_plane_funcs ssd130x_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = ssd130x_primary_plane_reset,
+ .atomic_create_state = ssd130x_primary_plane_create_state,
.atomic_duplicate_state = ssd130x_primary_plane_duplicate_state,
.atomic_destroy_state = ssd130x_primary_plane_destroy_state,
.destroy = drm_plane_cleanup,
@@ -1467,7 +1467,7 @@ static int ssd133x_crtc_atomic_check(struct drm_crtc *crtc,
}
/* Called during init to allocate the CRTC's atomic state. */
-static void ssd130x_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *ssd130x_crtc_create_state(struct drm_crtc *crtc)
{
struct ssd130x_crtc_state *ssd130x_state;
@@ -1475,9 +1475,11 @@ static void ssd130x_crtc_reset(struct drm_crtc *crtc)
ssd130x_state = kzalloc_obj(*ssd130x_state);
if (!ssd130x_state)
- return;
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(&ssd130x_state->base, crtc);
- __drm_atomic_helper_crtc_reset(crtc, &ssd130x_state->base);
+ return &ssd130x_state->base;
}
static struct drm_crtc_state *ssd130x_crtc_duplicate_state(struct drm_crtc *crtc)
@@ -1534,7 +1536,7 @@ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs[] = {
};
static const struct drm_crtc_funcs ssd130x_crtc_funcs = {
- .reset = ssd130x_crtc_reset,
+ .atomic_create_state = ssd130x_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c
index 66990398b903..7f0bc1e31076 100644
--- a/drivers/gpu/drm/sprd/sprd_dpu.c
+++ b/drivers/gpu/drm/sprd/sprd_dpu.c
@@ -707,7 +707,7 @@ static const struct drm_crtc_funcs sprd_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = sprd_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c
index f0e98653d35a..c69612728f2b 100644
--- a/drivers/gpu/drm/sti/sti_crtc.c
+++ b/drivers/gpu/drm/sti/sti_crtc.c
@@ -328,7 +328,7 @@ static const struct drm_crtc_funcs sti_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.destroy = sti_crtc_destroy,
.set_property = sti_crtc_set_property,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.late_register = sti_crtc_late_register,
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 8818dcf98da7..775846bb502b 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -1209,7 +1209,7 @@ static void ltdc_crtc_atomic_print_state(struct drm_printer *p,
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = ltdc_crtc_enable_vblank,
@@ -1221,7 +1221,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = ltdc_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 3fa1f4fda0f0..e6a20e2f0ade 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -183,7 +183,7 @@ static const struct drm_crtc_funcs sun4i_crtc_funcs = {
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.set_config = drm_atomic_helper_set_config,
.enable_vblank = sun4i_crtc_enable_vblank,
.disable_vblank = sun4i_crtc_disable_vblank,
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 97253d631b2b..91844a396a2b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -292,6 +292,7 @@ static void sun4i_hdmi_connector_reset(struct drm_connector *connector)
}
static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
+ .debugfs_init = drm_hdmi_connector_debugfs_init,
.detect = sun4i_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = sun4i_hdmi_connector_reset,
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index 00756ab78b4e..bad102134726 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -213,7 +213,7 @@ static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.destroy = drm_plane_cleanup,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.update_plane = drm_atomic_helper_update_plane,
};
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index 09f668c8af24..2e9cda45c04e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -293,7 +293,7 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.destroy = drm_plane_cleanup,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.update_plane = drm_atomic_helper_update_plane,
};
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
index 547f2327af5e..f2a3717be670 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
@@ -134,15 +134,15 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
.atomic_disable = drm_sysfb_plane_helper_atomic_disable, \
.get_scanout_buffer = drm_sysfb_plane_helper_get_scanout_buffer
-void drm_sysfb_plane_reset(struct drm_plane *plane);
+struct drm_plane_state *drm_sysfb_plane_atomic_create_state(struct drm_plane *plane);
struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane);
void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *plane_state);
#define DRM_SYSFB_PLANE_FUNCS \
- .reset = drm_sysfb_plane_reset, \
.update_plane = drm_atomic_helper_update_plane, \
.disable_plane = drm_atomic_helper_disable_plane, \
+ .atomic_create_state = drm_sysfb_plane_atomic_create_state, \
.atomic_duplicate_state = drm_sysfb_plane_atomic_duplicate_state, \
.atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state
@@ -171,12 +171,12 @@ int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_
.mode_valid = drm_sysfb_crtc_helper_mode_valid, \
.atomic_check = drm_sysfb_crtc_helper_atomic_check
-void drm_sysfb_crtc_reset(struct drm_crtc *crtc);
+struct drm_crtc_state *drm_sysfb_crtc_create_state(struct drm_crtc *crtc);
struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc);
void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state);
#define DRM_SYSFB_CRTC_FUNCS \
- .reset = drm_sysfb_crtc_reset, \
+ .atomic_create_state = drm_sysfb_crtc_create_state, \
.set_config = drm_atomic_helper_set_config, \
.page_flip = drm_atomic_helper_page_flip, \
.atomic_duplicate_state = drm_sysfb_crtc_atomic_duplicate_state, \
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
index d2de29caf89e..b92cc4b7d2ad 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
@@ -376,28 +376,28 @@ void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
struct drm_device *dev = plane->dev;
struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
struct iosys_map dst = sysfb->fb_addr;
- struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
unsigned int dst_pitch = sysfb->fb_pitch;
const struct drm_format_info *dst_format = sysfb->fb_format;
struct drm_rect dst_clip;
- unsigned long lines, linepixels, i;
+ unsigned long lines, linepixels, dst_size, i;
int idx;
- drm_rect_init(&dst_clip,
- plane_state->src_x >> 16, plane_state->src_y >> 16,
- plane_state->src_w >> 16, plane_state->src_h >> 16);
+ drm_rect_init(&dst_clip, 0, 0,
+ sysfb->fb_mode.hdisplay, sysfb->fb_mode.vdisplay);
lines = drm_rect_height(&dst_clip);
linepixels = drm_rect_width(&dst_clip);
+ dst_size = min_t(unsigned long,
+ drm_format_info_min_pitch(dst_format, 0, linepixels),
+ dst_pitch);
if (!drm_dev_enter(dev, &idx))
return;
/* Clear buffer to black if disabled */
- dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
for (i = 0; i < lines; ++i) {
- memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
+ memset_io(dst_vmap, 0, dst_size);
dst_vmap += dst_pitch;
}
@@ -420,20 +420,32 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
}
EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
-void drm_sysfb_plane_reset(struct drm_plane *plane)
+/**
+ * drm_sysfb_plane_atomic_create_state - creates sysfb plane state
+ * @plane: the plane
+ *
+ * This function implements struct &drm_plane_funcs.atomic_create_state for
+ * sysfb planes. It allocates the plane state of type
+ * struct drm_sysfb_plane_state.
+ *
+ * Returns:
+ * A pointer to a new plane state on success, or an ERR_PTR()-encoded
+ * error code otherwise.
+ */
+struct drm_plane_state *
+drm_sysfb_plane_atomic_create_state(struct drm_plane *plane)
{
struct drm_sysfb_plane_state *sysfb_plane_state;
- if (plane->state)
- drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state));
-
sysfb_plane_state = kzalloc_obj(*sysfb_plane_state);
- if (sysfb_plane_state)
- __drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base);
- else
- __drm_gem_reset_shadow_plane(plane, NULL);
+ if (!sysfb_plane_state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_gem_shadow_plane_state_init(plane, &sysfb_plane_state->base);
+
+ return &sysfb_plane_state->base.base;
}
-EXPORT_SYMBOL(drm_sysfb_plane_reset);
+EXPORT_SYMBOL(drm_sysfb_plane_atomic_create_state);
struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane)
{
@@ -515,23 +527,21 @@ int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_
}
EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
-void drm_sysfb_crtc_reset(struct drm_crtc *crtc)
+struct drm_crtc_state *drm_sysfb_crtc_create_state(struct drm_crtc *crtc)
{
struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
struct drm_sysfb_crtc_state *sysfb_crtc_state;
- if (crtc->state)
- drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state));
-
sysfb_crtc_state = kzalloc_obj(*sysfb_crtc_state);
- if (sysfb_crtc_state) {
- sysfb_crtc_state->format = sysfb->fb_format;
- __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base);
- } else {
- __drm_atomic_helper_crtc_reset(crtc, NULL);
- }
+ if (!sysfb_crtc_state)
+ return ERR_PTR(-ENOMEM);
+
+ sysfb_crtc_state->format = sysfb->fb_format;
+ __drm_atomic_helper_crtc_state_init(&sysfb_crtc_state->base, crtc);
+
+ return &sysfb_crtc_state->base;
}
-EXPORT_SYMBOL(drm_sysfb_crtc_reset);
+EXPORT_SYMBOL(drm_sysfb_crtc_create_state);
struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index fefc3761a4bc..b0bfa946e697 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1492,17 +1492,17 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
-static void tegra_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *tegra_crtc_create_state(struct drm_crtc *crtc)
{
- struct tegra_dc_state *state = kzalloc_obj(*state);
+ struct tegra_dc_state *state;
- if (crtc->state)
- tegra_crtc_atomic_destroy_state(crtc, crtc->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- if (state)
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
- else
- __drm_atomic_helper_crtc_reset(crtc, NULL);
+ __drm_atomic_helper_crtc_state_init(&state->base, crtc);
+
+ return &state->base;
}
static struct drm_crtc_state *
@@ -1911,7 +1911,7 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.set_config = drm_atomic_helper_set_config,
.destroy = tegra_dc_destroy,
- .reset = tegra_crtc_reset,
+ .atomic_create_state = tegra_crtc_create_state,
.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
.late_register = tegra_dc_late_register,
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index cb88aafbd36f..7b595310e737 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -20,11 +20,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_file.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "dc.h"
#include "drm.h"
@@ -1055,6 +1055,10 @@ tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
return err;
}
+static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
.disable = tegra_dsi_encoder_disable,
.enable = tegra_dsi_encoder_enable,
@@ -1078,8 +1082,14 @@ static int tegra_dsi_init(struct host1x_client *client)
&tegra_dsi_connector_helper_funcs);
dsi->output.connector.dpms = DRM_MODE_DPMS_OFF;
- drm_simple_encoder_init(drm, &dsi->output.encoder,
- DRM_MODE_ENCODER_DSI);
+ err = drm_encoder_init(drm, &dsi->output.encoder,
+ &tegra_dsi_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, NULL);
+ if (err) {
+ drm_err(drm, "failed to initialize encoder: %d\n", err);
+ return err;
+ }
+
drm_encoder_helper_add(&dsi->output.encoder,
&tegra_dsi_encoder_helper_funcs);
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c
index 0cb30910773f..2148b0c59815 100644
--- a/drivers/gpu/drm/tegra/plane.c
+++ b/drivers/gpu/drm/tegra/plane.c
@@ -24,28 +24,24 @@ static void tegra_plane_destroy(struct drm_plane *plane)
kfree(p);
}
-static void tegra_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *tegra_plane_atomic_create_state(struct drm_plane *plane)
{
struct tegra_plane *p = to_tegra_plane(plane);
struct tegra_plane_state *state;
unsigned int i;
- if (plane->state)
- __drm_atomic_helper_plane_destroy_state(plane->state);
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
- kfree(plane->state);
- plane->state = NULL;
+ state->base.plane = plane;
+ state->base.zpos = p->index;
+ state->base.normalized_zpos = p->index;
- state = kzalloc_obj(*state);
- if (state) {
- plane->state = &state->base;
- plane->state->plane = plane;
- plane->state->zpos = p->index;
- plane->state->normalized_zpos = p->index;
-
- for (i = 0; i < 3; i++)
- state->iova[i] = DMA_MAPPING_ERROR;
- }
+ for (i = 0; i < 3; i++)
+ state->iova[i] = DMA_MAPPING_ERROR;
+
+ return &state->base;
}
static struct drm_plane_state *
@@ -131,7 +127,7 @@ const struct drm_plane_funcs tegra_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = tegra_plane_destroy,
- .reset = tegra_plane_reset,
+ .atomic_create_state = tegra_plane_atomic_create_state,
.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
.atomic_destroy_state = tegra_plane_atomic_destroy_state,
.format_mod_supported = tegra_plane_format_mod_supported,
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index e67fbb2362e6..bc1c93c7554c 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -9,7 +9,8 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge_connector.h>
-#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_print.h>
#include "drm.h"
#include "dc.h"
@@ -194,6 +195,10 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
return err;
}
+static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
.disable = tegra_rgb_encoder_disable,
.enable = tegra_rgb_encoder_enable,
@@ -305,7 +310,13 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
if (!dc->rgb)
return -ENODEV;
- drm_simple_encoder_init(drm, &output->encoder, DRM_MODE_ENCODER_LVDS);
+ err = drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
+ if (err) {
+ drm_err(drm, "failed to initialize encoder: %d\n", err);
+ return err;
+ }
+
drm_encoder_helper_add(&output->encoder,
&tegra_rgb_encoder_helper_funcs);
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index 727766444d63..1afa9b162355 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -217,7 +217,7 @@ static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {
static const struct drm_crtc_funcs default_crtc_funcs = {
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
};
/**
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index 58d5eb033bdb..2a0ec11a6c90 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -2157,47 +2157,15 @@ int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane,
return 0;
}
-static
-dma_addr_t dispc_plane_state_dma_addr(const struct drm_plane_state *state)
-{
- struct drm_framebuffer *fb = state->fb;
- struct drm_gem_dma_object *gem;
- u32 x = state->src_x >> 16;
- u32 y = state->src_y >> 16;
-
- gem = drm_fb_dma_get_gem_obj(state->fb, 0);
-
- return gem->dma_addr + fb->offsets[0] + x * fb->format->cpp[0] +
- y * fb->pitches[0];
-}
-
-static
-dma_addr_t dispc_plane_state_p_uv_addr(const struct drm_plane_state *state)
-{
- struct drm_framebuffer *fb = state->fb;
- struct drm_gem_dma_object *gem;
- u32 x = state->src_x >> 16;
- u32 y = state->src_y >> 16;
-
- if (WARN_ON(state->fb->format->num_planes != 2))
- return 0;
-
- gem = drm_fb_dma_get_gem_obj(fb, 1);
-
- return gem->dma_addr + fb->offsets[1] +
- (x * fb->format->cpp[1] / fb->format->hsub) +
- (y * fb->pitches[1] / fb->format->vsub);
-}
-
void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane,
- const struct drm_plane_state *state,
+ struct drm_plane_state *state,
u32 hw_videoport)
{
bool lite = dispc->feat->vid_info[hw_plane].is_lite;
u32 fourcc = state->fb->format->format;
u16 cpp = state->fb->format->cpp[0];
u32 fb_width = state->fb->pitches[0] / cpp;
- dma_addr_t dma_addr = dispc_plane_state_dma_addr(state);
+ dma_addr_t dma_addr = drm_fb_dma_get_gem_addr(state->fb, state, 0);
struct dispc_scaling_params scale;
dispc_vid_calc_scaling(dispc, state, &scale, lite);
@@ -2229,7 +2197,7 @@ void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane,
if (state->fb->format->num_planes == 2) {
u16 cpp_uv = state->fb->format->cpp[1];
u32 fb_width_uv = state->fb->pitches[1] / cpp_uv;
- dma_addr_t p_uv_addr = dispc_plane_state_p_uv_addr(state);
+ dma_addr_t p_uv_addr = drm_fb_dma_get_gem_addr(state->fb, state, 1);
dispc_vid_write(dispc, hw_plane,
DISPC_VID_BA_UV_0, p_uv_addr & 0xffffffff);
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
index 739d211d0018..2183254f1ab7 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -140,7 +140,7 @@ int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane,
const struct drm_plane_state *state,
u32 hw_videoport);
void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane,
- const struct drm_plane_state *state,
+ struct drm_plane_state *state,
u32 hw_videoport);
void dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable);
const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len);
diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c
index 698f8d964ca0..10dbcc6cdf6a 100644
--- a/drivers/gpu/drm/tidss/tidss_encoder.c
+++ b/drivers/gpu/drm/tidss/tidss_encoder.c
@@ -9,11 +9,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_crtc.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_panel.h>
#include <drm/drm_of.h>
-#include <drm/drm_simple_kms_helper.h>
#include "tidss_crtc.h"
#include "tidss_drv.h"
@@ -81,6 +81,10 @@ static const struct drm_bridge_funcs tidss_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
};
+static const struct drm_encoder_funcs tidss_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
int tidss_encoder_create(struct tidss_device *tidss,
struct drm_bridge *next_bridge,
u32 encoder_type, u32 possible_crtcs)
@@ -95,8 +99,8 @@ int tidss_encoder_create(struct tidss_device *tidss,
if (IS_ERR(t_enc))
return PTR_ERR(t_enc);
- ret = drm_simple_encoder_init(&tidss->ddev, &t_enc->encoder,
- encoder_type);
+ ret = drm_encoder_init(&tidss->ddev, &t_enc->encoder,
+ &tidss_encoder_funcs, encoder_type, NULL);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c
index ef71b9957961..4297d9c1125e 100644
--- a/drivers/gpu/drm/tiny/appletbdrm.c
+++ b/drivers/gpu/drm/tiny/appletbdrm.c
@@ -505,17 +505,17 @@ static void appletbdrm_primary_plane_helper_atomic_disable(struct drm_plane *pla
drm_dev_exit(idx);
}
-static void appletbdrm_primary_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *appletbdrm_primary_plane_create_state(struct drm_plane *plane)
{
struct appletbdrm_plane_state *appletbdrm_state;
- WARN_ON(plane->state);
-
appletbdrm_state = kzalloc_obj(*appletbdrm_state);
if (!appletbdrm_state)
- return;
+ return ERR_PTR(-ENOMEM);
+
+ __drm_gem_shadow_plane_state_init(plane, &appletbdrm_state->base);
- __drm_gem_reset_shadow_plane(plane, &appletbdrm_state->base);
+ return &(&appletbdrm_state->base)->base;
}
static struct drm_plane_state *appletbdrm_primary_plane_duplicate_state(struct drm_plane *plane)
@@ -568,7 +568,7 @@ static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs
static const struct drm_plane_funcs appletbdrm_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = appletbdrm_primary_plane_reset,
+ .atomic_create_state = appletbdrm_primary_plane_create_state,
.atomic_duplicate_state = appletbdrm_primary_plane_duplicate_state,
.atomic_destroy_state = appletbdrm_primary_plane_destroy_state,
.destroy = drm_plane_cleanup,
@@ -605,7 +605,7 @@ static const struct drm_crtc_helper_funcs appletbdrm_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs appletbdrm_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c
index e2d957e51505..524d387f1d53 100644
--- a/drivers/gpu/drm/tiny/bochs.c
+++ b/drivers/gpu/drm/tiny/bochs.c
@@ -550,7 +550,7 @@ static const struct drm_crtc_helper_funcs bochs_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs bochs_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c
index 3bf23fcf6574..63c242e25662 100644
--- a/drivers/gpu/drm/tiny/cirrus-qemu.c
+++ b/drivers/gpu/drm/tiny/cirrus-qemu.c
@@ -419,7 +419,7 @@ static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs cirrus_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/tiny/pixpaper.c b/drivers/gpu/drm/tiny/pixpaper.c
index d02ac26d007c..b69e7c30607e 100644
--- a/drivers/gpu/drm/tiny/pixpaper.c
+++ b/drivers/gpu/drm/tiny/pixpaper.c
@@ -963,7 +963,7 @@ static const struct drm_plane_helper_funcs pixpaper_plane_helper_funcs = {
static const struct drm_crtc_funcs pixpaper_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
diff --git a/drivers/gpu/drm/tiny/sharp-memory.c b/drivers/gpu/drm/tiny/sharp-memory.c
index 7efd7b567f3b..434e73bd25a5 100644
--- a/drivers/gpu/drm/tiny/sharp-memory.c
+++ b/drivers/gpu/drm/tiny/sharp-memory.c
@@ -345,7 +345,7 @@ static const struct drm_crtc_helper_funcs sharp_memory_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs sharp_memory_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index ef56c18ded1b..1b9477381a1d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -506,7 +506,6 @@ struct ttm_bo_alloc_state {
*
* @bo: The buffer to allocate the backing store of
* @place: The place to attempt allocation in
- * @ctx: ttm_operation_ctx associated with this allocation
* @force_space: If we should evict buffers to force space
* @res: On allocation success, the resulting struct ttm_resource.
* @alloc_state: Object holding allocation state such as charged cgroups.
@@ -1450,7 +1449,7 @@ out:
return ret;
}
-const struct ttm_lru_walk_ops ttm_swap_ops = {
+static const struct ttm_lru_walk_ops ttm_swap_ops = {
.process_bo = ttm_bo_swapout_cb,
};
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index 289711035b67..dee085406ac2 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -400,7 +400,7 @@ static const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs udl_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c
index 8e4e5fc9d3c5..d2d75367b077 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_mode.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c
@@ -250,7 +250,7 @@ static const struct drm_crtc_funcs vbox_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
/* .gamma_set = vbox_crtc_gamma_set, */
.destroy = vbox_crtc_destroy,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c
index ade3309ae042..247863f8e2c5 100644
--- a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c
@@ -14,7 +14,7 @@ static const struct drm_crtc_helper_funcs vc4_dummy_crtc_helper_funcs = {
static const struct drm_crtc_funcs vc4_dummy_crtc_funcs = {
.atomic_destroy_state = vc4_crtc_destroy_state,
.atomic_duplicate_state = vc4_crtc_duplicate_state,
- .reset = vc4_crtc_reset,
+ .atomic_create_state = vc4_crtc_create_state,
};
struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 4572b0b9bae1..f42cd5353230 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1135,21 +1135,19 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc,
drm_atomic_helper_crtc_destroy_state(crtc, state);
}
-void vc4_crtc_reset(struct drm_crtc *crtc)
+struct drm_crtc_state *vc4_crtc_create_state(struct drm_crtc *crtc)
{
struct vc4_crtc_state *vc4_crtc_state;
- if (crtc->state)
- vc4_crtc_destroy_state(crtc, crtc->state);
-
vc4_crtc_state = kzalloc_obj(*vc4_crtc_state);
if (!vc4_crtc_state) {
- crtc->state = NULL;
- return;
+ return ERR_PTR(-ENOMEM);
}
vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED;
- __drm_atomic_helper_crtc_reset(crtc, &vc4_crtc_state->base);
+ __drm_atomic_helper_crtc_state_init(&vc4_crtc_state->base, crtc);
+
+ return &vc4_crtc_state->base;
}
int vc4_crtc_late_register(struct drm_crtc *crtc)
@@ -1170,7 +1168,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
.set_property = NULL,
.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
- .reset = vc4_crtc_reset,
+ .atomic_create_state = vc4_crtc_create_state,
.atomic_duplicate_state = vc4_crtc_duplicate_state,
.atomic_destroy_state = vc4_crtc_destroy_state,
.enable_vblank = vc4_enable_vblank,
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 0f5958c1e6b6..649032174dbd 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -949,7 +949,7 @@ int vc4_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc);
void vc4_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
-void vc4_crtc_reset(struct drm_crtc *crtc);
+struct drm_crtc_state *vc4_crtc_create_state(struct drm_crtc *crtc);
void vc4_crtc_handle_vblank(struct vc4_crtc *crtc);
void vc4_crtc_send_vblank(struct drm_crtc *crtc);
int vc4_crtc_late_register(struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 17c8635c5afa..ea1f2013b20e 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -513,6 +513,7 @@ static void vc4_hdmi_connector_reset(struct drm_connector *connector)
}
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
+ .debugfs_init = drm_hdmi_connector_debugfs_init,
.force = drm_atomic_helper_connector_hdmi_force,
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = vc4_hdmi_connector_reset,
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index 6c433270214d..20c9fc9283a8 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -363,20 +363,17 @@ static void vc4_plane_destroy_state(struct drm_plane *plane,
}
/* Called during init to allocate the plane's atomic state. */
-static void vc4_plane_reset(struct drm_plane *plane)
+static struct drm_plane_state *vc4_plane_create_state(struct drm_plane *plane)
{
struct vc4_plane_state *vc4_state;
- if (plane->state)
- __drm_atomic_helper_plane_destroy_state(plane->state);
-
- kfree(plane->state);
-
vc4_state = kzalloc_obj(*vc4_state);
if (!vc4_state)
- return;
+ return ERR_PTR(-ENOMEM);
- __drm_atomic_helper_plane_reset(plane, &vc4_state->base);
+ __drm_atomic_helper_plane_state_init(&vc4_state->base, plane);
+
+ return &vc4_state->base;
}
static void vc4_dlist_counter_increment(struct vc4_plane_state *vc4_state)
@@ -2495,7 +2492,7 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
static const struct drm_plane_funcs vc4_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = vc4_plane_reset,
+ .atomic_create_state = vc4_plane_create_state,
.atomic_duplicate_state = vc4_plane_duplicate_state,
.atomic_destroy_state = vc4_plane_destroy_state,
.format_mod_supported = vc4_format_mod_supported,
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index bc3f366fc3e6..c469f8b1068b 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -425,7 +425,7 @@ static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {}
static const struct drm_crtc_funcs vc4_txp_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = vc4_page_flip,
- .reset = vc4_crtc_reset,
+ .atomic_create_state = vc4_crtc_create_state,
.atomic_duplicate_state = vc4_crtc_duplicate_state,
.atomic_destroy_state = vc4_crtc_destroy_state,
.enable_vblank = vc4_txp_enable_vblank,
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
index 0b8a35d09cd2..c24c1588cbe6 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -149,7 +149,7 @@ static const struct drm_crtc_funcs vs_crtc_funcs = {
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.set_config = drm_atomic_helper_set_config,
.enable_vblank = vs_crtc_enable_vblank,
.disable_vblank = vs_crtc_disable_vblank,
diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c
index fa4f601dd0c8..05e7a04e614c 100644
--- a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c
@@ -12,6 +12,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
+#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
@@ -176,7 +177,8 @@ static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
break;
}
- dma_addr = vs_fb_get_dma_addr(fb, &state->src);
+ /* hardware handles clipping as seen below */
+ dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0);
regmap_write(dc->regs, VSDC_CURSOR_ADDRESS(output),
lower_32_bits(dma_addr));
@@ -199,7 +201,7 @@ static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
} else {
regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
VSDC_CURSOR_CONFIG_X_OFF_MASK,
- -state->crtc_x);
+ VSDC_CURSOR_CONFIG_X_OFF(-state->crtc_x));
regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
VSDC_CURSOR_LOCATION_X_MASK, 0);
}
@@ -213,7 +215,7 @@ static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
} else {
regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
VSDC_CURSOR_CONFIG_Y_OFF_MASK,
- -state->crtc_y);
+ VSDC_CURSOR_CONFIG_Y_OFF(-state->crtc_y));
regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
VSDC_CURSOR_LOCATION_Y_MASK, 0);
}
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c
index 7ddb9d2dcb83..f189c0bba5b7 100644
--- a/drivers/gpu/drm/verisilicon/vs_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_plane.c
@@ -107,26 +107,6 @@ int drm_format_to_vs_format(u32 drm_format, struct vs_format *vs_format)
return 0;
}
-dma_addr_t vs_fb_get_dma_addr(struct drm_framebuffer *fb,
- const struct drm_rect *src_rect)
-{
- struct drm_gem_dma_object *gem;
- dma_addr_t dma_addr;
-
- /* Get the physical address of the buffer in memory */
- gem = drm_fb_dma_get_gem_obj(fb, 0);
-
- /* Compute the start of the displayed memory */
- dma_addr = gem->dma_addr + fb->offsets[0];
-
- /* Fixup framebuffer address for src coordinates */
- dma_addr += drm_format_info_min_pitch(fb->format, 0,
- src_rect->x1 >> 16);
- dma_addr += (src_rect->y1 >> 16) * fb->pitches[0];
-
- return dma_addr;
-}
-
struct drm_plane_state *vs_plane_duplicate_state(struct drm_plane *plane)
{
struct vs_plane_state *vs_state, *vs_state_old;
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
index 32ed1d70f3b8..e4f82f1fc6e9 100644
--- a/drivers/gpu/drm/verisilicon/vs_plane.h
+++ b/drivers/gpu/drm/verisilicon/vs_plane.h
@@ -75,8 +75,6 @@ static inline struct vs_plane_state *to_vs_plane_state(struct drm_plane_state *s
}
int drm_format_to_vs_format(u32 drm_format, struct vs_format *vs_format);
-dma_addr_t vs_fb_get_dma_addr(struct drm_framebuffer *fb,
- const struct drm_rect *src_rect);
struct drm_plane_state *vs_plane_duplicate_state(struct drm_plane *plane);
void vs_plane_destroy_state(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/verisilicon/vs_primary_plane.c b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
index 1f2be41ae496..2750016a7f2c 100644
--- a/drivers/gpu/drm/verisilicon/vs_primary_plane.c
+++ b/drivers/gpu/drm/verisilicon/vs_primary_plane.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
+#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
@@ -126,7 +127,11 @@ static void vs_primary_plane_atomic_update(struct drm_plane *plane,
VSDC_FB_CONFIG_UV_SWIZZLE_EN,
vs_state->format.uv_swizzle);
- dma_addr = vs_fb_get_dma_addr(fb, &state->src);
+ /*
+ * Primary plane cannot be moved, no clipping is involved,
+ * so the non-clipped framebuffer address can be used.
+ */
+ dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0);
regmap_write(dc->regs, VSDC_FB_ADDRESS(output),
lower_32_bits(dma_addr));
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index a1a875a0c706..b60aab07e78d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -28,11 +28,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_vblank_helper.h>
@@ -55,7 +55,7 @@ static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
DRM_CRTC_VBLANK_TIMER_FUNCS,
@@ -232,6 +232,10 @@ static enum drm_mode_status virtio_gpu_conn_mode_valid(struct drm_connector *con
return MODE_BAD;
}
+static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
.mode_set = virtio_gpu_enc_mode_set,
.enable = virtio_gpu_enc_enable,
@@ -306,7 +310,11 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
if (vgdev->has_edid)
drm_connector_attach_edid_property(connector);
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_VIRTUAL);
+ ret = drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
+ DRM_MODE_ENCODER_VIRTUAL, NULL);
+ if (ret)
+ return ret;
+
drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
encoder->possible_crtcs = 1 << index;
diff --git a/drivers/gpu/drm/vkms/tests/gen_yuv_conversion.py b/drivers/gpu/drm/vkms/tests/gen_yuv_conversion.py
new file mode 100755
index 000000000000..f4d226137cc2
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/gen_yuv_conversion.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0+
+"""
+Derive the VKMS YCbCr -> RGB conversion constants and the vkms-format KUnit
+reference values.
+
+It produces:
+ 1. The S31.32 fixed-point conversion matrices used in
+ drivers/gpu/drm/vkms/vkms_formats.c and asserted by
+ vkms_format_test_conversion_matrix.
+ 2. The 16-bit reference YCbCr inputs for the limited-range
+ yuv_u16_to_argb_u16 round-trip cases (the values changed by the fix).
+
+Requires numpy.
+"""
+
+import numpy as np
+
+FP = 1 << 32 # S31.32 scale: fixed = round(coeff * 2^32)
+
+# ITU-R luma weights (Kr, Kb); Kg = 1 - Kr - Kb.
+ENCODINGS = {
+ "BT601": (0.299, 0.114),
+ "BT709": (0.2126, 0.0722),
+ "BT2020": (0.2627, 0.0593), # non-constant luminance
+}
+
+# 8-bit studio-range levels: luma 16..235 (range 219), chroma 16..240
+# (range 224, neutral 128); full-range maximum is 2^n - 1 = 255.
+FULL_MAX = 255
+Y_RANGE = 235 - 16 # 219
+C_RANGE = 240 - 16 # 224
+
+# 16-bit RGB test colors.
+COLORS = {
+ "white": (0xffff, 0xffff, 0xffff),
+ "gray": (0x8080, 0x8080, 0x8080),
+ "black": (0x0000, 0x0000, 0x0000),
+ "red": (0xffff, 0x0000, 0x0000),
+ "green": (0x0000, 0xffff, 0x0000),
+ "blue": (0x0000, 0x0000, 0xffff),
+}
+
+
+def rgb_matrix(kr, kb, limited):
+ """Normalized YCbCr -> RGB (Y in [0,1], Cb/Cr in [-0.5, 0.5])."""
+ kg = 1.0 - kr - kb
+ # Standard full-range relations.
+ m = [[1.0, 0.0, 2 * (1 - kr)],
+ [1.0, -2 * (1 - kb) * kb / kg, -2 * (1 - kr) * kr / kg],
+ [1.0, 2 * (1 - kb), 0.0]]
+ if limited:
+ # Expand studio input range to full range: luma by 255/219,
+ # chroma by 255/224 (relative to full-range maximum 2^n - 1).
+ ys, cs = FULL_MAX / Y_RANGE, FULL_MAX / C_RANGE
+ m = [[r[0] * ys, r[1] * cs, r[2] * cs] for r in m]
+ return m
+
+
+def ref_yuv(m, y_offset):
+ """16-bit YCbCr that VKMS decodes back to each RGB color.
+
+ VKMS decode: [R,G,B] = M . [Y - y_offset*257, Cb - 128*257, Cr - 128*257]
+ (8-bit offsets lifted to 16-bit by *257 = 65535/255), so invert and
+ re-add the offsets.
+ """
+ inv = np.linalg.inv(np.array(m, float))
+ off = np.array([y_offset * 257, 128 * 257, 128 * 257])
+ return {name: tuple(int(min(max(round(v), 0), 0xffff))
+ for v in inv @ np.array(rgb, float) + off)
+ for name, rgb in COLORS.items()}
+
+print(f"== RGB REFERENCE VALUES ==")
+for name, rgb in COLORS.items():
+ print(f" {name:6} {{ {rgb[0]:#06x}, {rgb[1]:#06x}, {rgb[2]:#06x} }}")
+print()
+
+for enc, (kr, kb) in ENCODINGS.items():
+ for limited in (False, True):
+ m = rgb_matrix(kr, kb, limited)
+ y_offset = 16 if limited else 0
+ print(f"== {enc} {'LIMITED' if limited else 'FULL'} (y_offset={y_offset}) ==")
+ for row in m:
+ print(" { " + ", ".join(str(round(v * FP)) for v in row) + " },")
+ for name, yuv in ref_yuv(m, y_offset).items():
+ print(f" {name:6} {{ {yuv[0]:#06x}, {yuv[1]:#06x}, {yuv[2]:#06x} }}")
+ print()
diff --git a/drivers/gpu/drm/vkms/tests/vkms_format_test.c b/drivers/gpu/drm/vkms/tests/vkms_format_test.c
index a7788fbc45dc..bbb1ad18c99f 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_format_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_format_test.c
@@ -54,6 +54,8 @@ struct yuv_u16_to_argb_u16_case {
* The YUV color representation were acquired via the colour python framework.
* Below are the function calls used for generating each case.
*
+ * The limited-range cases are generated by gen_yuv_conversion.py.
+ *
* For more information got to the docs:
* https://colour.readthedocs.io/en/master/generated/colour.RGB_to_YCbCr.html
*/
@@ -101,12 +103,12 @@ static struct yuv_u16_to_argb_u16_case yuv_u16_to_argb_u16_cases[] = {
.range = DRM_COLOR_YCBCR_LIMITED_RANGE,
.n_colors = 6,
.colors = {
- { "white", { 0xeb00, 0x8000, 0x8000 }, { 0xffff, 0xffff, 0xffff, 0xffff }},
- { "gray", { 0x7dee, 0x8000, 0x8000 }, { 0xffff, 0x8080, 0x8080, 0x8080 }},
- { "black", { 0x1000, 0x8000, 0x8000 }, { 0xffff, 0x0000, 0x0000, 0x0000 }},
- { "red", { 0x517b, 0x5a34, 0xf000 }, { 0xffff, 0xffff, 0x0000, 0x0000 }},
- { "green", { 0x908e, 0x35cc, 0x2237 }, { 0xffff, 0x0000, 0xffff, 0x0000 }},
- { "blue", { 0x28f7, 0xf000, 0x6dc9 }, { 0xffff, 0x0000, 0x0000, 0xffff }},
+ { "white", { 0xebeb, 0x8080, 0x8080 }, { 0xffff, 0xffff, 0xffff, 0xffff }},
+ { "gray", { 0x7e6c, 0x8080, 0x8080 }, { 0xffff, 0x8080, 0x8080, 0x8080 }},
+ { "black", { 0x1010, 0x8080, 0x8080 }, { 0xffff, 0x0000, 0x0000, 0x0000 }},
+ { "red", { 0x51cd, 0x5a8e, 0xf0f0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }},
+ { "green", { 0x911e, 0x3602, 0x2259 }, { 0xffff, 0x0000, 0xffff, 0x0000 }},
+ { "blue", { 0x2920, 0xf0f0, 0x6e37 }, { 0xffff, 0x0000, 0x0000, 0xffff }},
}
},
/*
@@ -151,12 +153,12 @@ static struct yuv_u16_to_argb_u16_case yuv_u16_to_argb_u16_cases[] = {
.range = DRM_COLOR_YCBCR_LIMITED_RANGE,
.n_colors = 6,
.colors = {
- { "white", { 0xeb00, 0x8000, 0x8000 }, { 0xffff, 0xffff, 0xffff, 0xffff }},
- { "gray", { 0x7dee, 0x8000, 0x8000 }, { 0xffff, 0x8080, 0x8080, 0x8080 }},
- { "black", { 0x1000, 0x8000, 0x8000 }, { 0xffff, 0x0000, 0x0000, 0x0000 }},
- { "red", { 0x3e8f, 0x6656, 0xf000 }, { 0xffff, 0xffff, 0x0000, 0x0000 }},
- { "green", { 0xaca1, 0x29aa, 0x1a45 }, { 0xffff, 0x0000, 0xffff, 0x0000 }},
- { "blue", { 0x1fd0, 0xf000, 0x75bb }, { 0xffff, 0x0000, 0x0000, 0xffff }},
+ { "white", { 0xebeb, 0x8080, 0x8080 }, { 0xffff, 0xffff, 0xffff, 0xffff }},
+ { "gray", { 0x7e6c, 0x8080, 0x8080 }, { 0xffff, 0x8080, 0x8080, 0x8080 }},
+ { "black", { 0x1010, 0x8080, 0x8080 }, { 0xffff, 0x0000, 0x0000, 0x0000 }},
+ { "red", { 0x3ece, 0x66bc, 0xf0f0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }},
+ { "green", { 0xad4e, 0x29d4, 0x1a5f }, { 0xffff, 0x0000, 0xffff, 0x0000 }},
+ { "blue", { 0x1ff0, 0xf0f0, 0x7631 }, { 0xffff, 0x0000, 0x0000, 0xffff }},
}
},
/*
@@ -201,12 +203,12 @@ static struct yuv_u16_to_argb_u16_case yuv_u16_to_argb_u16_cases[] = {
.range = DRM_COLOR_YCBCR_LIMITED_RANGE,
.n_colors = 6,
.colors = {
- { "white", { 0xeb00, 0x8000, 0x8000 }, { 0xffff, 0xffff, 0xffff, 0xffff }},
- { "gray", { 0x7dee, 0x8000, 0x8000 }, { 0xffff, 0x8080, 0x8080, 0x8080 }},
- { "black", { 0x1000, 0x8000, 0x8000 }, { 0xffff, 0x0000, 0x0000, 0x0000 }},
- { "red", { 0x4988, 0x60b9, 0xf000 }, { 0xffff, 0xffff, 0x0000, 0x0000 }},
- { "green", { 0xa47b, 0x2f47, 0x1902 }, { 0xffff, 0x0000, 0xffff, 0x0000 }},
- { "blue", { 0x1cfd, 0xf000, 0x76fe }, { 0xffff, 0x0000, 0x0000, 0xffff }},
+ { "white", { 0xebeb, 0x8080, 0x8080 }, { 0xffff, 0xffff, 0xffff, 0xffff }},
+ { "gray", { 0x7e6c, 0x8080, 0x8080 }, { 0xffff, 0x8080, 0x8080, 0x8080 }},
+ { "black", { 0x1010, 0x8080, 0x8080 }, { 0xffff, 0x0000, 0x0000, 0x0000 }},
+ { "red", { 0x49d2, 0x611a, 0xf0f0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }},
+ { "green", { 0xa520, 0x2f76, 0x191b }, { 0xffff, 0x0000, 0xffff, 0x0000 }},
+ { "blue", { 0x1d1a, 0xf0f0, 0x7775 }, { 0xffff, 0x0000, 0x0000, 0xffff }},
}
},
};
@@ -232,7 +234,7 @@ static void vkms_format_test_yuv_u16_to_argb_u16(struct kunit *test)
struct conversion_matrix matrix;
get_conversion_matrix_to_argb_u16
- (DRM_FORMAT_NV12, param->encoding, param->range, &matrix);
+ (DRM_FORMAT_NV12, param->encoding, param->range, false, &matrix);
argb = argb_u16_from_yuv161616(&matrix, color->yuv.y, color->yuv.u,
color->yuv.v);
diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c
index 071f3a8d2e7c..9424b1994239 100644
--- a/drivers/gpu/drm/vkms/vkms_colorop.c
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -12,11 +12,19 @@ static const u64 supported_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
+static const u64 supported_fm =
+ BIT(DRM_COLOROP_FM_YCBCR601_FULL_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR601_LIMITED_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR709_FULL_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR709_LIMITED_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB) |
+ BIT(DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB);
+
static const struct drm_colorop_funcs vkms_colorop_funcs = {
.destroy = drm_colorop_destroy,
};
-#define MAX_COLOR_PIPELINE_OPS 4
+#define MAX_COLOR_PIPELINE_OPS 5
static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list)
{
@@ -27,7 +35,25 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr
memset(ops, 0, sizeof(ops));
- /* 1st op: 1d curve */
+ /* 1st op: Fixed Matrix (YUV to RGB) */
+ ops[i] = kzalloc_obj(*ops[i]);
+ if (!ops[i]) {
+ drm_err(dev, "KMS: Failed to allocate colorop\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_fixed_matrix_init(dev, ops[i], plane, &vkms_colorop_funcs,
+ supported_fm,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ list->type = ops[i]->base.id;
+
+ i++;
+
+ /* 2nd op: 1d curve */
ops[i] = kzalloc_obj(*ops[i]);
if (!ops[i]) {
drm_err(dev, "KMS: Failed to allocate colorop\n");
@@ -41,11 +67,11 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr
if (ret)
goto cleanup;
- list->type = ops[i]->base.id;
+ drm_colorop_set_next_property(ops[i - 1], ops[i]);
i++;
- /* 2nd op: 3x4 matrix */
+ /* 3rd op: 3x4 matrix */
ops[i] = kzalloc_obj(*ops[i]);
if (!ops[i]) {
drm_err(dev, "KMS: Failed to allocate colorop\n");
@@ -62,7 +88,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr
i++;
- /* 3rd op: 3x4 matrix */
+ /* 4th op: 3x4 matrix */
ops[i] = kzalloc_obj(*ops[i]);
if (!ops[i]) {
drm_err(dev, "KMS: Failed to allocate colorop\n");
@@ -79,7 +105,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr
i++;
- /* 4th op: 1d curve */
+ /* 5th op: 1d curve */
ops[i] = kzalloc_obj(*ops[i]);
if (!ops[i]) {
drm_err(dev, "KMS: Failed to allocate colorop\n");
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 83d217085ad0..899120cd07ac 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -187,6 +187,12 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colo
if (colorop_state->data)
apply_3x4_matrix(pixel,
(struct drm_color_ctm_3x4 *)colorop_state->data->data);
+ } else if (colorop->type == DRM_COLOROP_FIXED_MATRIX) {
+ /*
+ * Fixed Matrix is a no-op here. YUV-to-RGB conversion
+ * is handled at format read time via the
+ * conversion_matrix set in vkms_plane_atomic_update().
+ */
}
}
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index 079abfba427d..fe17f49305a9 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -86,22 +86,24 @@ static void vkms_atomic_crtc_destroy_state(struct drm_crtc *crtc,
kfree(vkms_state);
}
-static void vkms_atomic_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *vkms_atomic_crtc_create_state(struct drm_crtc *crtc)
{
- struct vkms_crtc_state *vkms_state = kzalloc_obj(*vkms_state);
+ struct vkms_crtc_state *vkms_state;
+
+ vkms_state = kzalloc_obj(*vkms_state);
+ if (!vkms_state)
+ return ERR_PTR(-ENOMEM);
- if (crtc->state)
- vkms_atomic_crtc_destroy_state(crtc, crtc->state);
+ __drm_atomic_helper_crtc_state_init(&vkms_state->base, crtc);
+ INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
- __drm_atomic_helper_crtc_reset(crtc, &vkms_state->base);
- if (vkms_state)
- INIT_WORK(&vkms_state->composer_work, vkms_composer_worker);
+ return &vkms_state->base;
}
static const struct drm_crtc_funcs vkms_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = vkms_atomic_crtc_reset,
+ .atomic_create_state = vkms_atomic_crtc_create_state,
.atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
.atomic_destroy_state = vkms_atomic_crtc_destroy_state,
DRM_CRTC_VBLANK_TIMER_FUNCS,
diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c
index dfb8e13cba87..964b574d9ed7 100644
--- a/drivers/gpu/drm/vkms/vkms_formats.c
+++ b/drivers/gpu/drm/vkms/vkms_formats.c
@@ -788,15 +788,36 @@ static const struct conversion_matrix yuv_bt601_full = {
};
/*
- * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.601"],
- * is_legal = True,
- * bits = 8) * 2**32).astype(int)
+ * BT.601 limited-/studio-range YCbCr to full-range RGB.
+ *
+ * The coefficients are derived as follows:
+ *
+ * 1. Take the standard ITU-R YCbCr -> RGB relations for luma weights
+ * Kr, Kb (Kg = 1 - Kr - Kb), with Y in [0, 1] and Cb, Cr in
+ * [-0.5, 0.5]. For BT.601 Kr = 0.299 and Kb = 0.114:
+ *
+ * R = Y + 2 * (1 - Kr) * Cr
+ * G = Y - 2 * (1 - Kb) * Kb / Kg * Cb - 2 * (1 - Kr) * Kr / Kg * Cr
+ * B = Y + 2 * (1 - Kb) * Cb
+ *
+ * These are exactly the yuv_bt601_full coefficients above.
+ *
+ * 2. Expand the studio input range to full range, relative to a
+ * full-range maximum of 2^n - 1 (255 for 8-bit): the luma
+ * coefficient is scaled by 255/(235 - 16) and the chroma
+ * coefficients by 255/(240 - 16). This matches the DRM UAPI
+ * definition and IGT's igt_ycbcr_to_rgb_matrix(). Note this differs
+ * from colour.matrix_YCbCr(is_legal=True), which normalises by 2^n
+ * and is thus off by a factor of 256/255.
+ *
+ * 3. Convert each coefficient to S31.32 fixed point, i.e.
+ * round(coeff * 2^32).
*/
static const struct conversion_matrix yuv_bt601_limited = {
.matrix = {
- { 5020601039, 0, 6881764740 },
- { 5020601039, -1689204679, -3505362278 },
- { 5020601039, 8697922339, 0 },
+ { 5000989317, 0, 6854882848 },
+ { 5000989317, -1682606224, -3491669458 },
+ { 5000989317, 8663946082, 0 },
},
.y_offset = 16,
};
@@ -816,15 +837,14 @@ static const struct conversion_matrix yuv_bt709_full = {
};
/*
- * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.709"],
- * is_legal = True,
- * bits = 8) * 2**32).astype(int)
+ * BT.709 limited-range YCbCr to full-range RGB (Kr = 0.2126, Kb = 0.0722).
+ * Derived as described for yuv_bt601_limited.
*/
static const struct conversion_matrix yuv_bt709_limited = {
.matrix = {
- { 5020601039, 0, 7729959424 },
- { 5020601039, -919487572, -2297803934 },
- { 5020601039, 9108275786, 0 },
+ { 5000989317, 0, 7699764272 },
+ { 5000989317, -915895824, -2288828138 },
+ { 5000989317, 9072696586, 0 },
},
.y_offset = 16,
};
@@ -844,15 +864,14 @@ static const struct conversion_matrix yuv_bt2020_full = {
};
/*
- * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"],
- * is_legal = True,
- * bits = 8) * 2**32).astype(int)
+ * BT.2020 non-constant-luminance limited-range YCbCr to full-range RGB
+ * (Kr = 0.2627, Kb = 0.0593). Derived as described for yuv_bt601_limited.
*/
static const struct conversion_matrix yuv_bt2020_limited = {
.matrix = {
- { 5020601039, 0, 7238124312 },
- { 5020601039, -807714626, -2804506279 },
- { 5020601039, 9234915964, 0 },
+ { 5000989317, 0, 7209850391 },
+ { 5000989317, -804559491, -2793551177 },
+ { 5000989317, 9198842076, 0 },
},
.y_offset = 16,
};
@@ -876,16 +895,25 @@ static void swap_uv_columns(struct conversion_matrix *matrix)
* @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h])
* @encoding: DRM_COLOR_* value for which to obtain a conversion matrix
* @range: DRM_COLOR_*_RANGE value for which to obtain a conversion matrix
+ * @bypass: If true, return an identity (no-op) matrix that passes the samples
+ * through unchanged, ignoring @encoding and @range. Used when a fixed
+ * matrix colorop is present but bypassed.
* @matrix: Pointer to store the value into
*/
void get_conversion_matrix_to_argb_u16(u32 format,
enum drm_color_encoding encoding,
enum drm_color_range range,
+ bool bypass,
struct conversion_matrix *matrix)
{
const struct conversion_matrix *matrix_to_copy;
bool limited_range;
+ if (bypass) {
+ memcpy(matrix, &no_operation, sizeof(no_operation));
+ return;
+ }
+
switch (range) {
case DRM_COLOR_YCBCR_LIMITED_RANGE:
limited_range = true;
diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h
index eeb208cdd6b1..d969cc669a5e 100644
--- a/drivers/gpu/drm/vkms/vkms_formats.h
+++ b/drivers/gpu/drm/vkms/vkms_formats.h
@@ -10,7 +10,7 @@ pixel_read_line_t get_pixel_read_line_function(u32 format);
pixel_write_t get_pixel_write_function(u32 format);
void get_conversion_matrix_to_argb_u16(u32 format, enum drm_color_encoding encoding,
- enum drm_color_range range,
+ enum drm_color_range range, bool bypass,
struct conversion_matrix *matrix);
#if IS_ENABLED(CONFIG_KUNIT)
diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c
index 68cb2a3335e6..6ee5c3f3207c 100644
--- a/drivers/gpu/drm/vkms/vkms_plane.c
+++ b/drivers/gpu/drm/vkms/vkms_plane.c
@@ -6,6 +6,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
+#include <drm/drm_colorop.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@@ -131,6 +132,9 @@ static void vkms_plane_atomic_update(struct drm_plane *plane,
struct drm_framebuffer *fb = new_state->fb;
struct vkms_frame_info *frame_info;
u32 fmt;
+ enum drm_color_encoding encoding = new_state->color_encoding;
+ enum drm_color_range range = new_state->color_range;
+ bool bypass = false;
if (!new_state->crtc || !fb)
return;
@@ -148,7 +152,49 @@ static void vkms_plane_atomic_update(struct drm_plane *plane,
frame_info->rotation = new_state->rotation;
vkms_plane_state->pixel_read_line = get_pixel_read_line_function(fmt);
- get_conversion_matrix_to_argb_u16(fmt, new_state->color_encoding, new_state->color_range,
+
+ if (new_state->color_pipeline) {
+ struct drm_colorop *colorop = new_state->color_pipeline;
+ struct drm_colorop_state *colorop_state;
+
+ colorop_state = drm_atomic_get_new_colorop_state(state, colorop);
+ bypass = !colorop_state || colorop_state->bypass;
+
+ if (!bypass) {
+ switch (colorop_state->fixed_matrix_type) {
+ case DRM_COLOROP_FM_YCBCR601_FULL_RGB:
+ encoding = DRM_COLOR_YCBCR_BT601;
+ range = DRM_COLOR_YCBCR_FULL_RANGE;
+ break;
+ case DRM_COLOROP_FM_YCBCR601_LIMITED_RGB:
+ encoding = DRM_COLOR_YCBCR_BT601;
+ range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+ break;
+ case DRM_COLOROP_FM_YCBCR709_FULL_RGB:
+ encoding = DRM_COLOR_YCBCR_BT709;
+ range = DRM_COLOR_YCBCR_FULL_RANGE;
+ break;
+ case DRM_COLOROP_FM_YCBCR709_LIMITED_RGB:
+ encoding = DRM_COLOR_YCBCR_BT709;
+ range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+ break;
+ case DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB:
+ encoding = DRM_COLOR_YCBCR_BT2020;
+ range = DRM_COLOR_YCBCR_FULL_RANGE;
+ break;
+ case DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB:
+ encoding = DRM_COLOR_YCBCR_BT2020;
+ range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+ break;
+ default:
+ encoding = DRM_COLOR_YCBCR_BT709;
+ range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+ break;
+ }
+ }
+ }
+
+ get_conversion_matrix_to_argb_u16(fmt, encoding, range, bypass,
&vkms_plane_state->conversion_matrix);
}
@@ -175,6 +221,13 @@ static int vkms_plane_atomic_check(struct drm_plane *plane,
if (ret != 0)
return ret;
+ if (new_plane_state->color_pipeline && new_plane_state->fb->format->is_yuv) {
+ struct drm_colorop *colorop = new_plane_state->color_pipeline;
+
+ if (colorop->type != DRM_COLOROP_FIXED_MATRIX)
+ return -EINVAL;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 1b407b61f683..0b470809661c 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -206,32 +206,24 @@ vmw_du_crtc_duplicate_state(struct drm_crtc *crtc)
/**
- * vmw_du_crtc_reset - creates a blank vmw crtc state
+ * vmw_du_crtc_create_state - creates a blank vmw crtc state
* @crtc: DRM crtc
*
- * Resets the atomic state for @crtc by freeing the state pointer (which
- * might be NULL, e.g. at driver load time) and allocating a new empty state
- * object.
+ * Allocates a new empty state object for @crtc.
*/
-void vmw_du_crtc_reset(struct drm_crtc *crtc)
+struct drm_crtc_state *vmw_du_crtc_create_state(struct drm_crtc *crtc)
{
struct vmw_crtc_state *vcs;
-
- if (crtc->state) {
- __drm_atomic_helper_crtc_destroy_state(crtc->state);
-
- kfree(vmw_crtc_state_to_vcs(crtc->state));
- }
-
vcs = kzalloc_obj(*vcs);
-
if (!vcs) {
DRM_ERROR("Cannot allocate vmw_crtc_state\n");
- return;
+ return ERR_PTR(-ENOMEM);
}
- __drm_atomic_helper_crtc_reset(crtc, &vcs->base);
+ __drm_atomic_helper_crtc_state_init(&vcs->base, crtc);
+
+ return &vcs->base;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index 2224d7d91d1b..e641f33341e6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -421,7 +421,7 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *state);
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_commit *state);
-void vmw_du_crtc_reset(struct drm_crtc *crtc);
+struct drm_crtc_state *vmw_du_crtc_create_state(struct drm_crtc *crtc);
struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc);
void vmw_du_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index af3e32174563..a4b28341f622 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -249,7 +249,7 @@ static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc)
static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_ldu_crtc_destroy,
- .reset = vmw_du_crtc_reset,
+ .atomic_create_state = vmw_du_crtc_create_state,
.atomic_duplicate_state = vmw_du_crtc_duplicate_state,
.atomic_destroy_state = vmw_du_crtc_destroy_state,
.set_config = drm_atomic_helper_set_config,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index c83061cf7455..a05f935f1841 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -305,7 +305,7 @@ static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc,
static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_sou_crtc_destroy,
- .reset = vmw_du_crtc_reset,
+ .atomic_create_state = vmw_du_crtc_create_state,
.atomic_duplicate_state = vmw_du_crtc_duplicate_state,
.atomic_destroy_state = vmw_du_crtc_destroy_state,
.set_config = drm_atomic_helper_set_config,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index 4139837f4caf..939da0e11dbb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -778,7 +778,7 @@ out_unref:
static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_stdu_crtc_destroy,
- .reset = vmw_du_crtc_reset,
+ .atomic_create_state = vmw_du_crtc_create_state,
.atomic_duplicate_state = vmw_du_crtc_duplicate_state,
.atomic_destroy_state = vmw_du_crtc_destroy_state,
.set_config = drm_atomic_helper_set_config,
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index d5f922450565..0755066a81e7 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -29,8 +29,8 @@
#include <drm/drm_managed.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <linux/clk.h>
@@ -308,7 +308,7 @@ static const struct drm_crtc_funcs zynqmp_dpsub_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
+ .atomic_create_state = drm_atomic_helper_crtc_create_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = zynqmp_dpsub_crtc_enable_vblank,
@@ -417,6 +417,10 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = {
.minor = 0,
};
+static const struct drm_encoder_funcs zynqmp_dpsub_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
{
struct drm_encoder *encoder = &dpsub->drm->encoder;
@@ -436,7 +440,13 @@ static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
/* Create the encoder and attach the bridge. */
encoder->possible_crtcs |= drm_crtc_mask(&dpsub->drm->crtc);
- drm_simple_encoder_init(&dpsub->drm->dev, encoder, DRM_MODE_ENCODER_NONE);
+ ret = drm_encoder_init(&dpsub->drm->dev, encoder,
+ &zynqmp_dpsub_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret) {
+ drm_err(&dpsub->drm->dev, "failed to initialize encoder\n");
+ return ret;
+ }
ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c
index ed4c1c3acb3c..b75d32ca6ca0 100644
--- a/drivers/gpu/tests/gpu_buddy_test.c
+++ b/drivers/gpu/tests/gpu_buddy_test.c
@@ -38,7 +38,7 @@ static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test)
};
struct list_head allocated[ARRAY_SIZE(alignments)];
unsigned int i, max_subtree_align = 0;
- int ret, tree, order;
+ int ret, order;
struct gpu_buddy mm;
KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
@@ -78,15 +78,11 @@ static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test)
}
for (order = mm.max_order; order >= 0 && !root; order--) {
- for (tree = 0; tree < 2; tree++) {
- node = mm.free_trees[tree][order].rb_node;
- if (node) {
- root = container_of(node,
- struct gpu_buddy_block,
- rb);
- break;
- }
- }
+ node = mm.free_tree[order].rb_node;
+ if (node)
+ root = container_of(node,
+ struct gpu_buddy_block,
+ rb);
}
KUNIT_ASSERT_NOT_NULL(test, root);
@@ -97,15 +93,13 @@ static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test)
gpu_buddy_free_list(&mm, &allocated[i], 0);
for (order = 0; order <= mm.max_order; order++) {
- for (tree = 0; tree < 2; tree++) {
- node = mm.free_trees[tree][order].rb_node;
- if (!node)
- continue;
-
- block = container_of(node, struct gpu_buddy_block, rb);
- max_subtree_align = max(max_subtree_align,
- block->subtree_max_alignment);
- }
+ node = mm.free_tree[order].rb_node;
+ if (!node)
+ continue;
+
+ block = container_of(node, struct gpu_buddy_block, rb);
+ max_subtree_align = max(max_subtree_align,
+ block->subtree_max_alignment);
}
KUNIT_EXPECT_GE(test, max_subtree_align, ilog2(alignments[i]));
@@ -289,6 +283,121 @@ static void gpu_test_buddy_fragmentation_performance(struct kunit *test)
gpu_buddy_fini(&mm);
}
+static void gpu_test_buddy_dirty_tracker_performance(struct kunit *test)
+{
+ struct gpu_buddy_block *block, *tmp;
+ unsigned long elapsed_ms;
+ LIST_HEAD(clear_blocks);
+ LIST_HEAD(dirty_blocks);
+ LIST_HEAD(allocated);
+ struct gpu_buddy mm;
+ LIST_HEAD(results);
+ ktime_t start, end;
+ int i, count;
+
+ /*
+ * Contiguous alloc latency after alternating clear/dirty fragmentation
+ *
+ * Fill a 4 GiB pool with 4 KiB allocations, partition them into
+ * alternating cleared and dirty sets, then free both. In the old
+ * dual-tree design every adjacent buddy pair has one cleared half and
+ * one dirty half, so the pair sits on opposite sides of the clear/dirty
+ * merge barrier and cannot be coalesced at free() time. The pool
+ * stays fully fragmented and the subsequent contiguous 2 GiB allocation
+ * (smaller than the 4 GiB span so it routes through the contiguous
+ * allocator rather than the exact-range fast path) has to invoke
+ * __force_merge() to rebuild a 2 GiB block before it can succeed. With
+ * the dirty-tracker design buddy pairs coalesce unconditionally during
+ * free(), so the pool is already at max_order before the timed alloc
+ * begins and __force_merge() is not needed.
+ */
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, SZ_4G, SZ_4K),
+ "buddy_init failed\n");
+
+ for (i = 0; i < SZ_4G / SZ_4K; i++)
+ KUNIT_ASSERT_FALSE_MSG(test,
+ gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_4K, SZ_4K,
+ &allocated, 0),
+ "buddy_alloc hit an error size=%u\n", SZ_4K);
+
+ count = 0;
+ list_for_each_entry_safe(block, tmp, &allocated, link) {
+ if (count++ % 2 == 0)
+ list_move_tail(&block->link, &clear_blocks);
+ else
+ list_move_tail(&block->link, &dirty_blocks);
+ }
+
+ gpu_buddy_free_list(&mm, &clear_blocks, GPU_BUDDY_CLEARED);
+ gpu_buddy_free_list(&mm, &dirty_blocks, 0);
+
+ start = ktime_get();
+ KUNIT_ASSERT_FALSE_MSG(test,
+ gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_2G, SZ_2G,
+ &results,
+ GPU_BUDDY_CONTIGUOUS_ALLOCATION),
+ "contiguous alloc failed\n");
+ end = ktime_get();
+ elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+
+ kunit_info(test, "Contiguous alloc after fragmentation: %lu ms\n",
+ elapsed_ms);
+
+ gpu_buddy_free_list(&mm, &results, 0);
+ gpu_buddy_fini(&mm);
+
+ /*
+ * Repeated alloc throughput from a maximally fragmented pool
+ *
+ * Fill a 4 GiB pool with 4 KiB allocations, free even-indexed blocks
+ * as cleared and odd-indexed blocks as dirty. The alternating pattern
+ * ensures every adjacent buddy pair has one cleared half and one dirty
+ * half, so each pair lands on opposite sides of the old merge barrier.
+ * Each of the 16 384 x 256 KiB allocations in the timed loop asks for a
+ * single contiguous block (size == min_block_size, CONTIGUOUS flag), so
+ * it cannot be satisfied by 64 stray 4 KiB blocks: under the old design
+ * every alloc has to pay the __force_merge() cost to rebuild a 256 KiB
+ * block. With the dirty-tracker design the pool collapses to one
+ * max_order block during free(), so each alloc is a simple O(log N)
+ * split.
+ */
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, SZ_4G, SZ_4K),
+ "buddy_init failed\n");
+
+ for (i = 0; i < SZ_4G / SZ_4K; i++)
+ KUNIT_ASSERT_FALSE_MSG(test,
+ gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_4K, SZ_4K,
+ &allocated, 0),
+ "buddy_alloc hit an error size=%u\n", SZ_4K);
+
+ count = 0;
+ list_for_each_entry_safe(block, tmp, &allocated, link) {
+ if (count++ % 2 == 0)
+ list_move_tail(&block->link, &clear_blocks);
+ else
+ list_move_tail(&block->link, &dirty_blocks);
+ }
+
+ gpu_buddy_free_list(&mm, &clear_blocks, GPU_BUDDY_CLEARED);
+ gpu_buddy_free_list(&mm, &dirty_blocks, 0);
+
+ start = ktime_get();
+ for (i = 0; i < SZ_4G / SZ_256K; i++)
+ KUNIT_ASSERT_FALSE_MSG(test,
+ gpu_buddy_alloc_blocks(&mm, 0, SZ_4G, SZ_256K, SZ_256K,
+ &results,
+ GPU_BUDDY_CONTIGUOUS_ALLOCATION),
+ "buddy_alloc hit an error size=%u\n", SZ_256K);
+ end = ktime_get();
+ elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+
+ kunit_info(test, "Repeated 256 KiB allocs from fragmented pool: %lu ms\n",
+ elapsed_ms);
+
+ gpu_buddy_free_list(&mm, &results, 0);
+ gpu_buddy_fini(&mm);
+}
+
static void gpu_test_buddy_alloc_range_bias(struct kunit *test)
{
u32 mm_size, size, ps, bias_size, bias_start, bias_end, bias_rem;
@@ -931,10 +1040,12 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test)
/*
* Create a new mm. Intentionally fragment the address space by creating
* two alternating lists. Free both lists, one as dirty the other as clean.
- * Try to allocate double the previous size with matching min_page_size. The
- * allocation should never fail as it calls the force_merge. Also check that
- * the page is always dirty after force_merge. Free the page as dirty, then
- * repeat the whole thing, increment the order until we hit the max_order.
+ * Try to allocate double the previous size with matching min_page_size.
+ * The allocation should never fail because buddy pairs coalesce
+ * unconditionally at free() time, rebuilding the larger block. Also check
+ * that the page is always dirty (a merged block spanning a dirty half is
+ * dirty). Free the page as dirty, then repeat the whole thing, increment
+ * the order until we hit the max_order.
*/
i = 0;
@@ -1027,7 +1138,7 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test)
gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
gpu_buddy_free_list(&mm, &dirty, 0);
gpu_buddy_reset_clear(&mm, false);
- KUNIT_EXPECT_EQ(test, mm.clear_avail, 0);
+ KUNIT_EXPECT_EQ(test, gpu_buddy_clear_avail(&mm), 0);
gpu_buddy_fini(&mm);
/*
@@ -1050,7 +1161,7 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test)
gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
gpu_buddy_free_list(&mm, &dirty, 0);
gpu_buddy_reset_clear(&mm, true);
- KUNIT_EXPECT_EQ(test, mm.clear_avail, mm_size);
+ KUNIT_EXPECT_EQ(test, gpu_buddy_clear_avail(&mm), mm_size);
gpu_buddy_fini(&mm);
}
@@ -1496,6 +1607,7 @@ static struct kunit_case gpu_buddy_tests[] = {
KUNIT_CASE(gpu_test_buddy_alloc_range),
KUNIT_CASE(gpu_test_buddy_alloc_range_bias),
KUNIT_CASE_SLOW(gpu_test_buddy_fragmentation_performance),
+ KUNIT_CASE_SLOW(gpu_test_buddy_dirty_tracker_performance),
KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order),
KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation),
KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress),
diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
index 8b0ca36083dd..e63c7f075f54 100644
--- a/include/drm/display/drm_dp.h
+++ b/include/drm/display/drm_dp.h
@@ -1035,6 +1035,7 @@
# define DP_EDP_14a 0x04 /* eDP 1.4a */
# define DP_EDP_14b 0x05 /* eDP 1.4b */
# define DP_EDP_15 0x06 /* eDP 1.5 */
+# define DP_EDP_20 0x07 /* eDP 2.0 */
#define DP_EDP_GENERAL_CAP_1 0x701
# define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0)
diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h
index 13375bd0f4ae..596c57d9d250 100644
--- a/include/drm/display/drm_hdmi_state_helper.h
+++ b/include/drm/display/drm_hdmi_state_helper.h
@@ -3,6 +3,7 @@
#ifndef DRM_HDMI_STATE_HELPER_H_
#define DRM_HDMI_STATE_HELPER_H_
+struct dentry;
struct drm_atomic_commit;
struct drm_connector;
struct drm_connector_state;
@@ -29,5 +30,7 @@ void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector);
enum drm_mode_status
drm_hdmi_connector_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode);
+void drm_hdmi_connector_debugfs_init(struct drm_connector *connector,
+ struct dentry *root);
#endif // DRM_HDMI_STATE_HELPER_H_
diff --git a/include/drm/display/drm_scdc.h b/include/drm/display/drm_scdc.h
index 3d58f37e8ed8..7f0b05b2f280 100644
--- a/include/drm/display/drm_scdc.h
+++ b/include/drm/display/drm_scdc.h
@@ -29,6 +29,8 @@
#define SCDC_SOURCE_VERSION 0x02
#define SCDC_UPDATE_0 0x10
+#define SCDC_RSED_UPDATE (1 << 6)
+#define SCDC_FLT_UPDATE (1 << 5)
#define SCDC_READ_REQUEST_TEST (1 << 2)
#define SCDC_CED_UPDATE (1 << 1)
#define SCDC_STATUS_UPDATE (1 << 0)
@@ -46,14 +48,25 @@
#define SCDC_CONFIG_0 0x30
#define SCDC_READ_REQUEST_ENABLE (1 << 0)
+#define SCDC_CONFIG_1 0x31
+#define SCDC_FRL_RATE 0x0f
+#define SCDC_FFE_LEVELS 0xf0
+
#define SCDC_STATUS_FLAGS_0 0x40
+#define SCDC_DSC_FAIL (1 << 7)
+#define SCDC_FLT_READY (1 << 6)
+#define SCDC_LN3_LOCK (1 << 4)
#define SCDC_CH2_LOCK (1 << 3)
#define SCDC_CH1_LOCK (1 << 2)
#define SCDC_CH0_LOCK (1 << 1)
-#define SCDC_CH_LOCK_MASK (SCDC_CH2_LOCK | SCDC_CH1_LOCK | SCDC_CH0_LOCK)
+#define SCDC_CH_LOCK_MASK (SCDC_LN3_LOCK | SCDC_CH2_LOCK | SCDC_CH1_LOCK | \
+ SCDC_CH0_LOCK)
#define SCDC_CLOCK_DETECT (1 << 0)
#define SCDC_STATUS_FLAGS_1 0x41
+#define SCDC_LN_EVEN_TRAIN_PTRN 0x0f
+#define SCDC_LN_ODD_TRAIN_PTRN 0xf0
+#define SCDC_STATUS_FLAGS_2 0x42
#define SCDC_ERR_DET_0_L 0x50
#define SCDC_ERR_DET_0_H 0x51
@@ -65,6 +78,12 @@
#define SCDC_ERR_DET_CHECKSUM 0x56
+#define SCDC_ERR_DET_3_L 0x57
+#define SCDC_ERR_DET_3_H 0x58
+
+#define SCDC_ERR_DET_RS_L 0x59
+#define SCDC_ERR_DET_RS_H 0x5a
+
#define SCDC_TEST_CONFIG_0 0xc0
#define SCDC_TEST_READ_REQUEST (1 << 7)
#define SCDC_TEST_READ_REQUEST_DELAY(x) ((x) & 0x7f)
diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h
index 34600476a1b9..a3b20adaac7e 100644
--- a/include/drm/display/drm_scdc_helper.h
+++ b/include/drm/display/drm_scdc_helper.h
@@ -24,17 +24,108 @@
#ifndef DRM_SCDC_HELPER_H
#define DRM_SCDC_HELPER_H
+#include <linux/errno.h>
#include <linux/types.h>
#include <drm/display/drm_scdc.h>
struct drm_connector;
struct i2c_adapter;
+struct dentry;
-ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
- size_t size);
-ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
- const void *buffer, size_t size);
+struct drm_scdc_status_flags {
+ /* Status Register 0 */
+ bool clock_detected;
+ bool ch0_locked;
+ bool ch1_locked;
+ bool ch2_locked;
+ bool ln3_locked;
+ bool flt_ready;
+ bool dsc_fail;
+
+ /* Status Register 1 */
+ u8 ln0_training_pattern : 4;
+ u8 ln1_training_pattern : 4;
+
+ /* Status Register 2 */
+ u8 ln2_training_pattern : 4;
+ u8 ln3_training_pattern : 4;
+};
+
+enum drm_scdc_frl_rate {
+ SCDC_FRL_RATE_OFF = 0,
+ SCDC_FRL_RATE_3X3 = 1,
+ SCDC_FRL_RATE_6X3 = 2,
+ SCDC_FRL_RATE_6X4 = 3,
+ SCDC_FRL_RATE_8X4 = 4,
+ SCDC_FRL_RATE_10X4 = 5,
+ SCDC_FRL_RATE_12X4 = 6,
+ SCDC_FRL_RATE_RESV_7 = 7,
+ SCDC_FRL_RATE_RESV_8 = 8,
+ SCDC_FRL_RATE_RESV_9 = 9,
+ SCDC_FRL_RATE_RESV_10 = 10,
+ SCDC_FRL_RATE_RESV_11 = 11,
+ SCDC_FRL_RATE_RESV_12 = 12,
+ SCDC_FRL_RATE_RESV_13 = 13,
+ SCDC_FRL_RATE_RESV_14 = 14,
+ SCDC_FRL_RATE_RESV_15 = 15
+};
+
+/**
+ * drm_scdc_num_frl_lanes - get number of lanes for a given FRL rate
+ * @rate: one of &enum drm_scdc_frl_rate
+ *
+ * For a given @rate, return the number of lanes it uses.
+ *
+ * Returns: %-EINVAL if @rate is not a valid FRL rate, or the number of lanes
+ * for a given &enum drm_scdc_frl_rate on success (including %0 for "off")
+ */
+static inline __pure int drm_scdc_num_frl_lanes(enum drm_scdc_frl_rate rate)
+{
+ switch (rate) {
+ case SCDC_FRL_RATE_OFF:
+ return 0;
+ case SCDC_FRL_RATE_3X3:
+ case SCDC_FRL_RATE_6X3:
+ return 3;
+ case SCDC_FRL_RATE_6X4:
+ case SCDC_FRL_RATE_8X4:
+ case SCDC_FRL_RATE_10X4:
+ case SCDC_FRL_RATE_12X4:
+ return 4;
+ default:
+ return -EINVAL;
+ }
+}
+
+struct drm_scdc_state {
+ /** @stf: contents of the status flag registers */
+ struct drm_scdc_status_flags stf;
+ /** @scramling_enabled: true if TMDS scrambling is on */
+ bool scrambling_enabled;
+ /** @scrambling_detected: true if the sink actually detected scrambling */
+ bool scrambling_detected;
+ /**
+ * @tmds_bclk_x40: true if TMDS bit period is 1/40th of the TMDS
+ * clock period, false if it's 1/10th of the clock period.
+ */
+ bool tmds_bclk_x40;
+ /** @rate: FRL rate set by the source */
+ enum drm_scdc_frl_rate rate : 4;
+ /** @ffe_levels: The FFE levels for @rate set by the source */
+ u8 ffe_levels : 4;
+ /** @error_count: character error counts for each channel/link */
+ u16 error_count[4];
+ /** @rs_corrections: number of Reed-Solomon Corrections */
+ u16 rs_corrections;
+ /** @scdc: raw SCDC data buffer */
+ u8 scdc[256];
+};
+
+int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
+ size_t size);
+int drm_scdc_write(struct i2c_adapter *adapter, u8 offset, const void *buffer,
+ size_t size);
/**
* drm_scdc_readb - read a single byte from SCDC
@@ -77,4 +168,8 @@ bool drm_scdc_get_scrambling_status(struct drm_connector *connector);
bool drm_scdc_set_scrambling(struct drm_connector *connector, bool enable);
bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, bool set);
+int drm_scdc_read_state(struct drm_connector *connector,
+ struct drm_scdc_state *state);
+void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root);
+
#endif
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 224fae40ed2b..26ef67c835e2 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -135,6 +135,114 @@ enum drm_colorop_curve_1d_type {
};
/**
+ * enum drm_colorop_fixed_matrix_type - type of Fixed Matrix
+ *
+ * Describes a Fixed Matrix operation to be applied by the DRM_COLOROP_FIXED_MATRIX
+ */
+enum drm_colorop_fixed_matrix_type {
+ /**
+ * @DRM_COLOROP_FM_YCBCR601_FULL_RGB:
+ *
+ * enum string "YCbCr 601 Full to RGB"
+ *
+ * Converts full-range YCbCr into full-range RGB using the BT.601
+ * coefficients. Y is normalized to [0, 1] and Cb, Cr are centered
+ * at 0 (the nominal 0.5 offset removed) before the matrix::
+ *
+ * | R | | 1.0 0.000000 1.402000 | | Y |
+ * | G | = | 1.0 -0.344136 -0.714136 | x | Cb |
+ * | B | | 1.0 1.772000 0.000000 | | Cr |
+ */
+ DRM_COLOROP_FM_YCBCR601_FULL_RGB,
+
+ /**
+ * @DRM_COLOROP_FM_YCBCR601_LIMITED_RGB:
+ *
+ * enum string "YCbCr 601 Limited to RGB"
+ *
+ * Converts limited- (narrow-) range YCbCr into full-range RGB using
+ * the BT.601 coefficients. Before the matrix Y is offset by 16/255
+ * and Cb, Cr are centered at 0, following the studio-range
+ * convention of ITU-R BT.601::
+ *
+ * | R | | 1.164384 0.000000 1.596027 | | Y - 16/255 |
+ * | G | = | 1.164384 -0.391762 -0.812968 | x | Cb |
+ * | B | | 1.164384 2.017232 0.000000 | | Cr |
+ */
+ DRM_COLOROP_FM_YCBCR601_LIMITED_RGB,
+
+ /**
+ * @DRM_COLOROP_FM_YCBCR709_FULL_RGB:
+ *
+ * enum string "YCbCr 709 Full to RGB"
+ *
+ * Converts full-range YCbCr into full-range RGB using the BT.709
+ * coefficients. Y is normalized to [0, 1] and Cb, Cr are centered
+ * at 0 (the nominal 0.5 offset removed) before the matrix::
+ *
+ * | R | | 1.0 0.000000 1.574800 | | Y |
+ * | G | = | 1.0 -0.187324 -0.468124 | x | Cb |
+ * | B | | 1.0 1.855600 0.000000 | | Cr |
+ */
+ DRM_COLOROP_FM_YCBCR709_FULL_RGB,
+
+ /**
+ * @DRM_COLOROP_FM_YCBCR709_LIMITED_RGB:
+ *
+ * enum string "YCbCr 709 Limited to RGB"
+ *
+ * Converts limited- (narrow-) range YCbCr into full-range RGB using
+ * the BT.709 coefficients. Before the matrix Y is offset by 16/255
+ * and Cb, Cr are centered at 0, following the studio-range
+ * convention of ITU-R BT.709::
+ *
+ * | R | | 1.164384 0.000000 1.792741 | | Y - 16/255 |
+ * | G | = | 1.164384 -0.213249 -0.532909 | x | Cb |
+ * | B | | 1.164384 2.112402 0.000000 | | Cr |
+ */
+ DRM_COLOROP_FM_YCBCR709_LIMITED_RGB,
+
+ /**
+ * @DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB:
+ *
+ * enum string "YCbCr 2020 NC Full to RGB"
+ *
+ * Converts full-range YCbCr into full-range RGB using the BT.2020
+ * non-constant luminance coefficients. Y is normalized to [0, 1]
+ * and Cb, Cr are centered at 0 (the nominal 0.5 offset removed)
+ * before the matrix::
+ *
+ * | R | | 1.0 0.000000 1.474600 | | Y |
+ * | G | = | 1.0 -0.164553 -0.571353 | x | Cb |
+ * | B | | 1.0 1.881400 0.000000 | | Cr |
+ */
+ DRM_COLOROP_FM_YCBCR2020_NC_FULL_RGB,
+
+ /**
+ * @DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB:
+ *
+ * enum string "YCbCr 2020 NC Limited to RGB"
+ *
+ * Converts limited- (narrow-) range YCbCr into full-range RGB using
+ * the BT.2020 non-constant luminance coefficients. Before the matrix
+ * Y is offset by 16/255 and Cb, Cr are centered at 0, following the
+ * studio-range convention of ITU-R BT.2020::
+ *
+ * | R | | 1.164384 0.000000 1.678674 | | Y - 16/255 |
+ * | G | = | 1.164384 -0.187326 -0.650424 | x | Cb |
+ * | B | | 1.164384 2.141772 0.000000 | | Cr |
+ */
+ DRM_COLOROP_FM_YCBCR2020_NC_LIMITED_RGB,
+
+ /**
+ * @DRM_COLOROP_FM_COUNT:
+ *
+ * enum value denoting the size of the enum
+ */
+ DRM_COLOROP_FM_COUNT
+};
+
+/**
* struct drm_colorop_state - mutable colorop state
*/
struct drm_colorop_state {
@@ -197,6 +305,13 @@ struct drm_colorop_state {
*/
enum drm_colorop_lut3d_interpolation_type lut3d_interpolation;
+ /**
+ * @fixed_matrix_type:
+ *
+ * Type of Fixed Matrix operation.
+ */
+ enum drm_colorop_fixed_matrix_type fixed_matrix_type;
+
/** @state: backpointer to global drm_atomic_commit */
struct drm_atomic_commit *state;
};
@@ -367,6 +482,13 @@ struct drm_colorop {
struct drm_property *data_property;
/**
+ * @fixed_matrix_type_property:
+ *
+ * Sub-type for DRM_COLOROP_FIXED_MATRIX type.
+ */
+ struct drm_property *fixed_matrix_type_property;
+
+ /**
* @next_property:
*
* Read-only property to next colorop in the pipeline
@@ -422,6 +544,10 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col
uint32_t lut_size,
enum drm_colorop_lut3d_interpolation_type interpolation,
uint32_t flags);
+int drm_plane_colorop_fixed_matrix_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane,
+ const struct drm_colorop_funcs *funcs,
+ u64 supported_fm, uint32_t flags);
struct drm_colorop_state *
drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop);
@@ -480,6 +606,7 @@ drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_interpolation_ty
const char *
drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type);
+const char *drm_get_colorop_fixed_matrix_type_name(enum drm_colorop_fixed_matrix_type type);
void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index a0cf0268de48..c61345e6fb08 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -255,6 +255,44 @@ struct drm_scdc {
};
/**
+ * struct drm_hdmi_vrr_cap - Information about VRR capabilities of a HDMI sink
+ *
+ * Describes the VRR support provided by HDMI 2.1 sink. The information is
+ * fetched fom additional HFVSDB blocks defined for HDMI 2.1.
+ */
+struct drm_hdmi_vrr_cap {
+ /** @fva: flag for Fast VActive (Quick Frame Transport) support */
+ bool fva;
+
+ /** @mcnmvrr: flag for Negative M VRR support */
+ bool cnmvrr;
+
+ /** @mcinema_vrr: flag for Cinema VRR support */
+ bool cinema_vrr;
+
+ /** @mdelta: flag for limited frame-to-frame compensation support */
+ bool mdelta;
+
+ /**
+ * @vrr_min : minimum supported variable refresh rate in Hz.
+ * Valid values only inide 1 - 48 range
+ */
+ u16 vrr_min;
+
+ /**
+ * @vrr_max : maximum supported variable refresh rate in Hz (optional).
+ * Valid values are either 0 (max based on video mode) or >= 100
+ */
+ u16 vrr_max;
+
+ /**
+ * @supported: flag for vrr support based on checking for VRRmin and
+ * VRRmax values having correct values.
+ */
+ bool supported;
+};
+
+/**
* struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink
*
* Describes the DSC support provided by HDMI 2.1 sink.
@@ -330,6 +368,15 @@ struct drm_hdmi_info {
/** @max_lanes: supported by sink */
u8 max_lanes;
+ /** @fapa_start_location: flag for the FAPA in blanking support */
+ bool fapa_start_location;
+
+ /** @allm: flag for Auto Low Latency Mode support by sink */
+ bool allm;
+
+ /** @vrr_cap: VRR capabilities of the sink */
+ struct drm_hdmi_vrr_cap vrr_cap;
+
/** @dsc_cap: DSC capabilities of the sink */
struct drm_hdmi_dsc_cap dsc_cap;
};
@@ -765,7 +812,7 @@ enum drm_bus_flags {
* struct drm_amd_vsdb_info - AMD-specific VSDB information
*
* This structure holds information parsed from the AMD Vendor-Specific Data
- * Block (VSDB) version 3.
+ * Block (VSDB) versions 1 to 3.
*/
struct drm_amd_vsdb_info {
/**
@@ -774,6 +821,26 @@ struct drm_amd_vsdb_info {
u8 version;
/**
+ * @freesync_supported: FreeSync (variable refresh rate) supported by panel
+ */
+ bool freesync_supported;
+
+ /**
+ * @min_frame_rate: FreeSync minimum refresh rate in Hz
+ */
+ u16 min_frame_rate;
+
+ /**
+ * @max_frame_rate: FreeSync maximum refresh rate in Hz
+ */
+ u16 max_frame_rate;
+
+ /**
+ * @freesync_vcp_code: MCCS VCP code for FreeSync
+ */
+ u8 freesync_vcp_code;
+
+ /**
* @replay_mode: Panel Replay supported
*/
bool replay_mode;
diff --git a/include/drm/drm_gem_atomic_helper.h b/include/drm/drm_gem_atomic_helper.h
index 3e01c619a25e..2deb0cd9ebbe 100644
--- a/include/drm/drm_gem_atomic_helper.h
+++ b/include/drm/drm_gem_atomic_helper.h
@@ -94,7 +94,9 @@ void __drm_gem_destroy_shadow_plane_state(struct drm_shadow_plane_state *shadow_
void __drm_gem_reset_shadow_plane(struct drm_plane *plane,
struct drm_shadow_plane_state *shadow_plane_state);
-void drm_gem_reset_shadow_plane(struct drm_plane *plane);
+void __drm_gem_shadow_plane_state_init(struct drm_plane *plane,
+ struct drm_shadow_plane_state *shadow_plane_state);
+struct drm_plane_state *drm_gem_create_shadow_plane_state(struct drm_plane *plane);
struct drm_plane_state *drm_gem_duplicate_shadow_plane_state(struct drm_plane *plane);
void drm_gem_destroy_shadow_plane_state(struct drm_plane *plane,
struct drm_plane_state *plane_state);
@@ -107,7 +109,7 @@ void drm_gem_destroy_shadow_plane_state(struct drm_plane *plane,
* macro initializes struct drm_plane_funcs to use the rsp helper functions.
*/
#define DRM_GEM_SHADOW_PLANE_FUNCS \
- .reset = drm_gem_reset_shadow_plane, \
+ .atomic_create_state = drm_gem_create_shadow_plane_state, \
.atomic_duplicate_state = drm_gem_duplicate_shadow_plane_state, \
.atomic_destroy_state = drm_gem_destroy_shadow_plane_state
@@ -130,7 +132,8 @@ int drm_gem_simple_kms_begin_shadow_fb_access(struct drm_simple_display_pipe *pi
struct drm_plane_state *plane_state);
void drm_gem_simple_kms_end_shadow_fb_access(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state);
-void drm_gem_simple_kms_reset_shadow_plane(struct drm_simple_display_pipe *pipe);
+struct drm_plane_state *
+drm_gem_simple_kms_create_shadow_plane_state(struct drm_simple_display_pipe *pipe);
struct drm_plane_state *
drm_gem_simple_kms_duplicate_shadow_plane_state(struct drm_simple_display_pipe *pipe);
void drm_gem_simple_kms_destroy_shadow_plane_state(struct drm_simple_display_pipe *pipe,
@@ -147,7 +150,7 @@ void drm_gem_simple_kms_destroy_shadow_plane_state(struct drm_simple_display_pip
#define DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS \
.begin_fb_access = drm_gem_simple_kms_begin_shadow_fb_access, \
.end_fb_access = drm_gem_simple_kms_end_shadow_fb_access, \
- .reset_plane = drm_gem_simple_kms_reset_shadow_plane, \
+ .create_plane_state = drm_gem_simple_kms_create_shadow_plane_state, \
.duplicate_plane_state = drm_gem_simple_kms_duplicate_shadow_plane_state, \
.destroy_plane_state = drm_gem_simple_kms_destroy_shadow_plane_state
diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h
index 78ab23d844ce..d93e25fdc2e1 100644
--- a/include/drm/drm_mipi_dbi.h
+++ b/include/drm/drm_mipi_dbi.h
@@ -234,7 +234,7 @@ void drm_mipi_dbi_plane_helper_atomic_update(struct drm_plane *plane,
*/
#define DRM_MIPI_DBI_CRTC_FUNCS \
- .reset = drm_atomic_helper_crtc_reset, \
+ .atomic_create_state = drm_atomic_helper_crtc_create_state, \
.set_config = drm_atomic_helper_set_config, \
.page_flip = drm_atomic_helper_page_flip, \
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, \
diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
index 2adc5ac688e1..0805903c093b 100644
--- a/include/drm/drm_print.h
+++ b/include/drm/drm_print.h
@@ -644,6 +644,9 @@ static inline struct device *__drm_to_dev(const struct drm_device *drm)
#define drm_err_ratelimited(drm, fmt, ...) \
__drm_printk((drm), err, _ratelimited, "*ERROR* " fmt, ##__VA_ARGS__)
+#define drm_warn_ratelimited(drm, fmt, ...) \
+ __drm_printk((drm), warn, _ratelimited, fmt, ##__VA_ARGS__)
+
#define drm_dbg_core(drm, fmt, ...) \
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__)
diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
index cb672ce0e856..4a4a840d84f1 100644
--- a/include/drm/drm_simple_kms_helper.h
+++ b/include/drm/drm_simple_kms_helper.h
@@ -39,11 +39,11 @@ struct drm_simple_display_pipe_funcs {
struct drm_plane_state *plane_state);
int (*enable_vblank)(struct drm_simple_display_pipe *pipe);
void (*disable_vblank)(struct drm_simple_display_pipe *pipe);
- void (*reset_crtc)(struct drm_simple_display_pipe *pipe);
struct drm_crtc_state * (*duplicate_crtc_state)(struct drm_simple_display_pipe *pipe);
void (*destroy_crtc_state)(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state);
void (*reset_plane)(struct drm_simple_display_pipe *pipe);
+ struct drm_plane_state * (*create_plane_state)(struct drm_simple_display_pipe *pipe);
struct drm_plane_state * (*duplicate_plane_state)(struct drm_simple_display_pipe *pipe);
void (*destroy_plane_state)(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state);
@@ -68,10 +68,6 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
const uint64_t *format_modifiers,
struct drm_connector *connector);
-int drm_simple_encoder_init(struct drm_device *dev,
- struct drm_encoder *encoder,
- int encoder_type);
-
void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
size_t offset, int encoder_type);
diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h
index 2c36124bb696..ddc4eca84175 100644
--- a/include/linux/gpu_buddy.h
+++ b/include/linux/gpu_buddy.h
@@ -43,8 +43,8 @@
/**
* GPU_BUDDY_CLEAR_ALLOCATION - Prefer pre-cleared (zeroed) memory
*
- * Attempt to allocate from the clear tree first. If insufficient clear
- * memory is available, falls back to dirty memory. Useful when the
+ * Attempt to allocate outside dirty-tracked ranges first. If insufficient
+ * clear memory is available, falls back to dirty memory. Useful when the
* caller needs zeroed memory and wants to avoid GPU clear operations.
*/
#define GPU_BUDDY_CLEAR_ALLOCATION BIT(3)
@@ -53,8 +53,8 @@
* GPU_BUDDY_CLEARED - Mark returned blocks as cleared
*
* Used with gpu_buddy_free_list() to indicate that the memory being
- * freed has been cleared (zeroed). The blocks will be placed in the
- * clear tree for future GPU_BUDDY_CLEAR_ALLOCATION requests.
+ * freed has been cleared (zeroed). The blocks will be removed from the
+ * dirty tracker for future GPU_BUDDY_CLEAR_ALLOCATION requests.
*/
#define GPU_BUDDY_CLEARED BIT(4)
@@ -67,15 +67,17 @@
*/
#define GPU_BUDDY_TRIM_DISABLE BIT(5)
-enum gpu_buddy_free_tree {
- GPU_BUDDY_CLEAR_TREE = 0,
- GPU_BUDDY_DIRTY_TREE,
- GPU_BUDDY_MAX_FREE_TREES,
+/*
+ * Clear/dirty state of a free block. Ordered so a numerically larger value
+ * is "more clear" (DIRTY < MIXED < CLEAR) which lets subtree_block_state be
+ * maintained as a simple max-augment over the per-order free tree.
+ */
+enum gpu_block_state {
+ GPU_BLOCK_DIRTY = 0,
+ GPU_BLOCK_MIXED = 1,
+ GPU_BLOCK_CLEAR = 2,
};
-#define for_each_free_tree(tree) \
- for ((tree) = 0; (tree) < GPU_BUDDY_MAX_FREE_TREES; (tree)++)
-
/**
* struct gpu_buddy_block - Block within a buddy allocator
*
@@ -103,6 +105,13 @@ struct gpu_buddy_block {
#define GPU_BUDDY_ALLOCATED (1 << 10)
#define GPU_BUDDY_FREE (2 << 10)
#define GPU_BUDDY_SPLIT (3 << 10)
+/*
+ * GPU_BUDDY_HEADER_CLEAR has two roles:
+ * - FREE state: set when the block's full range is cleared (dirty
+ * tracker confirmed no overlap).
+ * - ALLOCATED state: set when the block was served from cleared memory,
+ * informing the caller that no GPU clear pass is needed.
+ */
#define GPU_BUDDY_HEADER_CLEAR GENMASK_ULL(9, 9)
/* Free to be used, if needed in the future */
#define GPU_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6)
@@ -128,14 +137,45 @@ struct gpu_buddy_block {
struct list_head link;
};
/* private: */
- struct list_head tmp_link;
+ enum gpu_block_state subtree_block_state;
unsigned int subtree_max_alignment;
+ struct list_head tmp_link;
+ bool has_clear;
};
/* Order-zero must be at least SZ_4K */
#define GPU_BUDDY_MAX_ORDER (63 - 12)
/**
+ * struct gpu_dirty_extent - a contiguous dirty address range
+ *
+ * Tracks a single contiguous address range whose memory content is known
+ * to be dirty. Extents are non-overlapping and stored in an augmented
+ * red-black tree sorted by @start. The augmented value @subtree_max_size
+ * allows O(log N) search for an extent of at least a given size.
+ */
+struct gpu_dirty_extent {
+/* private: */
+ struct rb_node rb;
+ u64 start;
+ u64 end;
+ u64 subtree_max_size;
+};
+
+/**
+ * struct gpu_dirty_tracker - tracks dirty address intervals
+ *
+ * Maintains a set of non-overlapping dirty extents as an augmented
+ * red-black tree.
+ */
+struct gpu_dirty_tracker {
+/* private: */
+ struct rb_root root;
+ /* Total bytes of dirty memory currently tracked. */
+ u64 total_dirty;
+};
+
+/**
* struct gpu_buddy - GPU binary buddy allocator
*
* The buddy allocator provides efficient power-of-two memory allocation
@@ -152,20 +192,21 @@ struct gpu_buddy_block {
* @chunk_size: Minimum allocation granularity in bytes. Must be at least SZ_4K.
* @size: Total size of the address space managed by this allocator in bytes.
* @avail: Total free space currently available for allocation in bytes.
- * @clear_avail: Free space available in the clear tree (zeroed memory) in bytes.
- * This is a subset of @avail.
* @lock_dep_map: Annotates gpu_buddy API with a driver provided lock.
*/
struct gpu_buddy {
/* private: */
+ /* Tracker of dirty address ranges (decoupled from free_tree). */
+ struct gpu_dirty_tracker dirty;
/*
- * Array of red-black trees for free block management.
- * Indexed as free_trees[clear/dirty][order] where:
- * - Index 0 (GPU_BUDDY_CLEAR_TREE): blocks with zeroed content
- * - Index 1 (GPU_BUDDY_DIRTY_TREE): blocks with unknown content
- * Each tree holds free blocks of the corresponding order.
+ * One RB-tree per order containing all free blocks (clear and
+ * dirty alike). The augment field subtree_block_state (a max over
+ * the subtree of each block's state) lets clear allocations
+ * find the right-most fully-clear or mixed block in O(log N).
+ * Dirty free blocks coexist here but are also indexed by the
+ * @dirty tracker for fast dirty allocation lookups.
*/
- struct rb_root **free_trees;
+ struct rb_root *free_tree;
/*
* Array of root blocks representing the top-level blocks of the
* binary tree(s). Multiple roots exist when the total size is not
@@ -194,7 +235,6 @@ struct gpu_buddy {
u64 chunk_size;
u64 size;
u64 avail;
- u64 clear_avail;
#ifdef CONFIG_LOCKDEP
struct lockdep_map *lock_dep_map;
#endif
@@ -226,17 +266,32 @@ struct gpu_buddy {
*
* Ensure driver lock is held.
*/
-static inline void gpu_buddy_driver_lock_held(struct gpu_buddy *mm)
+static inline void gpu_buddy_driver_lock_held(const struct gpu_buddy *mm)
{
if (mm->lock_dep_map)
lockdep_assert(lock_is_held_type(mm->lock_dep_map, 0));
}
#else
-static inline void gpu_buddy_driver_lock_held(struct gpu_buddy *mm)
+static inline void gpu_buddy_driver_lock_held(const struct gpu_buddy *mm)
{
}
#endif
+/**
+ * gpu_buddy_clear_avail - free space that is clear (zeroed), in bytes
+ * @mm: gpu buddy allocator
+ *
+ * A subset of @mm->avail. Derived on demand as @mm->avail minus the bytes
+ * the dirty tracker records as dirty, so it is always consistent with the
+ * tracker without a cached field to keep in sync. Zero for a fresh pool,
+ * which is fully dirty.
+ */
+static inline u64 gpu_buddy_clear_avail(const struct gpu_buddy *mm)
+{
+ gpu_buddy_driver_lock_held(mm);
+ return mm->avail - mm->dirty.total_dirty;
+}
+
static inline u64
gpu_buddy_block_offset(const struct gpu_buddy_block *block)
{
diff --git a/include/sound/omap-hdmi-audio.h b/include/sound/omap-hdmi-audio.h
index e5f82044a404..8dca92360ee0 100644
--- a/include/sound/omap-hdmi-audio.h
+++ b/include/sound/omap-hdmi-audio.h
@@ -34,6 +34,7 @@ struct omap_hdmi_audio_pdata {
phys_addr_t audio_dma_addr;
const struct omap_hdmi_audio_ops *ops;
+ void (*audio_hpd)(struct device *dev, bool connected);
};
#endif /* __OMAP_HDMI_AUDIO_H__ */
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index bd435effdcee..ed1a660a3dfd 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -955,6 +955,18 @@ enum drm_colorop_type {
* color = lut3d[index]
*/
DRM_COLOROP_3D_LUT,
+
+ /**
+ * @DRM_COLOROP_FIXED_MATRIX:
+ *
+ * enum string "Fixed Matrix"
+ *
+ * A Colorop block that performs a pre-defined matrix operation selected
+ * via the FIXED_MATRIX_TYPE enum property. The driver advertises the supported
+ * operations through this property.
+ */
+ DRM_COLOROP_FIXED_MATRIX,
+
};
/**
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 4683f3d68022..47a41646de4b 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -28,7 +28,7 @@ DEFINE_STATIC_SRCU(dmemcg_srcu);
struct dmem_cgroup_region {
/**
* @ref: References keeping the region alive.
- * Keeps the region reference alive after a succesful RCU lookup.
+ * Keeps the region reference alive after a successful RCU lookup.
*/
struct kref ref;
@@ -696,7 +696,7 @@ EXPORT_SYMBOL_GPL(dmem_cgroup_uncharge);
* dmem_cgroup_try_charge() - Try charging a new allocation to a region.
* @region: dmem region to charge
* @size: Size (in bytes) to charge.
- * @ret_pool: On succesfull allocation, the pool that is charged.
+ * @ret_pool: On successful allocation, the pool that is charged.
* @ret_limit_pool: On a failed allocation, the limiting pool.
*
* This function charges the @region region for a size of @size bytes.
diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c
index c3034a8979dd..c32ec7c0b0cf 100644
--- a/sound/soc/ti/omap-hdmi.c
+++ b/sound/soc/ti/omap-hdmi.c
@@ -17,6 +17,7 @@
#include <sound/dmaengine_pcm.h>
#include <uapi/sound/asound.h>
#include <sound/asoundef.h>
+#include <sound/jack.h>
#include <sound/omap-hdmi-audio.h>
#include "sdma-pcm.h"
@@ -33,6 +34,8 @@ struct hdmi_audio_data {
struct mutex current_stream_lock;
struct snd_pcm_substream *current_stream;
+ struct snd_soc_jack jack;
+ atomic_t jack_state;
};
static
@@ -260,6 +263,34 @@ static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
ad->current_stream = NULL;
}
+static void hdmi_audio_hpd(struct device *dev, bool connected)
+{
+ struct hdmi_audio_data *ad = dev_get_drvdata(dev);
+
+ if (atomic_xchg(&ad->jack_state, connected) == connected)
+ return;
+
+ snd_soc_jack_report(&ad->jack,
+ connected ? SND_JACK_AVOUT : 0, SND_JACK_AVOUT);
+ dev_dbg(dev, "HDMI %s\n", connected ? "CONNECTED" : "DISCONNECTED");
+}
+
+static int hdmi_audio_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct hdmi_audio_data *ad = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = snd_soc_card_jack_new(
+ card, "HDMI", SND_JACK_AVOUT, &ad->jack);
+ if (ret < 0) {
+ dev_err(card->dev, "Cannot create HDMI jack: %i\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdmi_dai_startup,
.hw_params = hdmi_dai_hw_params,
@@ -369,25 +400,42 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
card->dai_link->stream_name = card->name;
card->dai_link->cpus->dai_name = dev_name(ad->dssdev);
card->dai_link->platforms->name = dev_name(ad->dssdev);
+ card->dai_link->init = hdmi_audio_dai_init;
card->num_links = 1;
card->dev = dev;
+ atomic_set(&ad->jack_state, -1);
+ ad->card = card;
+
+ snd_soc_card_set_drvdata(card, ad);
ret = devm_snd_soc_register_card(dev, card);
if (ret)
return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n");
- snd_soc_card_set_drvdata(card, ad);
-
dev_set_drvdata(dev, ad);
+ ha->audio_hpd = hdmi_audio_hpd;
+
return 0;
}
+static void omap_hdmi_audio_remove(struct platform_device *pdev)
+{
+ struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data;
+
+ /*
+ * hdmi4_unbind() holds audio lock across platform_device_unregister(),
+ * so audio_hpd() cannot run concurrently with this callback.
+ */
+ ha->audio_hpd = NULL;
+}
+
static struct platform_driver hdmi_audio_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = omap_hdmi_audio_probe,
+ .remove = omap_hdmi_audio_remove,
};
module_platform_driver(hdmi_audio_driver);