S: Maintained
F: drivers/acpi/arm64
- ACPI I2C MULTI INSTANTIATE DRIVER
+ ACPI SERIAL MULTI INSTANTIATE DRIVER
S: Maintained
- F: drivers/platform/x86/i2c-multi-instantiate.c
+ F: drivers/platform/x86/serial-multi-instantiate.c
ACPI PCC(Platform Communication Channel) MAILBOX DRIVER
S: Supported
W: http://wiki.analog.com/AD5254
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/misc/ad525x_dpot.c
AD5398 CURRENT REGULATOR DRIVER (AD5398/AD5821)
S: Supported
W: http://wiki.analog.com/AD5398
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/regulator/ad5398.c
AD714X CAPACITANCE TOUCH SENSOR DRIVER (AD7142/3/7/8/7A)
S: Supported
W: http://wiki.analog.com/AD7142
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/input/misc/ad714x.c
AD7877 TOUCHSCREEN DRIVER
S: Supported
W: http://wiki.analog.com/AD7877
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/input/touchscreen/ad7877.c
AD7879 TOUCHSCREEN DRIVER (AD7879/AD7889)
S: Supported
W: http://wiki.analog.com/AD7879
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/input/touchscreen/ad7879.c
ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR)
S: Supported
W: https://wiki.analog.com/ADF7242
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
F: drivers/net/ieee802154/adf7242.c
S: Supported
W: http://wiki.analog.com/ADP5520
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/gpio/gpio-adp5520.c
F: drivers/input/keyboard/adp5520-keys.c
F: drivers/leds/leds-adp5520.c
S: Supported
W: http://wiki.analog.com/ADP5588
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/gpio/gpio-adp5588.c
F: drivers/input/keyboard/adp5588-keys.c
S: Supported
W: http://wiki.analog.com/ADP8860
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/video/backlight/adp8860_bl.c
ADT746X FAN DRIVER
S: Supported
W: http://wiki.analog.com/ADXL345
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
F: drivers/input/misc/adxl34x.c
F: drivers/iio/accel/adxl355_i2c.c
F: drivers/iio/accel/adxl355_spi.c
+ ADXL367 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
+ S: Supported
+ W: http://ez.analog.com/community/linux-device-drivers
+ F: Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml
+ F: drivers/iio/accel/adxl367*
+
ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml
F: drivers/iio/accel/adxl372.c
F: drivers/iio/accel/adxl372_i2c.c
S: Maintained
F: drivers/platform/x86/amd-pmc.*
+ AMD HSMP DRIVER
+ S: Maintained
+ F: Documentation/x86/amd_hsmp.rst
+ F: arch/x86/include/asm/amd_hsmp.h
+ F: arch/x86/include/uapi/asm/amd_hsmp.h
+ F: drivers/platform/x86/amd_hsmp.c
+
AMD POWERPLAY AND SWSMU
S: Supported
F: Documentation/admin-guide/pm/amd-pstate.rst
F: drivers/cpufreq/amd-pstate*
+ F: tools/power/x86/amd_pstate_tracer/amd_pstate_trace.py
AMD PTDMA DRIVER
F: Documentation/hid/amd-sfh*
F: drivers/hid/amd-sfh-hid/
+ AMPHION VPU CODEC V4L2 DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/media/amphion,vpu.yaml
+ F: drivers/media/platform/amphion/
+
AMS AS73211 DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml
F: drivers/iio/adc/ad7192.c
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml
F: drivers/iio/adc/ad7292.c
+ ANALOG DEVICES INC AD7293 DRIVER
+ S: Supported
+ W: https://ez.analog.com/linux-software-drivers
+ F: Documentation/devicetree/bindings/iio/dac/adi,ad7293.yaml
+ F: drivers/iio/dac/ad7293.c
+
ANALOG DEVICES INC AD7768-1 DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
F: drivers/iio/adc/ad7768-1.c
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
F: drivers/iio/adc/ad7780.c
S: Maintained
F: drivers/media/i2c/ad9389b*
+ ANALOG DEVICES INC ADA4250 DRIVER
+ S: Supported
+ W: https://ez.analog.com/linux-software-drivers
+ F: Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml
+ F: drivers/iio/amplifiers/ada4250.c
+
ANALOG DEVICES INC ADGS1408 DRIVER
S: Supported
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/net/adi,adin.yaml
F: drivers/net/phy/adin.c
S: Supported
F: drivers/iio/imu/adis.c
+ F: drivers/iio/imu/adis_buffer.c
+ F: drivers/iio/imu/adis_trigger.c
F: include/linux/iio/imu/adis.h
ANALOG DEVICES INC ADIS16460 DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml
F: drivers/iio/imu/adis16460.c
ANALOG DEVICES INC ADIS16475 DRIVER
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
S: Supported
F: drivers/iio/imu/adis16475.c
F: Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml
F: drivers/hwmon/adm1177.c
+ ANALOG DEVICES INC ADMV1013 DRIVER
+ S: Supported
+ W: https://ez.analog.com/linux-software-drivers
+ F: Documentation/devicetree/bindings/iio/frequency/adi,admv1013.yaml
+ F: drivers/iio/frequency/admv1013.c
+
+ ANALOG DEVICES INC ADMV8818 DRIVER
+ S: Supported
+ W: https://ez.analog.com/linux-software-drivers
+ F: Documentation/devicetree/bindings/iio/filter/adi,admv8818.yaml
+ F: drivers/iio/filter/admv8818.c
+
+ ANALOG DEVICES INC ADMV1014 DRIVER
+ S: Supported
+ W: https://ez.analog.com/linux-software-drivers
+ F: Documentation/devicetree/bindings/iio/frequency/adi,admv1014.yaml
+ F: drivers/iio/frequency/admv1014.c
+
ANALOG DEVICES INC ADP5061 DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/power/supply/adp5061.c
+ ANALOG DEVICES INC ADRF6780 DRIVER
+ S: Supported
+ W: https://ez.analog.com/linux-software-drivers
+ F: Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml
+ F: drivers/iio/frequency/adrf6780.c
+
ANALOG DEVICES INC ADV7180 DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/media/i2c/adv7180.c
F: Documentation/devicetree/bindings/media/i2c/adv7180.yaml
S: Supported
W: http://wiki.analog.com/
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: sound/soc/codecs/ad1*
F: sound/soc/codecs/ad7*
F: sound/soc/codecs/adau*
ANALOG DEVICES INC DMA DRIVERS
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: drivers/dma/dma-axi-dmac.c
ANALOG DEVICES INC IIO DRIVERS
S: Supported
W: http://wiki.analog.com/
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
F: Documentation/devicetree/bindings/iio/*/adi,*
F: drivers/clk/analogbits/*
F: include/linux/clk/analogbits*
- ANDES ARCHITECTURE
- S: Supported
- T: git https://git.kernel.org/pub/scm/linux/kernel/git/greentime/linux.git
- F: Documentation/devicetree/bindings/interrupt-controller/andestech,ativic32.txt
- F: Documentation/devicetree/bindings/nds32/
- F: arch/nds32/
- N: nds32
- K: nds32
-
ANDROID CONFIG FRAGMENTS
S: Supported
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
F: drivers/android/
- F: drivers/staging/android/
ANDROID GOLDFISH PIC DRIVER
S: Maintained
+ C: irc://irc.libera.chat/armlinux
T: git git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git
F: arch/arm/boot/dts/Makefile
F: arch/arm64/boot/dts/Makefile
ARM SUB-ARCHITECTURES
S: Maintained
+ C: irc://irc.libera.chat/armlinux
T: git git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git
F: arch/arm/mach-*/
F: arch/arm/plat-*/
F: drivers/clk/sunxi/
ARM/Allwinner sunXi SoC support
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git
T: git https://github.com/AsahiLinux/linux.git
F: Documentation/devicetree/bindings/arm/apple.yaml
F: Documentation/devicetree/bindings/arm/apple/*
+ F: Documentation/devicetree/bindings/clock/apple,nco.yaml
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
- F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
+ F: Documentation/devicetree/bindings/interrupt-controller/apple,*
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
F: Documentation/devicetree/bindings/power/apple*
F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
F: arch/arm64/boot/dts/apple/
+ F: drivers/clk/clk-apple-nco.c
F: drivers/i2c/busses/i2c-pasemi-core.c
F: drivers/i2c/busses/i2c-pasemi-platform.c
F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
F: drivers/soc/apple/*
+ F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h
F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h
F: arch/arm64/boot/dts/intel/keembay-evm.dts
F: arch/arm64/boot/dts/intel/keembay-soc.dtsi
- ARM/INTEL RESEARCH IMOTE/STARGATE 2 MACHINE SUPPORT
- S: Maintained
- F: arch/arm/mach-pxa/stargate2.c
- F: drivers/pcmcia/pxa2xx_stargate2.c
-
ARM/INTEL XSC3 (MANZANO) ARM CORE
ARM/Microchip (AT91) SoC support
- M: Ludovic Desroches <ludovic.desroches@microchip.com>
+ M: Claudiu Beznea <claudiu.beznea@microchip.com>
S: Supported
W: http://www.linux4sam.org
S: Supported
F: Documentation/devicetree/bindings/*/*/*npcm*
F: Documentation/devicetree/bindings/*/*npcm*
+ F: Documentation/devicetree/bindings/arm/npcm/*
F: arch/arm/boot/dts/nuvoton-npcm*
F: arch/arm/mach-npcm/
F: drivers/*/*npcm*
S: Maintained
+ W: https://github.com/neuschaefer/wpcm450/wiki
F: Documentation/devicetree/bindings/*/*wpcm*
F: arch/arm/boot/dts/nuvoton-wpcm450*
F: arch/arm/mach-npcm/wpcm450.c
+ F: drivers/*/*/*wpcm*
F: drivers/*/*wpcm*
ARM/NXP S32G ARCHITECTURE
F: Documentation/devicetree/bindings/arm/rda.yaml
F: Documentation/devicetree/bindings/gpio/gpio-rda.yaml
F: Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt
- F: Documentation/devicetree/bindings/serial/rda,8810pl-uart.txt
+ F: Documentation/devicetree/bindings/serial/rda,8810pl-uart.yaml
F: Documentation/devicetree/bindings/timer/rda,8810pl-timer.txt
F: arch/arm/boot/dts/rda8810pl-*
F: drivers/clocksource/timer-rda.c
S: Supported
Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/
+ C: irc://irc.libera.chat/renesas-soc
T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next
F: Documentation/devicetree/bindings/arm/renesas.yaml
F: arch/arm64/boot/dts/renesas/
N: rockchip
ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES
S: Maintained
+ C: irc://irc.libera.chat/linux-exynos
Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux.git
F: Documentation/arm/samsung/
F: Documentation/devicetree/bindings/arm/samsung/
F: Documentation/devicetree/bindings/power/pd-samsung.yaml
S: Maintained
- F: drivers/media/platform/s5p-g2d/
+ F: drivers/media/platform/samsung/s5p-g2d/
ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT
S: Maintained
- F: drivers/media/platform/s5p-jpeg/
+ F: drivers/media/platform/samsung/s5p-jpeg/
ARM/SAMSUNG S5P SERIES Multi Format Codec (MFC) SUPPORT
S: Maintained
- F: drivers/media/platform/s5p-mfc/
+ F: drivers/media/platform/samsung/s5p-mfc/
ARM/SHMOBILE ARM ARCHITECTURE
S: Supported
Q: http://patchwork.kernel.org/project/linux-renesas-soc/list/
+ C: irc://irc.libera.chat/renesas-soc
T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-devel.git next
F: Documentation/devicetree/bindings/arm/renesas.yaml
F: arch/arm/boot/dts/emev2*
F: drivers/cpufreq/sti-cpufreq.c
F: drivers/dma/st_fdma*
F: drivers/i2c/busses/i2c-st.c
- F: drivers/media/platform/sti/c8sectpfe/
+ F: drivers/media/platform/st/sti/c8sectpfe/
F: drivers/media/rc/st_rc.c
F: drivers/mmc/host/sdhci-st.c
F: drivers/phy/st/phy-miphy28lp.c
N: stm
ARM/Synaptics SoC support
S: Maintained
F: Documentation/devicetree/bindings/media/tegra-cec.txt
F: drivers/media/cec/platform/tegra/
+ ARM/TESLA FSD SoC SUPPORT
+ S: Maintained
+ F: arch/arm64/boot/dts/tesla*
+
ARM/TETON BGA MACHINE SUPPORT
F: Documentation/devicetree/bindings/net/asix,ax88796c.yaml
F: drivers/net/ethernet/asix/ax88796c_*
+ ASPEED PECI CONTROLLER
+ S: Supported
+ F: Documentation/devicetree/bindings/peci/peci-aspeed.yaml
+ F: drivers/peci/controller/peci-aspeed.c
+
ASPEED PINCTRL DRIVERS
S: Maintained
F: Documentation/devicetree/bindings/media/aspeed-video.txt
- F: drivers/media/platform/aspeed-video.c
+ F: drivers/media/platform/aspeed/
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
S: Maintained
F: drivers/hwmon/asus_wmi_ec_sensors.c
+ ASUS EC HARDWARE MONITOR DRIVER
+ S: Maintained
+ F: drivers/hwmon/asus-ec-sensors.c
+
ASUS WIRELESS RADIO CONTROL DRIVER
F: drivers/net/wireless/ath/ath5k/
ATHEROS ATH6KL WIRELESS DRIVER
- S: Supported
+ S: Orphan
W: https://wireless.wiki.kernel.org/en/users/Drivers/ath6kl
- T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
F: drivers/net/wireless/ath/ath6kl/
ATI_REMOTE2 DRIVER
S: Maintained
F: arch/*/include/asm/atomic*.h
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml
F: drivers/hwmon/axi-fan-control.c
S: Supported
W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
- F: drivers/media/platform/sti/bdisp
+ F: drivers/media/platform/st/sti/bdisp
BECKHOFF CX5020 ETHERCAT MASTER DRIVER
F: Documentation/block/
F: block/
F: drivers/block/
+ F: include/linux/bio.h
F: include/linux/blk*
F: kernel/trace/blktrace.c
F: lib/sbitmap.c
F: net/sched/cls_bpf.c
F: samples/bpf/
F: scripts/bpf_doc.py
+ F: scripts/pahole-flags.sh
+ F: scripts/pahole-version.sh
F: tools/bpf/
F: tools/lib/bpf/
F: tools/testing/selftests/bpf/
F: drivers/net/ethernet/broadcom/bcm4908_enet.*
F: drivers/net/ethernet/broadcom/unimac.h
+ BROADCOM BCM4908 PINMUX DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/pinctrl/brcm,bcm4908-pinctrl.yaml
+ F: drivers/pinctrl/bcm/pinctrl-bcm4908.c
+
BROADCOM BCM5301X ARM ARCHITECTURE
S: Maintained
- T: git git://github.com/broadcom/cygnus-linux.git
+ T: git git://github.com/broadcom/stblinux.git
F: arch/arm64/boot/dts/broadcom/northstar2/*
F: arch/arm64/boot/dts/broadcom/stingray/*
F: drivers/clk/bcm/clk-ns*
S: Maintained
F: drivers/mtd/nand/raw/brcmnand/
+ F: include/linux/platform_data/brcmnand.h
BROADCOM STB PCIE DRIVER
K: csky
CA8210 IEEE-802.15.4 RADIO DRIVER
- S: Maintained
+ S: Orphan
W: https://github.com/Cascoda/ca8210-linux.git
F: Documentation/devicetree/bindings/net/ieee802154/ca8210.txt
F: drivers/net/ieee802154/ca8210.c
S: Orphan
T: git git://linuxtv.org/media_tree.git
F: Documentation/admin-guide/media/cafe_ccic*
- F: drivers/media/platform/marvell-ccic/
+ F: drivers/media/platform/marvell/
CAIF NETWORK LAYER
CEPH COMMON CODE (LIBCEPH)
S: Supported
W: http://ceph.com/
CEPH DISTRIBUTED FILE SYSTEM CLIENT (CEPH)
S: Supported
CHROME HARDWARE PLATFORM SUPPORT
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git
F: drivers/platform/chrome/
CHROMEOS EC CODEC DRIVER
S: Maintained
F: Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml
F: sound/soc/codecs/cros_ec_codec.*
CHROMEOS EC SUBDRIVERS
S: Maintained
F: drivers/power/supply/cros_usbpd-charger.c
N: cros_ec
CHROMEOS EC USB TYPE-C DRIVER
S: Maintained
F: drivers/platform/chrome/cros_ec_typec.c
CHROMEOS EC USB PD NOTIFY DRIVER
S: Maintained
F: drivers/platform/chrome/cros_usbpd_notify.c
F: include/linux/platform_data/cros_usbpd_notify.h
S: Maintained
F: Documentation/devicetree/bindings/media/coda.yaml
- F: drivers/media/platform/coda/
+ F: drivers/media/platform/chips-media/
CODE OF CONDUCT
CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG)
S: Maintained
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-counter
F: Documentation/driver-api/generic-counter.rst
F: drivers/counter/
F: drivers/cpuidle/cpuidle-psci.h
F: drivers/cpuidle/cpuidle-psci-domain.c
+ CPUIDLE DRIVER - DT IDLE PM DOMAIN
+ S: Supported
+ F: drivers/cpuidle/dt_idle_genpd.c
+ F: drivers/cpuidle/dt_idle_genpd.h
+
+ CPUIDLE DRIVER - RISC-V SBI
+ S: Maintained
+ F: drivers/cpuidle/cpuidle-riscv-sbi.c
+
CRAMFS FILESYSTEM
S: Maintained
S: Maintained
+ F: Documentation/ABI/testing/sysfs-kernel-mm-damon
F: Documentation/admin-guide/mm/damon/
F: Documentation/vm/damon/
F: include/linux/damon.h
DELL LAPTOP SMM DRIVER
S: Maintained
+ F: Documentation/ABI/obsolete/procfs-i8k
F: drivers/hwmon/dell-smm-hwmon.c
F: include/uapi/linux/i8k.h
S: Supported
W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
- F: drivers/media/platform/sti/delta
+ F: drivers/media/platform/st/sti/delta
DELTA AHE-50DC FAN CONTROL MODULE DRIVER
F: Documentation/hwmon/dps920ab.rst
F: drivers/hwmon/pmbus/dps920ab.c
+ DELTA NETWORKS TN48M CPLD DRIVERS
+ S: Maintained
+ F: Documentation/devicetree/bindings/gpio/delta,tn48m-gpio.yaml
+ F: Documentation/devicetree/bindings/mfd/delta,tn48m-cpld.yaml
+ F: Documentation/devicetree/bindings/reset/delta,tn48m-reset.yaml
+ F: drivers/gpio/gpio-tn48m.c
+ F: include/dt-bindings/reset/delta,tn48m-reset.h
+
DENALI NAND DRIVER
S: Orphan
DEVICE-MAPPER (LVM)
- M: Mike Snitzer <snitzer@redhat.com>
+ M: Mike Snitzer <snitzer@kernel.org>
S: Maintained
F: Documentation/driver-api/dma-buf.rst
F: drivers/dma-buf/
F: include/linux/*fence.h
- F: include/linux/dma-buf*
+ F: include/linux/dma-buf.h
F: include/linux/dma-resv.h
K: \bdma_(?:buf|fence|resv)\b
F: kernel/dma/
DMA MAPPING BENCHMARK
- M: Barry Song <song.bao.hua@hisilicon.com>
+ M: Xiang Chen <chenxiang66@hisilicon.com>
F: kernel/dma/map_benchmark.c
F: tools/testing/selftests/dma/
DMA-BUF HEAPS FRAMEWORK
- R: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+ R: Benjamin Gaignard <benjamin.gaignard@collabora.com>
DRBD DRIVER
S: Supported
W: http://www.drbd.org
F: Documentation/devicetree/bindings/display/panel/olimex,lcd-olinuxino.yaml
F: drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c
+DRM DRIVER FOR PARADE PS8640 BRIDGE CHIP
+F: Documentation/devicetree/bindings/display/bridge/ps8640.yaml
+F: drivers/gpu/drm/bridge/parade-ps8640.c
+
DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS
S: Maintained
S: Orphan / Obsolete
F: drivers/gpu/drm/tdfx/
+DRM DRIVER FOR TI SN65DSI86 BRIDGE CHIP
+F: Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.yaml
+F: drivers/gpu/drm/bridge/ti-sn65dsi86.c
+
DRM DRIVER FOR TPO TPG110 PANELS
S: Maintained
F: drivers/gpu/drm/vboxvideo/
DRM DRIVER FOR VMWARE VIRTUAL GPU
S: Supported
T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
+F: Documentation/devicetree/bindings/display/bridge/
F: drivers/gpu/drm/bridge/
DRM DRIVERS FOR EXYNOS
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
F: Documentation/devicetree/bindings/display/exynos/
+ F: Documentation/devicetree/bindings/display/samsung/
F: drivers/gpu/drm/exynos/
F: include/uapi/drm/exynos_drm.h
F: drivers/gpu/drm/rockchip/
DRM DRIVERS FOR STI
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
DRM DRIVERS FOR STM
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/edac/sb_edac.c
- EDAC-SIFIVE
- S: Supported
- F: drivers/edac/sifive_edac.c
-
EDAC-SKYLAKE
ETHERNET BRIDGE
S: Maintained
EXEC & BINFMT API
+ S: Supported
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve
F: arch/alpha/kernel/binfmt_loader.c
F: arch/x86/ia32/ia32_aout.c
F: fs/*binfmt_*.c
F: include/linux/binfmts.h
F: include/linux/elf.h
F: include/uapi/linux/binfmts.h
+ F: include/uapi/linux/elf.h
F: tools/testing/selftests/exec/
N: asm/elf.h
N: binfmt
S: Supported
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
F: security/integrity/evm/
+ F: security/integrity/
EXTENSIBLE FIRMWARE INTERFACE (EFI)
W: http://floatingpoint.sourceforge.net/emulator/index.html
F: arch/x86/math-emu/
+ FRAMEBUFFER CORE
+ F: drivers/video/fbdev/core/
+ S: Odd Fixes
+ T: git git://anongit.freedesktop.org/drm/drm-misc
+
FRAMEBUFFER LAYER
S: Maintained
F: drivers/soc/fsl/qe/
- F: include/soc/fsl/*qe*.h
- F: include/soc/fsl/*ucc*.h
+ F: include/soc/fsl/qe/
FREESCALE QUICC ENGINE UCC ETHERNET DRIVER
F: Documentation/devicetree/bindings/soc/fsl/
F: drivers/soc/fsl/
F: include/linux/fsl/
+ F: include/soc/fsl/
FREESCALE SOC FS_ENET DRIVER
F: include/linux/fs_enet_pd.h
FREESCALE SOC SOUND DRIVERS
- M: Nicolin Chen <nicoleotsuka@gmail.com>
+ M: Shengjiu Wang <shengjiu.wang@gmail.com>
- R: Shengjiu Wang <shengjiu.wang@gmail.com>
+ R: Nicolin Chen <nicoleotsuka@gmail.com>
S: Maintained
S: Maintained
F: drivers/platform/x86/fujitsu-tablet.c
+ FUNGIBLE ETHERNET DRIVERS
+ S: Supported
+ F: drivers/net/ethernet/fungible/
+
FUSE: FILESYSTEM IN USERSPACE
F: drivers/gpio/gpio-hisi.c
HISILICON HIGH PERFORMANCE RSA ENGINE DRIVER (HPRE)
- M: Zaibo Xu <xuzaibo@huawei.com>
+ M: Longfang Liu <liulongfang@huawei.com>
S: Maintained
F: Documentation/ABI/testing/debugfs-hisi-hpre
S: Maintained
F: Documentation/ABI/testing/debugfs-hisi-zip
F: drivers/crypto/hisilicon/qm.c
- F: drivers/crypto/hisilicon/qm.h
F: drivers/crypto/hisilicon/sgl.c
F: drivers/crypto/hisilicon/zip/
+ F: include/linux/hisi_acc_qm.h
HISILICON ROCE DRIVER
F: drivers/scsi/hisi_sas/
HISILICON SECURITY ENGINE V2 DRIVER (SEC2)
S: Maintained
F: Documentation/ABI/testing/debugfs-hisi-sec
F: drivers/mfd/hi6421-spmi-pmic.c
HISILICON TRUE RANDOM NUMBER GENERATOR V2 SUPPORT
- M: Zaibo Xu <xuzaibo@huawei.com>
+ M: Weili Qian <qianweili@huawei.com>
S: Maintained
F: drivers/crypto/hisilicon/trng/trng.c
S: Supported
W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
- F: drivers/media/platform/sti/hva
+ F: drivers/media/platform/st/sti/hva
HWPOISON MEMORY FAILURE HANDLING
S: Maintained
F: drivers/media/i2c/hi846.c
+ HYNIX HI847 SENSOR DRIVER
+ S: Maintained
+ F: drivers/media/i2c/hi847.c
+
Hyper-V/Azure CORE AND DRIVERS
W: https://github.com/o2genum/ideapad-slidebar
F: drivers/input/misc/ideapad_slidebar.c
+ IDMAPPED MOUNTS
+ S: Maintained
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux.git
+ F: Documentation/filesystems/idmappings.rst
+ F: tools/testing/selftests/mount_setattr/
+ F: include/linux/mnt_idmapping.h
+
IDT VersaClock 5 CLOCK DRIVER
S: Maintained
S: Maintained
F: drivers/usb/atm/ueagle-atm.c
+ IMAGIS TOUCHSCREEN DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/input/touchscreen/imagis,ist3038c.yaml
+ F: drivers/input/touchscreen/imagis.c
+
IMGTEC ASCII LCD DRIVER
S: Maintained
F: sound/soc/codecs/jz47*
F: sound/soc/jz4740/
+ INJOINIC IP5xxx POWER BANK IC DRIVER
+ S: Maintained
+ F: drivers/power/supply/ip5xxx_power.c
+
INOTIFY
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
F: security/integrity/ima/
+ F: security/integrity/
INTEL 810/815 FRAMEBUFFER DRIVER
F: arch/x86/include/asm/intel_scu_ipc.h
F: drivers/platform/x86/intel_scu_*
+ INTEL SDSI DRIVER
+ S: Supported
+ F: drivers/platform/x86/intel/sdsi.c
+ F: tools/arch/x86/intel_sdsi/
+ F: tools/testing/selftests/drivers/sdsi/
+
INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
S: Maintained
F: drivers/firmware/stratix10-svc.c
F: include/linux/firmware/intel/stratix10-smc.h
F: include/linux/firmware/intel/stratix10-svc-client.h
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux.git
INTEL TELEMETRY DRIVER
S: Maintained
- F: drivers/platform/x86/intel/uncore-frequency.c
+ F: Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst
+ F: drivers/platform/x86/intel/uncore-frequency/
INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER
F: Documentation/devicetree/bindings/counter/interrupt-counter.yaml
F: drivers/counter/interrupt-cnt.c
+ INTERSIL ISL7998X VIDEO DECODER DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/media/i2c/isil,isl79987.yaml
+ F: drivers/media/i2c/isl7998x.c
+
INVENSENSE ICM-426xx IMU DRIVER
F: include/linux/of_iommu.h
F: include/uapi/linux/iommu.h
+ IOSYS-MAP HELPERS
+ S: Maintained
+ T: git git://anongit.freedesktop.org/drm/drm-misc
+ F: include/linux/iosys-map.h
+
IO_URING
S: Supported
W: http://openipmi.sourceforge.net/
+ T: git https://github.com/cminyard/linux-ipmi.git for-next
F: Documentation/driver-api/ipmi.rst
F: Documentation/devicetree/bindings/ipmi/
F: drivers/char/ipmi/
S: Maintained
- F: drivers/media/platform/rcar_jpu.c
+ F: drivers/media/platform/renesas/rcar_jpu.c
JSM Neo PCI based serial card
S: Supported
+ F: Documentation/admin-guide/reporting-regressions.rst
+ F: Documentation/process/handling-regressions.rst
KERNEL SELFTEST FRAMEWORK
KERNEL SMB3 SERVER (KSMBD)
S: Maintained
T: git git://git.samba.org/ksmbd.git
KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
S: Supported
W: http://www.ibm.com/developerworks/linux/linux390/
F: include/uapi/linux/keyctl.h
F: security/keys/
+ KEYS/KEYRINGS_INTEGRITY
+ S: Supported
+ F: security/integrity/platform_certs
+
KFENCE
S: Maintained
F: drivers/ata/pata_arasan_cf.c
F: include/linux/pata_arasan_cf_data.h
+ LIBATA PATA DRIVERS
+ F: drivers/ata/ata_*.c
+ F: drivers/ata/pata_*.c
+
LIBATA PATA FARADAY FTIDE010 AND GEMINI SATA BRIDGE DRIVERS
F: include/linux/ata.h
F: include/linux/libata.h
- LIBNVDIMM BLK: MMIO-APERTURE DRIVER
- S: Supported
- Q: https://patchwork.kernel.org/project/linux-nvdimm/list/
- P: Documentation/nvdimm/maintainer-entry-profile.rst
- F: drivers/nvdimm/blk.c
- F: drivers/nvdimm/region_devs.c
-
LIBNVDIMM BTT: BLOCK TRANSLATION TABLE
LITEX PLATFORM
S: Maintained
F: Documentation/devicetree/bindings/*/litex,*.yaml
F: arch/openrisc/boot/dts/or1klitex.dts
- F: drivers/soc/litex/litex_soc_ctrl.c
- F: drivers/tty/serial/liteuart.c
F: include/linux/litex.h
+ F: drivers/tty/serial/liteuart.c
+ F: drivers/soc/litex/*
+ F: drivers/net/ethernet/litex/*
+ F: drivers/mmc/host/litex_mmc.c
+ N: litex
LIVE PATCHING
F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
F: drivers/iio/dac/ltc1660.c
+ LTC2688 IIO DAC DRIVER
+ S: Supported
+ W: http://ez.analog.com/community/linux-device-drivers
+ F: Documentation/ABI/testing/sysfs-bus-iio-dac-ltc2688
+ F: Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml
+ F: drivers/iio/dac/ltc2688.c
+
LTC2947 HARDWARE MONITOR DRIVER
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml
F: drivers/hwmon/ltc2947-core.c
F: drivers/hwmon/ltc2947-i2c.c
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
F: drivers/iio/temperature/ltc2983.c
S: Supported
- W: http://ez.analog.com/community/linux-device-drivers
+ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
F: drivers/i2c/muxes/i2c-mux-ltc4306.c
W: http://linux-test-project.github.io/
T: git git://github.com/linux-test-project/ltp.git
+ LYNX 28G SERDES PHY DRIVER
+ S: Supported
+ F: Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
+ F: drivers/phy/freescale/phy-fsl-lynx-28g.c
+
LYNX PCS MODULE
F: drivers/phy/marvell/phy-mvebu-a3700-comphy.c
F: drivers/phy/marvell/phy-mvebu-a3700-utmi.c
+ MARVELL ARMADA 3700 SERIAL DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/clock/marvell,armada-3700-uart-clock.yaml
+ F: Documentation/devicetree/bindings/serial/mvebu-uart.txt
+ F: drivers/tty/serial/mvebu-uart.c
+
MARVELL ARMADA DRM SUPPORT
S: Maintained
MAXIM MAX17040 FAMILY FUEL GAUGE DRIVERS
MAXIM MAX17042 FAMILY FUEL GAUGE DRIVERS
F: drivers/regulator/max77650-regulator.c
F: include/linux/mfd/max77650.h
+ MAXIM MAX77714 PMIC MFD DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/mfd/maxim,max77714.yaml
+ F: drivers/mfd/max77714.c
+ F: include/linux/mfd/max77714.h
+
MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER
S: Supported
- F: Documentation/devicetree/bindings/*/*max77802.txt
+ F: Documentation/devicetree/bindings/*/*max77802.yaml
F: drivers/regulator/max77802-regulator.c
F: include/dt-bindings/*/*max77802.h
F: drivers/power/supply/max77976_charger.c
MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
S: Supported
+ F: Documentation/devicetree/bindings/power/supply/maxim,max14577.yaml
+ F: Documentation/devicetree/bindings/power/supply/maxim,max77693.yaml
F: drivers/power/supply/max14577_charger.c
F: drivers/power/supply/max77693_charger.c
MAXIM PMIC AND MUIC DRIVERS FOR EXYNOS BASED BOARDS
S: Supported
+ F: Documentation/devicetree/bindings/*/maxim,max14577.yaml
F: Documentation/devicetree/bindings/*/maxim,max77686.yaml
+ F: Documentation/devicetree/bindings/*/maxim,max77693.yaml
+ F: Documentation/devicetree/bindings/*/maxim,max77843.yaml
F: Documentation/devicetree/bindings/clock/maxim,max77686.txt
- F: Documentation/devicetree/bindings/mfd/max14577.txt
F: Documentation/devicetree/bindings/mfd/max77693.txt
+ F: drivers/*/*max77843.c
F: drivers/*/max14577*.c
F: drivers/*/max77686*.c
F: drivers/*/max77693*.c
S: Maintained
T: git git://linuxtv.org/media_tree.git
- F: drivers/media/platform/imx-pxp.[ch]
+ F: drivers/media/platform/nxp/imx-pxp.[ch]
MEDIA DRIVERS FOR ASCOT2E
S: Maintained
T: git git://linuxtv.org/media_tree.git
F: Documentation/admin-guide/media/imx7.rst
+ F: Documentation/devicetree/bindings/media/nxp,imx-mipi-csi2.yaml
F: Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml
- F: Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml
+ F: drivers/media/platform/imx/imx-mipi-csis.c
F: drivers/staging/media/imx/imx7-media-csi.c
- F: drivers/staging/media/imx/imx7-mipi-csis.c
MEDIA DRIVERS FOR HELENE
S: Maintained
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt
- F: drivers/staging/media/tegra-vde/
+ F: drivers/media/platform/nvidia/tegra-vde/
MEDIA DRIVERS FOR RENESAS - CEU
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/renesas,ceu.yaml
- F: drivers/media/platform/renesas-ceu.c
+ F: drivers/media/platform/renesas/renesas-ceu.c
F: include/media/drv-intf/renesas-ceu.h
MEDIA DRIVERS FOR RENESAS - DRIF
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/renesas,drif.yaml
- F: drivers/media/platform/rcar_drif.c
+ F: drivers/media/platform/renesas/rcar_drif.c
MEDIA DRIVERS FOR RENESAS - FCP
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/renesas,fcp.yaml
- F: drivers/media/platform/rcar-fcp.c
+ F: drivers/media/platform/renesas/rcar-fcp.c
F: include/media/rcar-fcp.h
MEDIA DRIVERS FOR RENESAS - FDP1
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/renesas,fdp1.yaml
- F: drivers/media/platform/rcar_fdp1.c
+ F: drivers/media/platform/renesas/rcar_fdp1.c
MEDIA DRIVERS FOR RENESAS - VIN
F: Documentation/devicetree/bindings/media/renesas,csi2.yaml
F: Documentation/devicetree/bindings/media/renesas,isp.yaml
F: Documentation/devicetree/bindings/media/renesas,vin.yaml
- F: drivers/media/platform/rcar-isp.c
- F: drivers/media/platform/rcar-vin/
+ F: drivers/media/platform/renesas/rcar-isp.c
+ F: drivers/media/platform/renesas/rcar-vin/
MEDIA DRIVERS FOR RENESAS - VSP1
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/renesas,vsp1.yaml
- F: drivers/media/platform/vsp1/
+ F: drivers/media/platform/renesas/vsp1/
MEDIA DRIVERS FOR ST STV0910 DEMODULATOR ICs
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
- F: drivers/media/platform/stm32/stm32-dcmi.c
+ F: drivers/media/platform/st/stm32/stm32-dcmi.c
MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
S: Supported
F: Documentation/devicetree/bindings/media/mediatek-jpeg-decoder.txt
- F: drivers/media/platform/mtk-jpeg/
+ F: drivers/media/platform/mediatek/jpeg/
MEDIATEK MDP DRIVER
S: Supported
F: Documentation/devicetree/bindings/media/mediatek-mdp.txt
- F: drivers/media/platform/mtk-mdp/
- F: drivers/media/platform/mtk-vpu/
+ F: drivers/media/platform/mediatek/mdp/
+ F: drivers/media/platform/mediatek/vpu/
MEDIATEK MEDIA DRIVER
S: Supported
F: Documentation/devicetree/bindings/media/mediatek-vcodec.txt
F: Documentation/devicetree/bindings/media/mediatek-vpu.txt
- F: drivers/media/platform/mtk-vcodec/
- F: drivers/media/platform/mtk-vpu/
+ F: drivers/media/platform/mediatek/vcodec/
+ F: drivers/media/platform/mediatek/vpu/
MEDIATEK MMC/SD/SDIO DRIVER
S: Maintained
+ F: Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
F: drivers/net/wireless/mediatek/mt76/
MEDIATEK MT7601U WIRELESS LAN DRIVER
F: kernel/sched/membarrier.c
MEMBLOCK
- M: Mike Rapoport <rppt@linux.ibm.com>
+ M: Mike Rapoport <rppt@kernel.org>
S: Maintained
F: Documentation/core-api/boot-time-mm.rst
F: include/linux/memblock.h
F: mm/memblock.c
+ F: tools/testing/memblock/
MEMORY CONTROLLER DRIVERS
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl.git
S: Supported
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml
- F: drivers/media/platform/meson/ge2d/
+ F: drivers/media/platform/amlogic/meson-ge2d/
MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS
S: Supported
F: sound/soc/atmel
+ MICROCHIP CSI2DC DRIVER
+ S: Supported
+ F: Documentation/devicetree/bindings/media/microchip,csi2dc.yaml
+ F: drivers/media/platform/atmel/microchip-csi2dc.c
+
MICROCHIP ECC DRIVER
S: Supported
F: Documentation/devicetree/bindings/media/atmel,isc.yaml
F: Documentation/devicetree/bindings/media/microchip,xisc.yaml
- F: drivers/media/platform/atmel/atmel-isc-base.c
- F: drivers/media/platform/atmel/atmel-isc-regs.h
- F: drivers/media/platform/atmel/atmel-isc.h
- F: drivers/media/platform/atmel/atmel-sama5d2-isc.c
- F: drivers/media/platform/atmel/atmel-sama7g5-isc.c
+ F: drivers/media/platform/atmel/atmel-isc*
+ F: drivers/media/platform/atmel/atmel-sama*-isc*
F: include/linux/atmel-isc-media.h
MICROCHIP ISI DRIVER
W: http://www.nftables.org/
Q: http://patchwork.ozlabs.org/project/netfilter-devel/list/
C: irc://irc.libera.chat/netfilter
- T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git
- T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git
F: include/linux/netfilter*
F: include/linux/netfilter/
F: include/net/netfilter/
NETWORKING DRIVERS
S: Maintained
Q: https://patchwork.kernel.org/project/netdevbpf/list/
NETWORKING [GENERAL]
S: Maintained
Q: https://patchwork.kernel.org/project/netdevbpf/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
F: Documentation/networking/
+ F: Documentation/process/maintainer-netdev.rst
F: include/linux/in.h
F: include/linux/net.h
F: include/linux/netdevice.h
F: net/ipv4/nexthop.c
NFC SUBSYSTEM
S: Maintained
NFS, SUNRPC, AND LOCKD CLIENTS
+ M: Anna Schumaker <anna@kernel.org>
S: Maintained
W: http://client.linux-nfs.org
NTB AMD DRIVER
S: Supported
F: drivers/ntb/hw/amd/
S: Supported
W: https://github.com/jonmason/ntb/wiki
T: git git://github.com/jonmason/ntb.git
NTB IDT DRIVER
S: Supported
F: drivers/ntb/hw/idt/
NTB INTEL DRIVER
S: Supported
W: https://github.com/davejiang/linux/wiki
T: git https://github.com/davejiang/linux.git
S: Maintained
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux.git clk/imx
+ F: Documentation/devicetree/bindings/clock/imx*
F: drivers/clk/imx/
+ F: include/dt-bindings/clock/imx*
NXP i.MX 8MQ DCSS DRIVER
F: drivers/regulator/pf8x00-regulator.c
NXP PTN5150A CC LOGIC AND EXTCON DRIVER
S: Maintained
F: Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml
S: Maintained
F: Documentation/devicetree/bindings/media/ti,omap3isp.txt
- F: drivers/media/platform/omap3isp/
+ F: drivers/media/platform/ti/omap3isp/
F: drivers/staging/media/omap4iss/
OMAP MMC SUPPORT
S: Maintained
F: drivers/char/pcmcia/cm4040_cs.*
+ OMNIVISION OG01A1B SENSOR DRIVER
+ S: Maintained
+ F: drivers/media/i2c/og01a1b.c
+
OMNIVISION OV02A10 SENSOR DRIVER
F: Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml
F: drivers/media/i2c/ov02a10.c
+ OMNIVISION OV08D10 SENSOR DRIVER
+ S: Maintained
+ T: git git://linuxtv.org/media_tree.git
+ F: drivers/media/i2c/ov08d10.c
+
OMNIVISION OV13858 SENSOR DRIVER
S: Maintained
F: drivers/char/hw_random/optee-rng.c
+ OP-TEE RTC DRIVER
+ S: Maintained
+ F: drivers/rtc/rtc-optee.c
+
OPA-VNIC DRIVER
S: Maintained
+ C: irc://irc.libera.chat/devicetree
W: http://www.devicetree.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
F: Documentation/ABI/testing/sysfs-firmware-ofw
OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
S: Maintained
+ C: irc://irc.libera.chat/devicetree
Q: http://patchwork.ozlabs.org/project/devicetree-bindings/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
F: Documentation/devicetree/
PARAVIRT_OPS INTERFACE
S: Supported
PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
S: Maintained
S: Maintained
F: drivers/platform/x86/peaq-wmi.c
+ PECI HARDWARE MONITORING DRIVERS
+ S: Supported
+ F: Documentation/hwmon/peci-cputemp.rst
+ F: Documentation/hwmon/peci-dimmtemp.rst
+ F: drivers/hwmon/peci/
+
+ PECI SUBSYSTEM
+ S: Supported
+ F: Documentation/devicetree/bindings/peci/
+ F: Documentation/peci/
+ F: drivers/peci/
+ F: include/linux/peci-cpu.h
+ F: include/linux/peci.h
+
PENSANDO ETHERNET DRIVERS
- R: Jiri Olsa <jolsa@redhat.com>
+ R: Jiri Olsa <jolsa@kernel.org>
PIN CONTROLLER - SAMSUNG
S: Maintained
+ C: irc://irc.libera.chat/linux-exynos
Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pinctrl/samsung.git
- F: Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
+ F: Documentation/devicetree/bindings/pinctrl/samsung,pinctrl*yaml
F: drivers/pinctrl/samsung/
F: include/dt-bindings/pinctrl/samsung.h
S: Supported
F: drivers/pinctrl/pinctrl-thunderbay.c
+ PIN CONTROLLER - SUNPLUS / TIBBO
+ S: Maintained
+ W: https://sunplus.atlassian.net/wiki/spaces/doc/overview
+ F: Documentation/devicetree/bindings/pinctrl/sunplus,*
+ F: drivers/pinctrl/sunplus/
+ F: include/dt-bindings/pinctrl/sppctl*.h
+
PKTCDVD DRIVER
S: Orphan
PRESSURE STALL INFORMATION (PSI)
S: Maintained
F: include/linux/psi*
F: kernel/sched/psi.c
S: Maintained
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git sysctl-next
F: fs/proc/proc_sysctl.c
F: include/linux/sysctl.h
F: kernel/sysctl-test.c
F: include/asm-generic/syscall.h
F: include/linux/ptrace.h
F: include/linux/regset.h
- F: include/linux/tracehook.h
F: include/uapi/linux/ptrace.h
F: include/uapi/linux/ptrace.h
F: kernel/ptrace.c
F: sound/soc/codecs/wsa881x.c
F: sound/soc/qcom/
+ QCOM EMBEDDED USB DEBUGGER (EUD)
+ S: Maintained
+ F: Documentation/ABI/testing/sysfs-driver-eud
+ F: Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml
+ F: drivers/usb/misc/qcom_eud.c
+
QCOM IPA DRIVER
W: https://wireless.wiki.kernel.org/en/users/Drivers/ath10k
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
F: drivers/net/wireless/ath/ath10k/
+ F: Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
QUALCOMM ATHEROS ATH11K WIRELESS DRIVER
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
+ F: Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
F: drivers/net/wireless/ath/ath11k/
QUALCOMM ATHEROS ATH9K WIRELESS DRIVER
- S: Supported
+ S: Maintained
W: https://wireless.wiki.kernel.org/en/users/Drivers/ath9k
F: Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml
F: drivers/net/wireless/ath/ath9k/
S: Maintained
- F: Documentation/devicetree/bindings/power/avs/qcom,cpr.txt
+ F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml
F: drivers/soc/qcom/cpr.c
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
S: Maintained
- F: Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt
+ F: Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-nvmem.yaml
+ F: Documentation/devicetree/bindings/opp/opp-v2-kryo-cpu.yaml
F: drivers/cpufreq/qcom-cpufreq-nvmem.c
QUALCOMM CRYPTO DRIVERS
F: drivers/misc/fastrpc.c
F: include/uapi/misc/fastrpc.h
- QUALCOMM GENERIC INTERFACE I2C DRIVER
- S: Supported
- F: drivers/i2c/busses/i2c-qcom-geni.c
-
QUALCOMM HEXAGON ARCHITECTURE
- M: Brian Cain <bcain@codeaurora.org>
+ M: Brian Cain <bcain@quicinc.com>
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/bcain/linux.git
S: Supported
F: arch/hexagon/
F: drivers/mtd/nand/raw/qcom_nandc.c
QUALCOMM RMNET DRIVER
S: Maintained
F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
F: drivers/media/platform/qcom/venus/
QUALCOMM WCN36XX WIRELESS DRIVER
- M: Kalle Valo <kvalo@kernel.org>
+ M: Loic Poulain <loic.poulain@linaro.org>
S: Supported
W: https://wireless.wiki.kernel.org/en/users/Drivers/wcn36xx
- T: git git://github.com/KrasnikovEugene/wcn36xx.git
F: drivers/net/wireless/ath/wcn36xx/
QUANTENNA QTNFMAC WIRELESS DRIVER
S: Maintained
F: arch/mips/ralink
+ RALINK MT7621 MIPS ARCHITECTURE
+ S: Maintained
+ F: arch/mips/boot/dts/ralink/mt7621*
+
RALINK RT2X00 WIRELESS LAN DRIVER
T: git https://git.kernel.org/pub/scm/linux/kernel/git/crng/random.git
S: Maintained
F: drivers/char/random.c
+ F: drivers/virt/vmgenid.c
RAPIDIO SUBSYSTEM
READ-COPY UPDATE (RCU)
REALTEK RTL83xx SMI DSA ROUTER CHIPS
S: Maintained
- F: Documentation/devicetree/bindings/net/dsa/realtek-smi.txt
- F: drivers/net/dsa/realtek-smi*
- F: drivers/net/dsa/rtl83*
+ F: Documentation/devicetree/bindings/net/dsa/realtek.yaml
+ F: drivers/net/dsa/realtek/*
REALTEK WIRELESS DRIVER (rtlwifi family)
F: drivers/i2c/busses/i2c-rcar.c
F: drivers/i2c/busses/i2c-sh_mobile.c
+ RENESAS R-CAR SATA DRIVER
+ S: Supported
+ F: Documentation/devicetree/bindings/ata/renesas,rcar-sata.yaml
+ F: drivers/ata/sata_rcar.c
+
RENESAS R-CAR THERMAL DRIVERS
F: drivers/mtd/nand/raw/r852.c
F: drivers/mtd/nand/raw/r852.h
+ RISC-V PMU DRIVERS
+ S: Supported
+ F: drivers/perf/riscv_pmu.c
+ F: drivers/perf/riscv_pmu_legacy.c
+ F: drivers/perf/riscv_pmu_sbi.c
+
RISC-V ARCHITECTURE
RISC-V/MICROCHIP POLARFIRE SOC SUPPORT
S: Supported
+ F: arch/riscv/boot/dts/microchip/
F: drivers/mailbox/mailbox-mpfs.c
F: drivers/soc/microchip/
F: include/soc/microchip/mpfs.h
F: sound/soc/rockchip/rockchip_i2s_tdm.*
ROCKCHIP ISP V1 DRIVER
S: Maintained
S: Supported
- F: Documentation/devicetree/bindings/mfd/bd9571mwv.txt
+ F: Documentation/devicetree/bindings/mfd/rohm,bd9571mwv.yaml
F: drivers/gpio/gpio-bd9571mwv.c
F: drivers/mfd/bd9571mwv.c
F: drivers/regulator/bd9571mwv-regulator.c
S390
- M: Christian Borntraeger <borntraeger@linux.ibm.com>
- R: Alexander Gordeev <agordeev@linux.ibm.com>
+ M: Alexander Gordeev <agordeev@linux.ibm.com>
+ R: Christian Borntraeger <borntraeger@linux.ibm.com>
S: Supported
S: Supported
W: http://www.ibm.com/developerworks/linux/linux390/
F: Documentation/s390/vfio-ap.rst
- F: drivers/s390/crypto/vfio_ap_drv.c
- F: drivers/s390/crypto/vfio_ap_ops.c
- F: drivers/s390/crypto/vfio_ap_private.h
+ F: drivers/s390/crypto/vfio_ap*
S390 VFIO-CCW DRIVER
F: drivers/s390/scsi/zfcp_*
S3C ADC BATTERY DRIVER
S: Odd Fixes
F: drivers/power/supply/s3c_adc_battery.c
F: security/safesetid/
SAMSUNG AUDIO (ASoC) DRIVERS
S: Supported
F: sound/soc/samsung/
SAMSUNG EXYNOS PSEUDO RANDOM NUMBER GENERATOR (RNG) DRIVER
S: Maintained
F: drivers/platform/x86/samsung-laptop.c
SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS
S: Maintained
- F: drivers/media/platform/s3c-camif/
+ F: drivers/media/platform/samsung/s3c-camif/
F: include/media/drv-intf/s3c_camif.h
SAMSUNG S3FWRN5 NFC DRIVER
S: Maintained
F: drivers/media/i2c/s5k5baf.c
SAMSUNG S5P Security SubSystem (SSS) DRIVER
S: Supported
Q: https://patchwork.linuxtv.org/project/linux-media/list/
- F: drivers/media/platform/exynos4-is/
+ F: drivers/media/platform/samsung/exynos4-is/
SAMSUNG SOC CLOCK DRIVERS
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/snawrocki/clk.git
F: include/linux/platform_data/clk-s3c2410.h
SAMSUNG SPI DRIVERS
S: Maintained
- F: Documentation/devicetree/bindings/spi/spi-samsung.txt
+ F: Documentation/devicetree/bindings/spi/samsung,spi*.yaml
F: drivers/spi/spi-s3c*
F: include/linux/platform_data/spi-s3c64xx.h
F: include/linux/spi/s3c24xx-fiq.h
F: drivers/net/ethernet/samsung/sxgbe/
SAMSUNG THERMAL DRIVER
- S: Supported
- T: git https://github.com/lmajewski/linux-samsung-thermal.git
+ S: Maintained
+ F: Documentation/devicetree/bindings/thermal/samsung,exynos-thermal.yaml
F: drivers/thermal/samsung/
SAMSUNG USB2 PHY DRIVER
S: Supported
- F: Documentation/devicetree/bindings/phy/samsung-phy.txt
+ F: Documentation/devicetree/bindings/phy/samsung,usb2-phy.yaml
F: Documentation/driver-api/phy/samsung-usb2.rst
F: drivers/phy/samsung/phy-exynos4210-usb2.c
F: drivers/phy/samsung/phy-exynos4x12-usb2.c
SH_VOU V4L2 OUTPUT DRIVER
S: Orphan
- F: drivers/media/platform/sh_vou.c
+ F: drivers/media/platform/renesas/sh_vou.c
F: include/media/drv-intf/sh_vou.h
SI2157 MEDIA DRIVER
SILICON LABS WIRELESS DRIVERS (for WFxxx series)
S: Supported
+ F: Documentation/devicetree/bindings/staging/net/wireless/silabs,wfx.yaml
F: drivers/staging/wfx/
SILICON MOTION SM712 FRAME BUFFER DRIVER
W: http://www.winischhofer.at/linuxsisusbvga.shtml
F: drivers/usb/misc/sisusbvga/
+ SL28 CPLD MFD DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml
+ F: Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml
+ F: Documentation/devicetree/bindings/interrupt-controller/kontron,sl28cpld-intc.yaml
+ F: Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
+ F: Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml
+ F: Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml
+ F: drivers/gpio/gpio-sl28cpld.c
+ F: drivers/hwmon/sl28cpld-hwmon.c
+ F: drivers/irqchip/irq-sl28cpld.c
+ F: drivers/pwm/pwm-sl28cpld.c
+ F: drivers/watchdog/sl28cpld_wdt.c
+
SLAB ALLOCATOR
S: Maintained
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git
F: include/linux/sl?b*.h
F: mm/sl?b*
F: drivers/iio/imu/st_lsm6dsx/
ST MIPID02 CSI-2 TO PARALLEL BRIDGE DRIVER
S: Maintained
T: git git://linuxtv.org/media_tree.git
S: Odd Fixes
F: drivers/net/ethernet/adaptec/starfire*
- STARFIVE JH7100 CLOCK DRIVER
+ STARFIVE JH7100 CLOCK DRIVERS
S: Maintained
- F: Documentation/devicetree/bindings/clock/starfive,jh7100-clkgen.yaml
- F: drivers/clk/starfive/clk-starfive-jh7100.c
- F: include/dt-bindings/clock/starfive-jh7100.h
+ F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml
+ F: drivers/clk/starfive/clk-starfive-jh7100*
+ F: include/dt-bindings/clock/starfive-jh7100*.h
STARFIVE JH7100 PINCTRL DRIVER
F: sound/soc/sti/
STI CEC DRIVER
S: Maintained
F: Documentation/devicetree/bindings/media/stih-cec.txt
F: drivers/media/cec/platform/sti/
S: Maintained
F: drivers/net/ethernet/dlink/sundance.c
+ SUNPLUS OCOTP DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml
+ F: drivers/nvmem/sunplus-ocotp.c
+
SUNPLUS RTC DRIVER
F: Documentation/devicetree/bindings/rtc/sunplus,sp7021-rtc.yaml
F: drivers/rtc/rtc-sunplus.c
+ SUNPLUS SPI CONTROLLER INTERFACE DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/spi/spi-sunplus-sp7021.yaml
+ F: drivers/spi/spi-sunplus-sp7021.c
+
+ SUNPLUS UART DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml
+ F: drivers/tty/serial/sunplus-uart.c
+
SUPERH
S: Maintained
F: drivers/i2c/busses/i2c-designware-*
S: Maintained
+ F: Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
F: sound/soc/ti/
TEXAS INSTRUMENTS' DAC7612 DAC DRIVER
W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
- F: drivers/media/platform/am437x/
+ F: drivers/media/platform/ti/am437x/
TI BANDGAP AND THERMAL DRIVER
W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
- F: drivers/media/platform/davinci/
+ F: drivers/media/platform/ti/davinci/
F: include/media/davinci/
TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER
Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: Documentation/devicetree/bindings/media/ti,cal.yaml
F: Documentation/devicetree/bindings/media/ti,vpe.yaml
- F: drivers/media/platform/ti-vpe/
+ F: drivers/media/platform/ti/cal/
+ F: drivers/media/platform/ti/vpe/
TI WILINK WIRELESS DRIVERS
F: Documentation/hwmon/tmp401.rst
F: drivers/hwmon/tmp401.c
+ TMP464 HARDWARE MONITOR DRIVER
+ S: Maintained
+ F: Documentation/devicetree/bindings/hwmon/ti,tmp464.yaml
+ F: Documentation/hwmon/tmp464.rst
+ F: drivers/hwmon/tmp464.c
+
TMP513 HARDWARE MONITOR DRIVER
F: Documentation/trace/hwlat_detector.rst
F: arch/*/kernel/trace.c
+ Real-time Linux Analysis (RTLA) tools
+ S: Maintained
+ F: Documentation/tools/rtla/
+ F: tools/tracing/rtla/
+
TRADITIONAL CHINESE DOCUMENTATION
T: git git://linuxtv.org/media_tree.git
F: drivers/media/pci/tw686x/
+ U-BOOT ENVIRONMENT VARIABLES
+ S: Maintained
+ F: Documentation/devicetree/bindings/nvmem/u-boot,env.yaml
+
UACCE ACCELERATOR FRAMEWORK
S: Supported
+ F: Documentation/devicetree/bindings/ufs/
F: Documentation/scsi/ufs.rst
F: drivers/scsi/ufs/
F: drivers/media/usb/zr364xx/
USER-MODE LINUX (UML)
S: Maintained
W: http://user-mode-linux.sourceforge.net
Q: https://patchwork.ozlabs.org/project/linux-um/list/
- T: git git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml.git
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/uml/linux.git next
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/uml/linux.git fixes
F: Documentation/virt/uml/
F: arch/um/
F: arch/x86/um/
S: Maintained
F: drivers/vfio/fsl-mc/
+ VFIO HISILICON PCI DRIVER
+ S: Maintained
+ F: drivers/vfio/pci/hisilicon/
+
VFIO MEDIATED DEVICE DRIVERS
F: include/linux/mdev.h
F: samples/vfio-mdev/
+ VFIO PCI DEVICE SPECIFIC DRIVERS
+ S: Maintained
+ P: Documentation/driver-api/vfio-pci-device-specific-driver-acceptance.rst
+ F: drivers/vfio/pci/*/
+
VFIO PLATFORM DRIVER
S: Maintained
F: drivers/vfio/platform/
+ VFIO MLX5 PCI DRIVER
+ S: Maintained
+ F: drivers/vfio/pci/mlx5/
+
VGA_SWITCHEROO
S: Maintained
F: include/media/videobuf2-*
VIMC VIRTUAL MEDIA CONTROLLER DRIVER
S: Maintained
W: https://linuxtv.org
VMWARE BALLOON DRIVER
S: Maintained
F: drivers/misc/vmw_balloon.c
VMWARE HYPERVISOR INTERFACE
S: Supported
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/vmware
F: arch/x86/include/asm/vmware.h
F: arch/x86/kernel/cpu/vmware.c
VMWARE PVRDMA DRIVER
S: Maintained
F: drivers/infiniband/hw/vmw_pvrdma/
VMware PVSCSI driver
S: Maintained
F: drivers/scsi/vmw_pvscsi.c
VMWARE VIRTUAL PTP CLOCK DRIVER
S: Supported
F: drivers/ptp/ptp_vmw.c
VMWARE VMCI DRIVER
S: Maintained
F: drivers/misc/vmw_vmci/
VMWARE VMMOUSE SUBDRIVER
S: Maintained
F: drivers/input/mouse/vmmouse.c
VMWARE VMXNET3 ETHERNET DRIVER
S: Maintained
F: drivers/net/vmxnet3/
S: Maintained
W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
- F: drivers/media/tuners/tuner-xc2028.*
+ F: drivers/media/tuners/xc2028.*
XDP (eXpress Data Path)
F: Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml
F: drivers/phy/xilinx/phy-zynqmp.c
+ XILINX ZYNQMP SHA3 DRIVER
+ S: Maintained
+ F: drivers/crypto/xilinx/zynqmp-sha.c
+
XILINX EVENT MANAGEMENT DRIVER
S: Maintained
S: Supported
W: http://www.marvell.com
- F: Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt
F: drivers/i2c/busses/i2c-xlp9xx.c
XRA1403 GPIO EXPANDER
S: Buried alive in reporters
- Q: http://patchwork.kernel.org/project/LKML/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
F: *
F: */
select ARCH_ENABLE_THP_MIGRATION if X86_64 && TRANSPARENT_HUGEPAGE
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
select ARCH_HAS_CACHE_LINE_SIZE
+ select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL
select ARCH_HAS_DEBUG_VM_PGTABLE if !X86_PAE
select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_WANT_DEFAULT_BPF_JIT if X86_64
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
select ARCH_WANTS_NO_INSTR
+ select ARCH_WANT_GENERAL_HUGETLB
select ARCH_WANT_HUGE_PMD_SHARE
select ARCH_WANT_LD_ORPHAN_WARN
select ARCH_WANTS_THP_SWAP if X86_64
select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_HUGE_VMAP if X86_64 || X86_PAE
+ select HAVE_ARCH_HUGE_VMALLOC if X86_64
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_JUMP_LABEL_RELATIVE
select HAVE_ARCH_KASAN if X86_64
select HAVE_KPROBES_ON_FTRACE
select HAVE_FUNCTION_ERROR_INJECTION
select HAVE_KRETPROBES
+ select HAVE_RETHOOK
select HAVE_KVM
select HAVE_LIVEPATCH if X86_64
select HAVE_MIXED_BREAKPOINTS_REGS
select HAVE_STACK_VALIDATION if X86_64
select HAVE_STATIC_CALL
select HAVE_STATIC_CALL_INLINE if HAVE_STACK_VALIDATION
- select HAVE_PREEMPT_DYNAMIC
+ select HAVE_PREEMPT_DYNAMIC_CALL
select HAVE_RSEQ
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_UNSTABLE_SCHED_CLOCK
config ARCH_HAS_CPU_RELAX
def_bool y
- config ARCH_HAS_FILTER_PGPROT
- def_bool y
-
config ARCH_HIBERNATION_POSSIBLE
def_bool y
config ARCH_SUSPEND_POSSIBLE
def_bool y
- config ARCH_WANT_GENERAL_HUGETLB
- def_bool y
-
config AUDIT_ARCH
def_bool y if X86_64
If unsure, say Y.
+config BOOT_VESA_SUPPORT
+ bool
+ help
+ If true, at least one selected framebuffer driver can take advantage
+ of VESA video modes set at an early boot stage via the vga= parameter.
+
config MAXSMP
bool "Enable Maximum number of SMP Processors and NUMA Nodes"
depends on X86_64 && SMP && DEBUG_KERNEL
Say Y if you intend to run this kernel on a Toshiba portable.
Say N otherwise.
- config I8K
- tristate "Dell i8k legacy laptop support"
- depends on HWMON
- depends on PROC_FS
- select SENSORS_DELL_SMM
- help
- This option enables legacy /proc/i8k userspace interface in hwmon
- dell-smm-hwmon driver. Character file /proc/i8k reports bios version,
- temperature and allows controlling fan speeds of Dell laptops via
- System Management Mode. For old Dell laptops (like Dell Inspiron 8000)
- it reports also power and hotkey status. For fan speed control is
- needed userspace package i8kutils.
-
- Say Y if you intend to run this kernel on old Dell laptops or want to
- use userspace package i8kutils.
- Say N otherwise.
-
config X86_REBOOTFIXUPS
bool "Enable X86 board specific fixups for reboot"
depends on X86_32
config ARCH_SELECT_MEMORY_MODEL
def_bool y
- depends on ARCH_SPARSEMEM_ENABLE
+ depends on ARCH_SPARSEMEM_ENABLE && ARCH_FLATMEM_ENABLE
config ARCH_MEMORY_PROBE
bool "Enable sysfs memory/probe interface"
specific cases in protected and virtual-8086 modes. Emulated
results are dummy.
+ config CC_HAS_IBT
+ # GCC >= 9 and binutils >= 2.29
+ # Retpoline check to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93654
+ # Clang/LLVM >= 14
+ # https://github.com/llvm/llvm-project/commit/e0b89df2e0f0130881bf6c39bf31d7f6aac00e0f
+ # https://github.com/llvm/llvm-project/commit/dfcf69770bc522b9e411c66454934a37c1f35332
+ def_bool ((CC_IS_GCC && $(cc-option, -fcf-protection=branch -mindirect-branch-register)) || \
+ (CC_IS_CLANG && CLANG_VERSION >= 140000)) && \
+ $(as-instr,endbr64)
+
+ config X86_KERNEL_IBT
+ prompt "Indirect Branch Tracking"
+ bool
+ depends on X86_64 && CC_HAS_IBT && STACK_VALIDATION
+ # https://github.com/llvm/llvm-project/commit/9d7001eba9c4cb311e03cd8cdc231f9e579f2d0f
+ depends on !LD_IS_LLD || LLD_VERSION >= 140000
+ help
+ Build the kernel with support for Indirect Branch Tracking, a
+ hardware support course-grain forward-edge Control Flow Integrity
+ protection. It enforces that all indirect calls must land on
+ an ENDBR instruction, as such, the compiler will instrument the
+ code with them to make this happen.
+
+ In addition to building the kernel with IBT, seal all functions that
+ are not indirect call targets, avoiding them ever becomming one.
+
+ This requires LTO like objtool runs and will slow down the build. It
+ does significantly reduce the number of ENDBR instructions in the
+ kernel image.
+
config X86_INTEL_MEMORY_PROTECTION_KEYS
prompt "Memory Protection Keys"
def_bool y
help
Support old a.out binaries in the 32bit emulation.
- config X86_X32
+ config X86_X32_ABI
bool "x32 ABI for 64-bit mode"
depends on X86_64
+ # llvm-objcopy does not convert x86_64 .note.gnu.property or
+ # compressed debug sections to x86_x32 properly:
+ # https://github.com/ClangBuiltLinux/linux/issues/514
+ # https://github.com/ClangBuiltLinux/linux/issues/1141
+ depends on $(success,$(OBJCOPY) --version | head -n1 | grep -qv llvm)
help
Include code to run binaries for the x32 native 32-bit ABI
for 64-bit processors. An x32 process gets access to the
full 64-bit register file and wide data path while leaving
pointers at 32 bits for smaller memory footprint.
- You will need a recent binutils (2.22 or later) with
- elf32_x86_64 support enabled to compile a kernel with this
- option set.
-
config COMPAT_32
def_bool y
depends on IA32_EMULATION || X86_32
config COMPAT
def_bool y
- depends on IA32_EMULATION || X86_X32
+ depends on IA32_EMULATION || X86_X32_ABI
if COMPAT
config COMPAT_FOR_U64_ALIGNMENT
* as a file descriptor by calling dma_buf_fd().
*
* 2. Userspace passes this file-descriptors to all drivers it wants this buffer
- * to share with: First the filedescriptor is converted to a &dma_buf using
+ * to share with: First the file descriptor is converted to a &dma_buf using
* dma_buf_get(). Then the buffer is attached to the device using
* dma_buf_attach().
*
*
* Interfaces::
*
- * void \*dma_buf_vmap(struct dma_buf \*dmabuf, struct dma_buf_map \*map)
- * void dma_buf_vunmap(struct dma_buf \*dmabuf, struct dma_buf_map \*map)
+ * void \*dma_buf_vmap(struct dma_buf \*dmabuf, struct iosys_map \*map)
+ * void dma_buf_vunmap(struct dma_buf \*dmabuf, struct iosys_map \*map)
*
* The vmap call can fail if there is no vmap support in the exporter, or if
* it runs out of vmalloc space. Note that the dma-buf layer keeps a reference
*
* Returns 0 on success, or a negative errno code otherwise.
*/
- int dma_buf_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map)
{
- struct dma_buf_map ptr;
+ struct iosys_map ptr;
int ret = 0;
- dma_buf_map_clear(map);
+ iosys_map_clear(map);
if (WARN_ON(!dmabuf))
return -EINVAL;
mutex_lock(&dmabuf->lock);
if (dmabuf->vmapping_counter) {
dmabuf->vmapping_counter++;
- BUG_ON(dma_buf_map_is_null(&dmabuf->vmap_ptr));
+ BUG_ON(iosys_map_is_null(&dmabuf->vmap_ptr));
*map = dmabuf->vmap_ptr;
goto out_unlock;
}
- BUG_ON(dma_buf_map_is_set(&dmabuf->vmap_ptr));
+ BUG_ON(iosys_map_is_set(&dmabuf->vmap_ptr));
ret = dmabuf->ops->vmap(dmabuf, &ptr);
if (WARN_ON_ONCE(ret))
* @dmabuf: [in] buffer to vunmap
* @map: [in] vmap pointer to vunmap
*/
- void dma_buf_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
+ void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)
{
if (WARN_ON(!dmabuf))
return;
- BUG_ON(dma_buf_map_is_null(&dmabuf->vmap_ptr));
+ BUG_ON(iosys_map_is_null(&dmabuf->vmap_ptr));
BUG_ON(dmabuf->vmapping_counter == 0);
- BUG_ON(!dma_buf_map_is_equal(&dmabuf->vmap_ptr, map));
+ BUG_ON(!iosys_map_is_equal(&dmabuf->vmap_ptr, map));
mutex_lock(&dmabuf->lock);
if (--dmabuf->vmapping_counter == 0) {
if (dmabuf->ops->vunmap)
dmabuf->ops->vunmap(dmabuf, map);
- dma_buf_map_clear(&dmabuf->vmap_ptr);
+ iosys_map_clear(&dmabuf->vmap_ptr);
}
mutex_unlock(&dmabuf->lock);
}
config ARM_SDE_INTERFACE
bool "ARM Software Delegated Exception Interface (SDEI)"
depends on ARM64
+ depends on ACPI_APEI_GHES
help
The Software Delegated Exception Interface (SDEI) is an ARM
standard for registering callbacks from the platform firmware
config SYSFB
bool
- default y
- depends on X86 || EFI
+ select BOOT_VESA_SUPPORT
config SYSFB_SIMPLEFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
- depends on SYSFB
+ depends on X86 || EFI
+ select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
}
/**
- * @amdgpu_amdkfd_reserve_mem_limit() - Decrease available memory by size
+ * amdgpu_amdkfd_reserve_mem_limit() - Decrease available memory by size
* of buffer including any reserved for control structures
*
* @adev: Device to which allocated BO belongs to
static int amdgpu_amdkfd_remove_eviction_fence(struct amdgpu_bo *bo,
struct amdgpu_amdkfd_fence *ef)
{
- struct dma_resv *resv = bo->tbo.base.resv;
- struct dma_resv_list *old, *new;
- unsigned int i, j, k;
+ struct dma_fence *replacement;
if (!ef)
return -EINVAL;
- old = dma_resv_shared_list(resv);
- if (!old)
- return 0;
-
- new = kmalloc(struct_size(new, shared, old->shared_max), GFP_KERNEL);
- if (!new)
- return -ENOMEM;
-
- /* Go through all the shared fences in the resevation object and sort
- * the interesting ones to the end of the list.
+ /* TODO: Instead of block before we should use the fence of the page
+ * table update and TLB flush here directly.
*/
- for (i = 0, j = old->shared_count, k = 0; i < old->shared_count; ++i) {
- struct dma_fence *f;
-
- f = rcu_dereference_protected(old->shared[i],
- dma_resv_held(resv));
-
- if (f->context == ef->base.context)
- RCU_INIT_POINTER(new->shared[--j], f);
- else
- RCU_INIT_POINTER(new->shared[k++], f);
- }
- new->shared_max = old->shared_max;
- new->shared_count = k;
-
- /* Install the new fence list, seqcount provides the barriers */
- write_seqcount_begin(&resv->seq);
- RCU_INIT_POINTER(resv->fence, new);
- write_seqcount_end(&resv->seq);
-
- /* Drop the references to the removed fences or move them to ef_list */
- for (i = j; i < old->shared_count; ++i) {
- struct dma_fence *f;
-
- f = rcu_dereference_protected(new->shared[i],
- dma_resv_held(resv));
- dma_fence_put(f);
- }
- kfree_rcu(old, rcu);
-
+ replacement = dma_fence_get_stub();
+ dma_resv_replace_fences(bo->tbo.base.resv, ef->base.context,
+ replacement);
+ dma_fence_put(replacement);
return 0;
}
continue;
if (attachment[i]->bo_va) {
amdgpu_bo_reserve(bo[i], true);
- amdgpu_vm_bo_rmv(adev, attachment[i]->bo_va);
+ amdgpu_vm_bo_del(adev, attachment[i]->bo_va);
amdgpu_bo_unreserve(bo[i]);
list_del(&attachment[i]->list);
}
pr_debug("\t remove VA 0x%llx in entry %p\n",
attachment->va, attachment);
- amdgpu_vm_bo_rmv(attachment->adev, attachment->bo_va);
+ amdgpu_vm_bo_del(attachment->adev, attachment->bo_va);
drm_gem_object_put(&bo->tbo.base);
list_del(&attachment->list);
kfree(attachment);
*
* Returns 0 for success, negative errno for errors.
*/
- static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr)
+ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr,
+ bool criu_resume)
{
struct amdkfd_process_info *process_info = mem->process_info;
struct amdgpu_bo *bo = mem->bo;
goto out;
}
+ if (criu_resume) {
+ /*
+ * During a CRIU restore operation, the userptr buffer objects
+ * will be validated in the restore_userptr_work worker at a
+ * later stage when it is scheduled by another ioctl called by
+ * CRIU master process for the target pid for restore.
+ */
+ atomic_inc(&mem->invalid);
+ mutex_unlock(&process_info->lock);
+ return 0;
+ }
+
ret = amdgpu_ttm_tt_get_user_pages(bo, bo->tbo.ttm->pages);
if (ret) {
pr_err("%s: Failed to get user pages: %d\n", __func__, ret);
return avm->pd_phys_addr;
}
+ void amdgpu_amdkfd_block_mmu_notifications(void *p)
+ {
+ struct amdkfd_process_info *pinfo = (struct amdkfd_process_info *)p;
+
+ mutex_lock(&pinfo->lock);
+ WRITE_ONCE(pinfo->block_mmu_notifications, true);
+ mutex_unlock(&pinfo->lock);
+ }
+
+ int amdgpu_amdkfd_criu_resume(void *p)
+ {
+ int ret = 0;
+ struct amdkfd_process_info *pinfo = (struct amdkfd_process_info *)p;
+
+ mutex_lock(&pinfo->lock);
+ pr_debug("scheduling work\n");
+ atomic_inc(&pinfo->evicted_bos);
+ if (!READ_ONCE(pinfo->block_mmu_notifications)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ WRITE_ONCE(pinfo->block_mmu_notifications, false);
+ schedule_delayed_work(&pinfo->restore_userptr_work, 0);
+
+ out_unlock:
+ mutex_unlock(&pinfo->lock);
+ return ret;
+ }
+
int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
struct amdgpu_device *adev, uint64_t va, uint64_t size,
void *drm_priv, struct kgd_mem **mem,
- uint64_t *offset, uint32_t flags)
+ uint64_t *offset, uint32_t flags, bool criu_resume)
{
struct amdgpu_vm *avm = drm_priv_to_vm(drm_priv);
enum ttm_bo_type bo_type = ttm_bo_type_device;
add_kgd_mem_to_kfd_bo_list(*mem, avm->process_info, user_addr);
if (user_addr) {
- ret = init_user_pages(*mem, user_addr);
+ pr_debug("creating userptr BO for user_addr = %llu\n", user_addr);
+ ret = init_user_pages(*mem, user_addr, criu_resume);
if (ret)
goto allocate_init_user_pages_failed;
} else if (flags & (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL |
true);
ret = unreserve_bo_and_vms(&ctx, false, false);
- /* Only apply no TLB flush on Aldebaran to
- * workaround regressions on other Asics.
- */
- if (table_freed && (adev->asic_type != CHIP_ALDEBARAN))
- *table_freed = true;
-
goto out;
out_unreserve:
int evicted_bos;
int r = 0;
+ /* Do not process MMU notifications until stage-4 IOCTL is received */
+ if (READ_ONCE(process_info->block_mmu_notifications))
+ return 0;
+
atomic_inc(&mem->invalid);
evicted_bos = atomic_inc_return(&process_info->evicted_bos);
if (evicted_bos == 1) {
return 0;
}
+
+ bool amdgpu_amdkfd_bo_mapped_to_dev(struct amdgpu_device *adev, struct kgd_mem *mem)
+ {
+ struct kfd_mem_attachment *entry;
+
+ list_for_each_entry(entry, &mem->attachments, list) {
+ if (entry->is_mapped && entry->adev == adev)
+ return true;
+ }
+ return false;
+ }
#include <drm/amdgpu_drm.h>
#include <drm/drm_syncobj.h>
+ #include "amdgpu_cs.h"
#include "amdgpu.h"
#include "amdgpu_trace.h"
#include "amdgpu_gmc.h"
goto free_chunk;
}
- mutex_lock(&p->ctx->lock);
-
/* skip guilty context job */
if (atomic_read(&p->ctx->guilty) == 1) {
ret = -ECANCELED;
if (free_vram >= 128 * 1024 * 1024 || free_vram >= total_vram / 8) {
s64 min_us;
- /* Be more aggresive on dGPUs. Try to fill a portion of free
+ /* Be more aggressive on dGPUs. Try to fill a portion of free
* VRAM now.
*/
if (!(adev->flags & AMD_IS_APU))
}
}
+ /* Move fence waiting after getting reservation lock of
+ * PD root. Then there is no need on a ctx mutex lock.
+ */
+ r = amdgpu_ctx_wait_prev_fence(p->ctx, p->entity);
+ if (unlikely(r != 0)) {
+ if (r != -ERESTARTSYS)
+ DRM_ERROR("amdgpu_ctx_wait_prev_fence failed.\n");
+ goto error_validate;
+ }
+
amdgpu_cs_get_threshold_for_moves(p->adev, &p->bytes_moved_threshold,
&p->bytes_moved_vis_threshold);
p->bytes_moved = 0;
dma_fence_put(parser->fence);
if (parser->ctx) {
- mutex_unlock(&parser->ctx->lock);
amdgpu_ctx_put(parser->ctx);
}
if (parser->bo_list)
memcpy(ib->ptr, kptr, chunk_ib->ib_bytes);
amdgpu_bo_kunmap(aobj);
- r = amdgpu_ring_parse_cs(ring, p, j);
+ r = amdgpu_ring_parse_cs(ring, p, p->job, ib);
if (r)
return r;
} else {
ib->ptr = (uint32_t *)kptr;
- r = amdgpu_ring_patch_cs_in_place(ring, p, j);
+ r = amdgpu_ring_patch_cs_in_place(ring, p, p->job, ib);
amdgpu_bo_kunmap(aobj);
if (r)
return r;
if (parser->job->uf_addr && ring->funcs->no_user_fence)
return -EINVAL;
- return amdgpu_ctx_wait_prev_fence(parser->ctx, parser->entity);
+ return 0;
}
static int amdgpu_cs_process_fence_dep(struct amdgpu_cs_parser *p,
amdgpu_bo_list_for_each_entry(e, p->bo_list) {
struct dma_resv *resv = e->tv.bo->base.resv;
struct dma_fence_chain *chain = e->chain;
+ struct dma_resv_iter cursor;
+ struct dma_fence *fence;
if (!chain)
continue;
/*
- * Work around dma_resv shortcomings by wrapping up the
- * submission in a dma_fence_chain and add it as exclusive
+ * Temporary workaround dma_resv shortcommings by wrapping up
+ * the submission in a dma_fence_chain and add it as exclusive
* fence.
+ *
+ * TODO: Remove together with dma_resv rework.
*/
- dma_fence_chain_init(chain, dma_resv_excl_fence(resv),
- dma_fence_get(p->fence), 1);
-
+ dma_resv_for_each_fence(&cursor, resv, false, fence) {
+ break;
+ }
+ dma_fence_chain_init(chain, fence, dma_fence_get(p->fence), 1);
rcu_assign_pointer(resv->fence_excl, &chain->base);
e->chain = NULL;
}
goto out;
r = amdgpu_cs_submit(&parser, cs);
-
out:
amdgpu_cs_parser_fini(&parser, r, reserved_buffers);
return 0;
default:
+ dma_fence_put(fence);
return -EINVAL;
}
}
#include "amdgpu.h"
-struct amdgpu_gtt_node {
- struct ttm_buffer_object *tbo;
- struct ttm_range_mgr_node base;
-};
-
static inline struct amdgpu_gtt_mgr *
to_gtt_mgr(struct ttm_resource_manager *man)
{
return container_of(man, struct amdgpu_gtt_mgr, manager);
}
-static inline struct amdgpu_gtt_node *
-to_amdgpu_gtt_node(struct ttm_resource *res)
-{
- return container_of(res, struct amdgpu_gtt_node, base.base);
-}
-
/**
* DOC: mem_info_gtt_total
*
*/
bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_resource *res)
{
- struct amdgpu_gtt_node *node = to_amdgpu_gtt_node(res);
+ struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);
- return drm_mm_node_allocated(&node->base.mm_nodes[0]);
+ return drm_mm_node_allocated(&node->mm_nodes[0]);
}
/**
{
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
uint32_t num_pages = PFN_UP(tbo->base.size);
- struct amdgpu_gtt_node *node;
+ struct ttm_range_mgr_node *node;
int r;
- node = kzalloc(struct_size(node, base.mm_nodes, 1), GFP_KERNEL);
+ node = kzalloc(struct_size(node, mm_nodes, 1), GFP_KERNEL);
if (!node)
return -ENOMEM;
- node->tbo = tbo;
- ttm_resource_init(tbo, place, &node->base.base);
+ ttm_resource_init(tbo, place, &node->base);
if (!(place->flags & TTM_PL_FLAG_TEMPORARY) &&
ttm_resource_manager_usage(man) > man->size) {
r = -ENOSPC;
if (place->lpfn) {
spin_lock(&mgr->lock);
- r = drm_mm_insert_node_in_range(&mgr->mm,
- &node->base.mm_nodes[0],
+ r = drm_mm_insert_node_in_range(&mgr->mm, &node->mm_nodes[0],
num_pages, tbo->page_alignment,
0, place->fpfn, place->lpfn,
DRM_MM_INSERT_BEST);
if (unlikely(r))
goto err_free;
- node->base.base.start = node->base.mm_nodes[0].start;
+ node->base.start = node->mm_nodes[0].start;
} else {
- node->base.mm_nodes[0].start = 0;
- node->base.mm_nodes[0].size = node->base.base.num_pages;
- node->base.base.start = AMDGPU_BO_INVALID_OFFSET;
+ node->mm_nodes[0].start = 0;
+ node->mm_nodes[0].size = node->base.num_pages;
+ node->base.start = AMDGPU_BO_INVALID_OFFSET;
}
- *res = &node->base.base;
+ *res = &node->base;
return 0;
err_free:
- ttm_resource_fini(man, &node->base.base);
+ ttm_resource_fini(man, &node->base);
kfree(node);
return r;
}
static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
- struct amdgpu_gtt_node *node = to_amdgpu_gtt_node(res);
+ struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);
struct amdgpu_gtt_mgr *mgr = to_gtt_mgr(man);
spin_lock(&mgr->lock);
- if (drm_mm_node_allocated(&node->base.mm_nodes[0]))
- drm_mm_remove_node(&node->base.mm_nodes[0]);
+ if (drm_mm_node_allocated(&node->mm_nodes[0]))
+ drm_mm_remove_node(&node->mm_nodes[0]);
spin_unlock(&mgr->lock);
ttm_resource_fini(man, res);
*
* Re-init the gart for each known BO in the GTT.
*/
- int amdgpu_gtt_mgr_recover(struct amdgpu_gtt_mgr *mgr)
+ void amdgpu_gtt_mgr_recover(struct amdgpu_gtt_mgr *mgr)
{
- struct amdgpu_gtt_node *node;
+ struct ttm_range_mgr_node *node;
struct drm_mm_node *mm_node;
struct amdgpu_device *adev;
- int r = 0;
adev = container_of(mgr, typeof(*adev), mman.gtt_mgr);
spin_lock(&mgr->lock);
drm_mm_for_each_node(mm_node, &mgr->mm) {
- node = container_of(mm_node, typeof(*node), base.mm_nodes[0]);
- amdgpu_ttm_recover_gart(node->tbo);
+ node = container_of(mm_node, typeof(*node), mm_nodes[0]);
- r = amdgpu_ttm_recover_gart(node->base.bo);
- if (r)
- break;
++ amdgpu_ttm_recover_gart(node->base.bo);
}
spin_unlock(&mgr->lock);
amdgpu_gart_invalidate_tlb(adev);
-
- return r;
}
/**
void amdgpu_pasid_free_delayed(struct dma_resv *resv,
u32 pasid)
{
- struct dma_fence *fence, **fences;
struct amdgpu_pasid_cb *cb;
- unsigned count;
+ struct dma_fence *fence;
int r;
- r = dma_resv_get_fences(resv, true, &count, &fences);
+ r = dma_resv_get_singleton(resv, true, &fence);
if (r)
goto fallback;
- if (count == 0) {
+ if (!fence) {
amdgpu_pasid_free(pasid);
return;
}
- if (count == 1) {
- fence = fences[0];
- kfree(fences);
- } else {
- uint64_t context = dma_fence_context_alloc(1);
- struct dma_fence_array *array;
-
- array = dma_fence_array_create(count, fences, context,
- 1, false);
- if (!array) {
- kfree(fences);
- goto fallback;
- }
- fence = &array->base;
- }
-
cb = kmalloc(sizeof(*cb), GFP_KERNEL);
if (!cb) {
/* Last resort when we are OOM */
unsigned i;
int r;
- if (ring->vmid_wait && !dma_fence_is_signaled(ring->vmid_wait))
+ if (!dma_fence_is_signaled(ring->vmid_wait))
return amdgpu_sync_fence(sync, ring->vmid_wait);
fences = kmalloc_array(id_mgr->num_ids, sizeof(void *), GFP_KERNEL);
#include <drm/ttm/ttm_range_manager.h>
#include <drm/amdgpu_drm.h>
+ #include <drm/drm_drv.h>
#include "amdgpu.h"
#include "amdgpu_object.h"
* @bo: buffer object to map
* @mem: memory object to map
* @mm_cur: range to map
- * @num_pages: number of pages to map
* @window: which GART window to use
* @ring: DMA ring to use for the copy
* @tmz: if we should setup a TMZ enabled mapping
+ * @size: in number of bytes to map, out number of bytes mapped
* @addr: resulting address inside the MC address space
*
* Setup one of the GART windows to access a specific piece of memory or return
static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
struct ttm_resource *mem,
struct amdgpu_res_cursor *mm_cur,
- unsigned num_pages, unsigned window,
- struct amdgpu_ring *ring, bool tmz,
- uint64_t *addr)
+ unsigned window, struct amdgpu_ring *ring,
+ bool tmz, uint64_t *size, uint64_t *addr)
{
struct amdgpu_device *adev = ring->adev;
- struct amdgpu_job *job;
- unsigned num_dw, num_bytes;
- struct dma_fence *fence;
+ unsigned offset, num_pages, num_dw, num_bytes;
uint64_t src_addr, dst_addr;
+ struct dma_fence *fence;
+ struct amdgpu_job *job;
void *cpu_addr;
uint64_t flags;
unsigned int i;
BUG_ON(adev->mman.buffer_funcs->copy_max_bytes <
AMDGPU_GTT_MAX_TRANSFER_SIZE * 8);
- BUG_ON(mem->mem_type == AMDGPU_PL_PREEMPT);
+
+ if (WARN_ON(mem->mem_type == AMDGPU_PL_PREEMPT))
+ return -EINVAL;
/* Map only what can't be accessed directly */
if (!tmz && mem->start != AMDGPU_BO_INVALID_OFFSET) {
return 0;
}
+
+ /*
+ * If start begins at an offset inside the page, then adjust the size
+ * and addr accordingly
+ */
+ offset = mm_cur->start & ~PAGE_MASK;
+
+ num_pages = PFN_UP(*size + offset);
+ num_pages = min_t(uint32_t, num_pages, AMDGPU_GTT_MAX_TRANSFER_SIZE);
+
+ *size = min(*size, (uint64_t)num_pages * PAGE_SIZE - offset);
+
*addr = adev->gmc.gart_start;
*addr += (u64)window * AMDGPU_GTT_MAX_TRANSFER_SIZE *
AMDGPU_GPU_PAGE_SIZE;
- *addr += mm_cur->start & ~PAGE_MASK;
+ *addr += offset;
num_dw = ALIGN(adev->mman.buffer_funcs->copy_num_dw, 8);
num_bytes = num_pages * 8 * AMDGPU_GPU_PAGES_IN_CPU_PAGE;
dma_addr_t *dma_addr;
dma_addr = &bo->ttm->dma_address[mm_cur->start >> PAGE_SHIFT];
- r = amdgpu_gart_map(adev, 0, num_pages, dma_addr, flags,
- cpu_addr);
- if (r)
- goto error_free;
+ amdgpu_gart_map(adev, 0, num_pages, dma_addr, flags, cpu_addr);
} else {
dma_addr_t dma_address;
dma_address += adev->vm_manager.vram_base_offset;
for (i = 0; i < num_pages; ++i) {
- r = amdgpu_gart_map(adev, i << PAGE_SHIFT, 1,
- &dma_address, flags, cpu_addr);
- if (r)
- goto error_free;
-
+ amdgpu_gart_map(adev, i << PAGE_SHIFT, 1, &dma_address,
+ flags, cpu_addr);
dma_address += PAGE_SIZE;
}
}
struct dma_resv *resv,
struct dma_fence **f)
{
- const uint32_t GTT_MAX_BYTES = (AMDGPU_GTT_MAX_TRANSFER_SIZE *
- AMDGPU_GPU_PAGE_SIZE);
-
struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
struct amdgpu_res_cursor src_mm, dst_mm;
struct dma_fence *fence = NULL;
mutex_lock(&adev->mman.gtt_window_lock);
while (src_mm.remaining) {
- uint32_t src_page_offset = src_mm.start & ~PAGE_MASK;
- uint32_t dst_page_offset = dst_mm.start & ~PAGE_MASK;
+ uint64_t from, to, cur_size;
struct dma_fence *next;
- uint32_t cur_size;
- uint64_t from, to;
- /* Copy size cannot exceed GTT_MAX_BYTES. So if src or dst
- * begins at an offset, then adjust the size accordingly
- */
- cur_size = max(src_page_offset, dst_page_offset);
- cur_size = min(min3(src_mm.size, dst_mm.size, size),
- (uint64_t)(GTT_MAX_BYTES - cur_size));
+ /* Never copy more than 256MiB at once to avoid a timeout */
+ cur_size = min3(src_mm.size, dst_mm.size, 256ULL << 20);
/* Map src to window 0 and dst to window 1. */
r = amdgpu_ttm_map_buffer(src->bo, src->mem, &src_mm,
- PFN_UP(cur_size + src_page_offset),
- 0, ring, tmz, &from);
+ 0, ring, tmz, &cur_size, &from);
if (r)
goto error;
r = amdgpu_ttm_map_buffer(dst->bo, dst->mem, &dst_mm,
- PFN_UP(cur_size + dst_page_offset),
- 1, ring, tmz, &to);
+ 1, ring, tmz, &cur_size, &to);
if (r)
goto error;
(abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE)) {
struct dma_fence *wipe_fence = NULL;
- r = amdgpu_fill_buffer(ttm_to_amdgpu_bo(bo), AMDGPU_POISON,
- NULL, &wipe_fence);
+ r = amdgpu_fill_buffer(abo, AMDGPU_POISON, NULL, &wipe_fence);
if (r) {
goto error;
} else if (wipe_fence) {
#endif
}
- static int amdgpu_ttm_gart_bind(struct amdgpu_device *adev,
- struct ttm_buffer_object *tbo,
- uint64_t flags)
+ static void amdgpu_ttm_gart_bind(struct amdgpu_device *adev,
+ struct ttm_buffer_object *tbo,
+ uint64_t flags)
{
struct amdgpu_bo *abo = ttm_to_amdgpu_bo(tbo);
struct ttm_tt *ttm = tbo->ttm;
struct amdgpu_ttm_tt *gtt = (void *)ttm;
- int r;
if (amdgpu_bo_encrypted(abo))
flags |= AMDGPU_PTE_TMZ;
if (abo->flags & AMDGPU_GEM_CREATE_CP_MQD_GFX9) {
uint64_t page_idx = 1;
- r = amdgpu_gart_bind(adev, gtt->offset, page_idx,
- gtt->ttm.dma_address, flags);
- if (r)
- goto gart_bind_fail;
+ amdgpu_gart_bind(adev, gtt->offset, page_idx,
+ gtt->ttm.dma_address, flags);
/* The memory type of the first page defaults to UC. Now
* modify the memory type to NC from the second page of
flags &= ~AMDGPU_PTE_MTYPE_VG10_MASK;
flags |= AMDGPU_PTE_MTYPE_VG10(AMDGPU_MTYPE_NC);
- r = amdgpu_gart_bind(adev,
- gtt->offset + (page_idx << PAGE_SHIFT),
- ttm->num_pages - page_idx,
- &(gtt->ttm.dma_address[page_idx]), flags);
+ amdgpu_gart_bind(adev, gtt->offset + (page_idx << PAGE_SHIFT),
+ ttm->num_pages - page_idx,
+ &(gtt->ttm.dma_address[page_idx]), flags);
} else {
- r = amdgpu_gart_bind(adev, gtt->offset, ttm->num_pages,
- gtt->ttm.dma_address, flags);
+ amdgpu_gart_bind(adev, gtt->offset, ttm->num_pages,
+ gtt->ttm.dma_address, flags);
}
-
- gart_bind_fail:
- if (r)
- DRM_ERROR("failed to bind %u pages at 0x%08llX\n",
- ttm->num_pages, gtt->offset);
-
- return r;
}
/*
struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void*)ttm;
uint64_t flags;
- int r = 0;
+ int r;
if (!bo_mem)
return -EINVAL;
/* bind pages into GART page tables */
gtt->offset = (u64)bo_mem->start << PAGE_SHIFT;
- r = amdgpu_gart_bind(adev, gtt->offset, ttm->num_pages,
- gtt->ttm.dma_address, flags);
-
- if (r)
- DRM_ERROR("failed to bind %u pages at 0x%08llX\n",
- ttm->num_pages, gtt->offset);
+ amdgpu_gart_bind(adev, gtt->offset, ttm->num_pages,
+ gtt->ttm.dma_address, flags);
gtt->bound = true;
- return r;
+ return 0;
}
/*
/* Bind pages */
gtt->offset = (u64)tmp->start << PAGE_SHIFT;
- r = amdgpu_ttm_gart_bind(adev, bo, flags);
- if (unlikely(r)) {
- ttm_resource_free(bo, &tmp);
- return r;
- }
-
+ amdgpu_ttm_gart_bind(adev, bo, flags);
amdgpu_gart_invalidate_tlb(adev);
ttm_resource_free(bo, &bo->resource);
ttm_bo_assign_mem(bo, tmp);
* Called by amdgpu_gtt_mgr_recover() from amdgpu_device_reset() to
* rebind GTT pages during a GPU reset.
*/
- int amdgpu_ttm_recover_gart(struct ttm_buffer_object *tbo)
+ void amdgpu_ttm_recover_gart(struct ttm_buffer_object *tbo)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(tbo->bdev);
uint64_t flags;
- int r;
if (!tbo->ttm)
- return 0;
+ return;
flags = amdgpu_ttm_tt_pte_flags(adev, tbo->ttm, tbo->resource);
- r = amdgpu_ttm_gart_bind(adev, tbo, flags);
-
- return r;
+ amdgpu_ttm_gart_bind(adev, tbo, flags);
}
/*
{
struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void *)ttm;
- int r;
/* if the pages have userptr pinning then clear that first */
if (gtt->userptr) {
return;
/* unbind shouldn't be done for GDS/GWS/OA in ttm_bo_clean_mm */
- r = amdgpu_gart_unbind(adev, gtt->offset, ttm->num_pages);
- if (r)
- DRM_ERROR("failed to unbind %u pages at 0x%08llX\n",
- gtt->ttm.num_pages, gtt->offset);
+ amdgpu_gart_unbind(adev, gtt->offset, ttm->num_pages);
gtt->bound = false;
}
return ttm_pool_free(&adev->mman.bdev.pool, ttm);
}
+ /**
+ * amdgpu_ttm_tt_get_userptr - Return the userptr GTT ttm_tt for the current
+ * task
+ *
+ * @tbo: The ttm_buffer_object that contains the userptr
+ * @user_addr: The returned value
+ */
+ int amdgpu_ttm_tt_get_userptr(const struct ttm_buffer_object *tbo,
+ uint64_t *user_addr)
+ {
+ struct amdgpu_ttm_tt *gtt;
+
+ if (!tbo->ttm)
+ return -EINVAL;
+
+ gtt = (void *)tbo->ttm;
+ *user_addr = gtt->userptr;
+ return 0;
+ }
+
/**
* amdgpu_ttm_tt_set_userptr - Initialize userptr GTT ttm_tt for the current
* task
}
}
+ static int amdgpu_ttm_access_memory_sdma(struct ttm_buffer_object *bo,
+ unsigned long offset, void *buf, int len, int write)
+ {
+ struct amdgpu_bo *abo = ttm_to_amdgpu_bo(bo);
+ struct amdgpu_device *adev = amdgpu_ttm_adev(abo->tbo.bdev);
+ struct amdgpu_res_cursor src_mm;
+ struct amdgpu_job *job;
+ struct dma_fence *fence;
+ uint64_t src_addr, dst_addr;
+ unsigned int num_dw;
+ int r, idx;
+
+ if (len != PAGE_SIZE)
+ return -EINVAL;
+
+ if (!adev->mman.sdma_access_ptr)
+ return -EACCES;
+
+ if (!drm_dev_enter(adev_to_drm(adev), &idx))
+ return -ENODEV;
+
+ if (write)
+ memcpy(adev->mman.sdma_access_ptr, buf, len);
+
+ num_dw = ALIGN(adev->mman.buffer_funcs->copy_num_dw, 8);
+ r = amdgpu_job_alloc_with_ib(adev, num_dw * 4, AMDGPU_IB_POOL_DELAYED, &job);
+ if (r)
+ goto out;
+
+ amdgpu_res_first(abo->tbo.resource, offset, len, &src_mm);
+ src_addr = amdgpu_ttm_domain_start(adev, bo->resource->mem_type) + src_mm.start;
+ dst_addr = amdgpu_bo_gpu_offset(adev->mman.sdma_access_bo);
+ if (write)
+ swap(src_addr, dst_addr);
+
+ amdgpu_emit_copy_buffer(adev, &job->ibs[0], src_addr, dst_addr, PAGE_SIZE, false);
+
+ amdgpu_ring_pad_ib(adev->mman.buffer_funcs_ring, &job->ibs[0]);
+ WARN_ON(job->ibs[0].length_dw > num_dw);
+
+ r = amdgpu_job_submit(job, &adev->mman.entity, AMDGPU_FENCE_OWNER_UNDEFINED, &fence);
+ if (r) {
+ amdgpu_job_free(job);
+ goto out;
+ }
+
+ if (!dma_fence_wait_timeout(fence, false, adev->sdma_timeout))
+ r = -ETIMEDOUT;
+ dma_fence_put(fence);
+
+ if (!(r || write))
+ memcpy(buf, adev->mman.sdma_access_ptr, len);
+ out:
+ drm_dev_exit(idx);
+ return r;
+ }
+
/**
* amdgpu_ttm_access_memory - Read or Write memory that backs a buffer object.
*
if (bo->resource->mem_type != TTM_PL_VRAM)
return -EIO;
+ if (amdgpu_device_has_timeouts_enabled(adev) &&
+ !amdgpu_ttm_access_memory_sdma(bo, offset, buf, len, write))
+ return len;
+
amdgpu_res_first(bo->resource, offset, len, &cursor);
while (cursor.remaining) {
size_t count, size = cursor.size;
.io_mem_reserve = &amdgpu_ttm_io_mem_reserve,
.io_mem_pfn = amdgpu_ttm_io_mem_pfn,
.access_memory = &amdgpu_ttm_access_memory,
- .del_from_lru_notify = &amdgpu_vm_del_from_lru_notify
};
/*
return r;
}
+ if (amdgpu_bo_create_kernel(adev, PAGE_SIZE, PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_GTT,
+ &adev->mman.sdma_access_bo, NULL,
+ &adev->mman.sdma_access_ptr))
+ DRM_WARN("Debug VRAM access will use slowpath MM access\n");
+
return 0;
}
if (adev->mman.stolen_reserved_size)
amdgpu_bo_free_kernel(&adev->mman.stolen_reserved_memory,
NULL, NULL);
+ amdgpu_bo_free_kernel(&adev->mman.sdma_access_bo, NULL,
+ &adev->mman.sdma_access_ptr);
amdgpu_ttm_fw_reserve_vram_fini(adev);
if (drm_dev_enter(adev_to_drm(adev), &idx)) {
adev->mman.buffer_funcs_enabled = enable;
}
+ static int amdgpu_ttm_prepare_job(struct amdgpu_device *adev,
+ bool direct_submit,
+ unsigned int num_dw,
+ struct dma_resv *resv,
+ bool vm_needs_flush,
+ struct amdgpu_job **job)
+ {
+ enum amdgpu_ib_pool_type pool = direct_submit ?
+ AMDGPU_IB_POOL_DIRECT :
+ AMDGPU_IB_POOL_DELAYED;
+ int r;
+
+ r = amdgpu_job_alloc_with_ib(adev, num_dw * 4, pool, job);
+ if (r)
+ return r;
+
+ if (vm_needs_flush) {
+ (*job)->vm_pd_addr = amdgpu_gmc_pd_addr(adev->gmc.pdb0_bo ?
+ adev->gmc.pdb0_bo :
+ adev->gart.bo);
+ (*job)->vm_needs_flush = true;
+ }
+ if (resv) {
+ r = amdgpu_sync_resv(adev, &(*job)->sync, resv,
+ AMDGPU_SYNC_ALWAYS,
+ AMDGPU_FENCE_OWNER_UNDEFINED);
+ if (r) {
+ DRM_ERROR("sync failed (%d).\n", r);
+ amdgpu_job_free(*job);
+ return r;
+ }
+ }
+ return 0;
+ }
+
int amdgpu_copy_buffer(struct amdgpu_ring *ring, uint64_t src_offset,
uint64_t dst_offset, uint32_t byte_count,
struct dma_resv *resv,
struct dma_fence **fence, bool direct_submit,
bool vm_needs_flush, bool tmz)
{
- enum amdgpu_ib_pool_type pool = direct_submit ? AMDGPU_IB_POOL_DIRECT :
- AMDGPU_IB_POOL_DELAYED;
struct amdgpu_device *adev = ring->adev;
+ unsigned num_loops, num_dw;
struct amdgpu_job *job;
-
uint32_t max_bytes;
- unsigned num_loops, num_dw;
unsigned i;
int r;
- if (direct_submit && !ring->sched.ready) {
+ if (!direct_submit && !ring->sched.ready) {
DRM_ERROR("Trying to move memory with ring turned off.\n");
return -EINVAL;
}
max_bytes = adev->mman.buffer_funcs->copy_max_bytes;
num_loops = DIV_ROUND_UP(byte_count, max_bytes);
num_dw = ALIGN(num_loops * adev->mman.buffer_funcs->copy_num_dw, 8);
-
- r = amdgpu_job_alloc_with_ib(adev, num_dw * 4, pool, &job);
+ r = amdgpu_ttm_prepare_job(adev, direct_submit, num_dw,
+ resv, vm_needs_flush, &job);
if (r)
return r;
- if (vm_needs_flush) {
- job->vm_pd_addr = amdgpu_gmc_pd_addr(adev->gmc.pdb0_bo ?
- adev->gmc.pdb0_bo : adev->gart.bo);
- job->vm_needs_flush = true;
- }
- if (resv) {
- r = amdgpu_sync_resv(adev, &job->sync, resv,
- AMDGPU_SYNC_ALWAYS,
- AMDGPU_FENCE_OWNER_UNDEFINED);
- if (r) {
- DRM_ERROR("sync failed (%d).\n", r);
- goto error_free;
- }
- }
-
for (i = 0; i < num_loops; i++) {
uint32_t cur_size_in_bytes = min(byte_count, max_bytes);
return r;
}
- int amdgpu_fill_buffer(struct amdgpu_bo *bo,
- uint32_t src_data,
- struct dma_resv *resv,
- struct dma_fence **fence)
+ static int amdgpu_ttm_fill_mem(struct amdgpu_ring *ring, uint32_t src_data,
+ uint64_t dst_addr, uint32_t byte_count,
+ struct dma_resv *resv,
+ struct dma_fence **fence,
+ bool vm_needs_flush)
{
- struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
- uint32_t max_bytes = adev->mman.buffer_funcs->fill_max_bytes;
- struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
-
- struct amdgpu_res_cursor cursor;
+ struct amdgpu_device *adev = ring->adev;
unsigned int num_loops, num_dw;
- uint64_t num_bytes;
-
struct amdgpu_job *job;
+ uint32_t max_bytes;
+ unsigned int i;
int r;
- if (!adev->mman.buffer_funcs_enabled) {
- DRM_ERROR("Trying to clear memory with ring turned off.\n");
- return -EINVAL;
- }
-
- if (bo->tbo.resource->mem_type == AMDGPU_PL_PREEMPT) {
- DRM_ERROR("Trying to clear preemptible memory.\n");
- return -EINVAL;
- }
-
- if (bo->tbo.resource->mem_type == TTM_PL_TT) {
- r = amdgpu_ttm_alloc_gart(&bo->tbo);
- if (r)
- return r;
- }
-
- num_bytes = bo->tbo.resource->num_pages << PAGE_SHIFT;
- num_loops = 0;
-
- amdgpu_res_first(bo->tbo.resource, 0, num_bytes, &cursor);
- while (cursor.remaining) {
- num_loops += DIV_ROUND_UP_ULL(cursor.size, max_bytes);
- amdgpu_res_next(&cursor, cursor.size);
- }
- num_dw = num_loops * adev->mman.buffer_funcs->fill_num_dw;
-
- /* for IB padding */
- num_dw += 64;
-
- r = amdgpu_job_alloc_with_ib(adev, num_dw * 4, AMDGPU_IB_POOL_DELAYED,
- &job);
+ max_bytes = adev->mman.buffer_funcs->fill_max_bytes;
+ num_loops = DIV_ROUND_UP_ULL(byte_count, max_bytes);
+ num_dw = ALIGN(num_loops * adev->mman.buffer_funcs->fill_num_dw, 8);
+ r = amdgpu_ttm_prepare_job(adev, false, num_dw, resv, vm_needs_flush,
+ &job);
if (r)
return r;
- if (resv) {
- r = amdgpu_sync_resv(adev, &job->sync, resv,
- AMDGPU_SYNC_ALWAYS,
- AMDGPU_FENCE_OWNER_UNDEFINED);
- if (r) {
- DRM_ERROR("sync failed (%d).\n", r);
- goto error_free;
- }
- }
-
- amdgpu_res_first(bo->tbo.resource, 0, num_bytes, &cursor);
- while (cursor.remaining) {
- uint32_t cur_size = min_t(uint64_t, cursor.size, max_bytes);
- uint64_t dst_addr = cursor.start;
+ for (i = 0; i < num_loops; i++) {
+ uint32_t cur_size = min(byte_count, max_bytes);
- dst_addr += amdgpu_ttm_domain_start(adev,
- bo->tbo.resource->mem_type);
amdgpu_emit_fill_buffer(adev, &job->ibs[0], src_data, dst_addr,
cur_size);
- amdgpu_res_next(&cursor, cur_size);
+ dst_addr += cur_size;
+ byte_count -= cur_size;
}
amdgpu_ring_pad_ib(ring, &job->ibs[0]);
return r;
}
+ int amdgpu_fill_buffer(struct amdgpu_bo *bo,
+ uint32_t src_data,
+ struct dma_resv *resv,
+ struct dma_fence **f)
+ {
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
+ struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
+ struct dma_fence *fence = NULL;
+ struct amdgpu_res_cursor dst;
+ int r;
+
+ if (!adev->mman.buffer_funcs_enabled) {
+ DRM_ERROR("Trying to clear memory with ring turned off.\n");
+ return -EINVAL;
+ }
+
+ amdgpu_res_first(bo->tbo.resource, 0, amdgpu_bo_size(bo), &dst);
+
+ mutex_lock(&adev->mman.gtt_window_lock);
+ while (dst.remaining) {
+ struct dma_fence *next;
+ uint64_t cur_size, to;
+
+ /* Never fill more than 256MiB at once to avoid timeouts */
+ cur_size = min(dst.size, 256ULL << 20);
+
+ r = amdgpu_ttm_map_buffer(&bo->tbo, bo->tbo.resource, &dst,
+ 1, ring, false, &cur_size, &to);
+ if (r)
+ goto error;
+
+ r = amdgpu_ttm_fill_mem(ring, src_data, to, cur_size, resv,
+ &next, true);
+ if (r)
+ goto error;
+
+ dma_fence_put(fence);
+ fence = next;
+
+ amdgpu_res_next(&dst, cur_size);
+ }
+ error:
+ mutex_unlock(&adev->mman.gtt_window_lock);
+ if (f)
+ *f = dma_fence_get(fence);
+ dma_fence_put(fence);
+ return r;
+ }
+
/**
* amdgpu_ttm_evict_resources - evict memory buffers
* @adev: amdgpu device object
if (bo->tbo.base.resv != vm->root.bo->tbo.base.resv)
return;
- vm->bulk_moveable = false;
+ dma_resv_assert_held(vm->root.bo->tbo.base.resv);
+
+ ttm_bo_set_bulk_move(&bo->tbo, &vm->lru_bulk_move);
if (bo->tbo.type == ttm_bo_type_kernel && bo->parent)
amdgpu_vm_bo_relocated(base);
else
list_add(&entry->tv.head, validated);
}
-/**
- * amdgpu_vm_del_from_lru_notify - update bulk_moveable flag
- *
- * @bo: BO which was removed from the LRU
- *
- * Make sure the bulk_moveable flag is updated when a BO is removed from the
- * LRU.
- */
-void amdgpu_vm_del_from_lru_notify(struct ttm_buffer_object *bo)
-{
- struct amdgpu_bo *abo;
- struct amdgpu_vm_bo_base *bo_base;
-
- if (!amdgpu_bo_is_amdgpu_bo(bo))
- return;
-
- if (bo->pin_count)
- return;
-
- abo = ttm_to_amdgpu_bo(bo);
- if (!abo->parent)
- return;
- for (bo_base = abo->vm_bo; bo_base; bo_base = bo_base->next) {
- struct amdgpu_vm *vm = bo_base->vm;
-
- if (abo->tbo.base.resv == vm->root.bo->tbo.base.resv)
- vm->bulk_moveable = false;
- }
-
-}
/**
* amdgpu_vm_move_to_lru_tail - move all BOs to the end of LRU
*
void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev,
struct amdgpu_vm *vm)
{
- struct amdgpu_vm_bo_base *bo_base;
-
- if (vm->bulk_moveable) {
- spin_lock(&adev->mman.bdev.lru_lock);
- ttm_bo_bulk_move_lru_tail(&vm->lru_bulk_move);
- spin_unlock(&adev->mman.bdev.lru_lock);
- return;
- }
-
- memset(&vm->lru_bulk_move, 0, sizeof(vm->lru_bulk_move));
-
spin_lock(&adev->mman.bdev.lru_lock);
- list_for_each_entry(bo_base, &vm->idle, vm_status) {
- struct amdgpu_bo *bo = bo_base->bo;
- struct amdgpu_bo *shadow = amdgpu_bo_shadowed(bo);
-
- if (!bo->parent)
- continue;
-
- ttm_bo_move_to_lru_tail(&bo->tbo, bo->tbo.resource,
- &vm->lru_bulk_move);
- if (shadow)
- ttm_bo_move_to_lru_tail(&shadow->tbo,
- shadow->tbo.resource,
- &vm->lru_bulk_move);
- }
+ ttm_lru_bulk_move_tail(&vm->lru_bulk_move);
spin_unlock(&adev->mman.bdev.lru_lock);
-
- vm->bulk_moveable = true;
}
/**
struct amdgpu_vm_bo_base *bo_base, *tmp;
int r;
- vm->bulk_moveable &= list_empty(&vm->evicted);
-
list_for_each_entry_safe(bo_base, tmp, &vm->evicted, vm_status) {
struct amdgpu_bo *bo = bo_base->bo;
struct amdgpu_bo *shadow = amdgpu_bo_shadowed(bo);
* Check if all VM PDs/PTs are ready for updates
*
* Returns:
- * True if eviction list is empty.
+ * True if VM is not evicting.
*/
bool amdgpu_vm_ready(struct amdgpu_vm *vm)
{
- return list_empty(&vm->evicted);
+ bool ret;
+
+ amdgpu_vm_eviction_lock(vm);
+ ret = !vm->evicting;
+ amdgpu_vm_eviction_unlock(vm);
+
+ return ret && list_empty(&vm->evicted);
}
/**
if (!entry->bo)
return;
+
shadow = amdgpu_bo_shadowed(entry->bo);
+ if (shadow) {
+ ttm_bo_set_bulk_move(&shadow->tbo, NULL);
+ amdgpu_bo_unref(&shadow);
+ }
+
+ ttm_bo_set_bulk_move(&entry->bo->tbo, NULL);
entry->bo->vm_bo = NULL;
list_del(&entry->vm_status);
- amdgpu_bo_unref(&shadow);
amdgpu_bo_unref(&entry->bo);
}
struct amdgpu_vm_pt_cursor cursor;
struct amdgpu_vm_bo_base *entry;
- vm->bulk_moveable = false;
-
for_each_amdgpu_vm_pt_dfs_safe(adev, vm, start, cursor, entry)
amdgpu_vm_free_table(entry);
nptes = max(nptes, 1u);
trace_amdgpu_vm_update_ptes(params, frag_start, upd_end,
- nptes, dst, incr, upd_flags,
+ min(nptes, 32u), dst, incr, upd_flags,
vm->task_info.pid,
vm->immediate.fence_context);
amdgpu_vm_update_flags(params, to_amdgpu_bo_vm(pt),
if (!bo)
return bo_va;
+ dma_resv_assert_held(bo->tbo.base.resv);
if (amdgpu_dmabuf_is_xgmi_accessible(adev, bo)) {
bo_va->is_xgmi = true;
/* Power up XGMI if it can be potentially used */
}
/**
- * amdgpu_vm_bo_rmv - remove a bo to a specific vm
+ * amdgpu_vm_bo_del - remove a bo from a specific vm
*
* @adev: amdgpu_device pointer
* @bo_va: requested bo_va
*
* Object have to be reserved!
*/
- void amdgpu_vm_bo_rmv(struct amdgpu_device *adev,
+ void amdgpu_vm_bo_del(struct amdgpu_device *adev,
struct amdgpu_bo_va *bo_va)
{
struct amdgpu_bo_va_mapping *mapping, *next;
struct amdgpu_vm *vm = bo_va->base.vm;
struct amdgpu_vm_bo_base **base;
+ dma_resv_assert_held(vm->root.bo->tbo.base.resv);
+
if (bo) {
+ dma_resv_assert_held(bo->tbo.base.resv);
if (bo->tbo.base.resv == vm->root.bo->tbo.base.resv)
- vm->bulk_moveable = false;
+ ttm_bo_set_bulk_move(&bo->tbo, NULL);
for (base = &bo_va->base.bo->vm_bo; *base;
base = &(*base)->next) {
/* Store positions of group of BOs */
struct ttm_lru_bulk_move lru_bulk_move;
- /* mark whether can do the bulk move */
- bool bulk_moveable;
/* Flag to indicate if VM is used for compute */
bool is_compute_context;
};
struct amdgpu_bo_va_mapping *amdgpu_vm_bo_lookup_mapping(struct amdgpu_vm *vm,
uint64_t addr);
void amdgpu_vm_bo_trace_cs(struct amdgpu_vm *vm, struct ww_acquire_ctx *ticket);
- void amdgpu_vm_bo_rmv(struct amdgpu_device *adev,
+ void amdgpu_vm_bo_del(struct amdgpu_device *adev,
struct amdgpu_bo_va *bo_va);
void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint32_t min_vm_size,
uint32_t fragment_size_default, unsigned max_level,
void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev,
struct amdgpu_vm *vm);
-void amdgpu_vm_del_from_lru_notify(struct ttm_buffer_object *bo);
void amdgpu_vm_get_memory(struct amdgpu_vm *vm, uint64_t *vram_mem,
uint64_t *gtt_mem, uint64_t *cpu_mem);
config DRM_PANEL_BRIDGE
def_bool y
depends on DRM_BRIDGE
- depends on DRM_KMS_HELPER
select DRM_PANEL
help
DRM bridge wrapper of DRM panels
config DRM_CHIPONE_ICN6211
tristate "Chipone ICN6211 MIPI-DSI/RGB Converter bridge"
depends on OF
- depends on DRM_KMS_HELPER
+ select DRM_KMS_HELPER
select DRM_MIPI_DSI
select DRM_PANEL_BRIDGE
help
config DRM_ITE_IT6505
tristate "ITE IT6505 DisplayPort bridge"
depends on OF
+ select DRM_DP_HELPER
select DRM_KMS_HELPER
select EXTCON
help
select DRM_DP_HELPER
select DRM_KMS_HELPER
select REGMAP_I2C
+ select DRM_MIPI_DSI
select DRM_PANEL
help
Toshiba TC358767 eDP bridge chip driver.
*/
#include <linux/bitfield.h>
+ #include <linux/bits.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/math64.h>
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <video/mipi_display.h>
/*
* ui2bc - UI time periods to byte clock cycles
*/
- static u32 ui2bc(struct nwl_dsi *dsi, unsigned long long ui)
+ static u32 ui2bc(unsigned int ui)
{
- u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
-
- return DIV64_U64_ROUND_UP(ui * dsi->lanes,
- dsi->mode.clock * 1000 * bpp);
+ return DIV_ROUND_UP(ui, BITS_PER_BYTE);
}
/*
}
/* values in byte clock cycles */
- cycles = ui2bc(dsi, cfg->clk_pre);
+ cycles = ui2bc(cfg->clk_pre);
DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n", cycles);
nwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, cycles);
cycles = ps2bc(dsi, cfg->lpx + cfg->clk_prepare + cfg->clk_zero);
DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n", cycles);
- cycles += ui2bc(dsi, cfg->clk_pre);
+ cycles += ui2bc(cfg->clk_pre);
DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_post: 0x%x\n", cycles);
nwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, cycles);
cycles = ps2bc(dsi, cfg->hs_exit);
/* Save the new desired phy config */
memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
- memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
+ drm_mode_copy(&dsi->mode, adjusted_mode);
drm_mode_debug_printmodeline(adjusted_mode);
if (pm_runtime_resume_and_get(dev) < 0)
{
struct nwl_dsi *dsi = bridge_to_dsi(bridge);
struct drm_bridge *panel_bridge;
- struct drm_panel *panel;
- int ret;
- ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0, &panel,
- &panel_bridge);
- if (ret)
- return ret;
-
- if (panel) {
- panel_bridge = drm_panel_bridge_add(panel);
- if (IS_ERR(panel_bridge))
- return PTR_ERR(panel_bridge);
- }
-
- if (!panel_bridge)
- return -EPROBE_DEFER;
+ panel_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node, 1, 0);
+ if (IS_ERR(panel_bridge))
+ return PTR_ERR(panel_bridge);
return drm_bridge_attach(bridge->encoder, panel_bridge, bridge, flags);
}
-static void nwl_dsi_bridge_detach(struct drm_bridge *bridge)
-{ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
-
- drm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0);
-}
-
static u32 *nwl_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
.mode_set = nwl_dsi_bridge_mode_set,
.mode_valid = nwl_dsi_bridge_mode_valid,
.attach = nwl_dsi_bridge_attach,
- .detach = nwl_dsi_bridge_detach,
};
static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
static const struct soc_device_attribute nwl_dsi_quirks_match[] = {
{ .soc_id = "i.MX8MQ", .revision = "2.0",
.data = (void *)E11418_HS_MODE_QUIRK },
- { /* sentinel. */ },
+ { /* sentinel. */ }
};
static int nwl_dsi_probe(struct platform_device *pdev)
{
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
struct device_node *np = pdata->dev->of_node;
- struct drm_panel *panel;
int ret;
- ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL);
- if (ret)
- return dev_err_probe(&adev->dev, ret,
- "could not find any panel node\n");
-
- pdata->next_bridge = devm_drm_panel_bridge_add(pdata->dev, panel);
+ pdata->next_bridge = devm_drm_of_get_bridge(pdata->dev, np, 1, 0);
if (IS_ERR(pdata->next_bridge)) {
DRM_ERROR("failed to create panel bridge\n");
return PTR_ERR(pdata->next_bridge);
static void ti_sn65dsi86_runtime_disable(void *data)
{
+ pm_runtime_dont_use_autosuspend(data);
pm_runtime_disable(data);
}
"failed to get reference clock\n");
pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(pdata->dev, 500);
+ pm_runtime_use_autosuspend(pdata->dev);
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_runtime_disable, dev);
if (ret)
return ret;
- pm_runtime_set_autosuspend_delay(pdata->dev, 500);
- pm_runtime_use_autosuspend(pdata->dev);
ti_sn65dsi86_debugfs_init(pdata);
state->mode_blob = NULL;
if (mode) {
+ struct drm_property_blob *blob;
+
drm_mode_convert_to_umode(&umode, mode);
- state->mode_blob =
- drm_property_create_blob(state->crtc->dev,
- sizeof(umode),
- &umode);
- if (IS_ERR(state->mode_blob))
- return PTR_ERR(state->mode_blob);
+ blob = drm_property_create_blob(crtc->dev,
+ sizeof(umode), &umode);
+ if (IS_ERR(blob))
+ return PTR_ERR(blob);
drm_mode_copy(&state->mode, mode);
+
+ state->mode_blob = blob;
state->enable = true;
drm_dbg_atomic(crtc->dev,
"Set [MODE:%s] for [CRTC:%d:%s] state %p\n",
struct drm_out_fence_state *fence_state;
int ret = 0;
unsigned int i, j, num_fences;
- struct drm_printer p = drm_info_printer(dev->dev);
/* disallow for drivers not supporting atomic: */
if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
} else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
ret = drm_atomic_nonblocking_commit(state);
} else {
- if (drm_debug_enabled(DRM_UT_STATE))
- drm_atomic_print_new_state(state, &p);
-
ret = drm_atomic_commit(state);
}
struct detailed_mode_closure {
struct drm_connector *connector;
- struct edid *edid;
+ const struct edid *edid;
bool preferred;
u32 quirks;
int modes;
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
+static void edid_header_fix(void *edid)
+{
+ memcpy(edid, edid_header, sizeof(edid_header));
+}
+
/**
* drm_edid_header_is_valid - sanity check the header of the base EDID block
* @raw_edid: pointer to raw base EDID block
*
* Return: 8 if the header is perfect, down to 0 if it's totally wrong.
*/
-int drm_edid_header_is_valid(const u8 *raw_edid)
+int drm_edid_header_is_valid(const void *_edid)
{
+ const struct edid *edid = _edid;
int i, score = 0;
- for (i = 0; i < sizeof(edid_header); i++)
- if (raw_edid[i] == edid_header[i])
+ for (i = 0; i < sizeof(edid_header); i++) {
+ if (edid->header[i] == edid_header[i])
score++;
+ }
return score;
}
MODULE_PARM_DESC(edid_fixup,
"Minimum number of valid EDID header bytes (0-8, default 6)");
-static int drm_edid_block_checksum(const u8 *raw_edid)
+static int edid_block_compute_checksum(const void *_block)
{
+ const u8 *block = _block;
int i;
u8 csum = 0, crc = 0;
for (i = 0; i < EDID_LENGTH - 1; i++)
- csum += raw_edid[i];
+ csum += block[i];
crc = 0x100 - csum;
return crc;
}
-static bool drm_edid_block_checksum_diff(const u8 *raw_edid, u8 real_checksum)
+static int edid_block_get_checksum(const void *_block)
{
- if (raw_edid[EDID_LENGTH - 1] != real_checksum)
- return true;
- else
- return false;
+ const struct edid *block = _block;
+
+ return block->checksum;
}
-static bool drm_edid_is_zero(const u8 *in_edid, int length)
+static int edid_block_tag(const void *_block)
{
- if (memchr_inv(in_edid, 0, length))
- return false;
+ const u8 *block = _block;
- return true;
+ return block[0];
+}
+
+static bool edid_is_zero(const void *edid, int length)
+{
+ return !memchr_inv(edid, 0, length);
}
/**
}
EXPORT_SYMBOL(drm_edid_are_equal);
+enum edid_block_status {
+ EDID_BLOCK_OK = 0,
+ EDID_BLOCK_NULL,
+ EDID_BLOCK_HEADER_CORRUPT,
+ EDID_BLOCK_HEADER_REPAIR,
+ EDID_BLOCK_HEADER_FIXED,
+ EDID_BLOCK_CHECKSUM,
+ EDID_BLOCK_VERSION,
+};
+
+static enum edid_block_status edid_block_check(const void *_block,
+ bool is_base_block)
+{
+ const struct edid *block = _block;
+
+ if (!block)
+ return EDID_BLOCK_NULL;
+
+ if (is_base_block) {
+ int score = drm_edid_header_is_valid(block);
+
+ if (score < clamp(edid_fixup, 0, 8))
+ return EDID_BLOCK_HEADER_CORRUPT;
+
+ if (score < 8)
+ return EDID_BLOCK_HEADER_REPAIR;
+ }
+
+ if (edid_block_compute_checksum(block) != edid_block_get_checksum(block))
+ return EDID_BLOCK_CHECKSUM;
+
+ if (is_base_block) {
+ if (block->version != 1)
+ return EDID_BLOCK_VERSION;
+ }
+
+ return EDID_BLOCK_OK;
+}
+
+static bool edid_block_status_valid(enum edid_block_status status, int tag)
+{
+ return status == EDID_BLOCK_OK ||
+ status == EDID_BLOCK_HEADER_FIXED ||
+ (status == EDID_BLOCK_CHECKSUM && tag == CEA_EXT);
+}
+
+static bool edid_block_valid(const void *block, bool base)
+{
+ return edid_block_status_valid(edid_block_check(block, base),
+ edid_block_tag(block));
+}
+
/**
* drm_edid_block_valid - Sanity check the EDID block (base or extension)
* @raw_edid: pointer to raw EDID block
- * @block: type of block to validate (0 for base, extension otherwise)
+ * @block_num: type of block to validate (0 for base, extension otherwise)
* @print_bad_edid: if true, dump bad EDID blocks to the console
* @edid_corrupt: if true, the header or checksum is invalid
*
*
* Return: True if the block is valid, false otherwise.
*/
-bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,
+bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,
bool *edid_corrupt)
{
- u8 csum;
- struct edid *edid = (struct edid *)raw_edid;
+ struct edid *block = (struct edid *)_block;
+ enum edid_block_status status;
+ bool is_base_block = block_num == 0;
+ bool valid;
- if (WARN_ON(!raw_edid))
+ if (WARN_ON(!block))
return false;
- if (edid_fixup > 8 || edid_fixup < 0)
- edid_fixup = 6;
+ status = edid_block_check(block, is_base_block);
+ if (status == EDID_BLOCK_HEADER_REPAIR) {
+ DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
+ edid_header_fix(block);
- if (block == 0) {
- int score = drm_edid_header_is_valid(raw_edid);
-
- if (score == 8) {
- if (edid_corrupt)
- *edid_corrupt = false;
- } else if (score >= edid_fixup) {
- /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6
- * The corrupt flag needs to be set here otherwise, the
- * fix-up code here will correct the problem, the
- * checksum is correct and the test fails
- */
- if (edid_corrupt)
- *edid_corrupt = true;
- DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
- memcpy(raw_edid, edid_header, sizeof(edid_header));
- } else {
- if (edid_corrupt)
- *edid_corrupt = true;
- goto bad;
- }
+ /* Retry with fixed header, update status if that worked. */
+ status = edid_block_check(block, is_base_block);
+ if (status == EDID_BLOCK_OK)
+ status = EDID_BLOCK_HEADER_FIXED;
}
- csum = drm_edid_block_checksum(raw_edid);
- if (drm_edid_block_checksum_diff(raw_edid, csum)) {
- if (edid_corrupt)
+ if (edid_corrupt) {
+ /*
+ * Unknown major version isn't corrupt but we can't use it. Only
+ * the base block can reset edid_corrupt to false.
+ */
+ if (is_base_block &&
+ (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION))
+ *edid_corrupt = false;
+ else if (status != EDID_BLOCK_OK)
*edid_corrupt = true;
-
- /* allow CEA to slide through, switches mangle this */
- if (raw_edid[0] == CEA_EXT) {
- DRM_DEBUG("EDID checksum is invalid, remainder is %d\n", csum);
- DRM_DEBUG("Assuming a KVM switch modified the CEA block but left the original checksum\n");
- } else {
- if (print_bad_edid)
- DRM_NOTE("EDID checksum is invalid, remainder is %d\n", csum);
-
- goto bad;
- }
}
- /* per-block-type checks */
- switch (raw_edid[0]) {
- case 0: /* base */
- if (edid->version != 1) {
- DRM_NOTE("EDID has major version %d, instead of 1\n", edid->version);
- goto bad;
+ /* Determine whether we can use this block with this status. */
+ valid = edid_block_status_valid(status, edid_block_tag(block));
+
+ /* Some fairly random status printouts. */
+ if (status == EDID_BLOCK_CHECKSUM) {
+ if (valid) {
+ DRM_DEBUG("EDID block checksum is invalid, remainder is %d\n",
+ edid_block_compute_checksum(block));
+ DRM_DEBUG("Assuming a KVM switch modified the block but left the original checksum\n");
+ } else if (print_bad_edid) {
+ DRM_NOTE("EDID block checksum is invalid, remainder is %d\n",
+ edid_block_compute_checksum(block));
}
-
- if (edid->revision > 4)
- DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
- break;
-
- default:
- break;
+ } else if (status == EDID_BLOCK_VERSION) {
+ DRM_NOTE("EDID has major version %d, instead of 1\n",
+ block->version);
}
- return true;
-
-bad:
- if (print_bad_edid) {
- if (drm_edid_is_zero(raw_edid, EDID_LENGTH)) {
+ if (!valid && print_bad_edid) {
+ if (edid_is_zero(block, EDID_LENGTH)) {
pr_notice("EDID block is all zeroes\n");
} else {
pr_notice("Raw EDID:\n");
print_hex_dump(KERN_NOTICE,
" \t", DUMP_PREFIX_NONE, 16, 1,
- raw_edid, EDID_LENGTH, false);
+ block, EDID_LENGTH, false);
}
}
- return false;
+
+ return valid;
}
EXPORT_SYMBOL(drm_edid_block_valid);
}
EXPORT_SYMBOL(drm_edid_is_valid);
+static struct edid *edid_filter_invalid_blocks(const struct edid *edid,
+ int invalid_blocks)
+{
+ struct edid *new, *dest_block;
+ int valid_extensions = edid->extensions - invalid_blocks;
+ int i;
+
+ new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, GFP_KERNEL);
+ if (!new)
+ goto out;
+
+ dest_block = new;
+ for (i = 0; i <= edid->extensions; i++) {
+ const void *block = edid + i;
+
+ if (edid_block_valid(block, i == 0))
+ memcpy(dest_block++, block, EDID_LENGTH);
+ }
+
+ new->extensions = valid_extensions;
+ new->checksum = edid_block_compute_checksum(new);
+
+out:
+ kfree(edid);
+
+ return new;
+}
+
#define DDC_SEGMENT_ADDR 0x30
/**
* drm_do_probe_ddc_edid() - get EDID information via I2C
/* Calculate real checksum for the last edid extension block data */
if (last_block < num_blocks)
connector->real_edid_checksum =
- drm_edid_block_checksum(edid + last_block * EDID_LENGTH);
+ edid_block_compute_checksum(edid + last_block * EDID_LENGTH);
if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
return;
u8 *block = edid + i * EDID_LENGTH;
char prefix[20];
- if (drm_edid_is_zero(block, EDID_LENGTH))
+ if (edid_is_zero(block, EDID_LENGTH))
sprintf(prefix, "\t[%02x] ZERO ", i);
else if (!drm_edid_block_valid(block, i, false, NULL))
sprintf(prefix, "\t[%02x] BAD ", i);
int *null_edid_counter = connector ? &connector->null_edid_counter : NULL;
bool *edid_corrupt = connector ? &connector->edid_corrupt : NULL;
void *edid;
- int i;
+ int try;
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (edid == NULL)
return NULL;
/* base block fetch */
- for (i = 0; i < 4; i++) {
+ for (try = 0; try < 4; try++) {
if (get_edid_block(data, edid, 0, EDID_LENGTH))
goto out;
if (drm_edid_block_valid(edid, 0, false, edid_corrupt))
break;
- if (i == 0 && drm_edid_is_zero(edid, EDID_LENGTH)) {
+ if (try == 0 && edid_is_zero(edid, EDID_LENGTH)) {
if (null_edid_counter)
(*null_edid_counter)++;
goto carp;
}
}
- if (i == 4)
+ if (try == 4)
goto carp;
return edid;
size_t len),
void *data)
{
- int i, j = 0, valid_extensions = 0;
- u8 *edid, *new;
- struct edid *override;
+ int j, invalid_blocks = 0;
+ struct edid *edid, *new, *override;
override = drm_get_override_edid(connector);
if (override)
return override;
- edid = (u8 *)drm_do_get_edid_base_block(connector, get_edid_block, data);
+ edid = drm_do_get_edid_base_block(connector, get_edid_block, data);
if (!edid)
return NULL;
- /* if there's no extensions or no connector, we're done */
- valid_extensions = edid[0x7e];
- if (valid_extensions == 0)
- return (struct edid *)edid;
+ if (edid->extensions == 0)
+ return edid;
- new = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL);
+ new = krealloc(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
if (!new)
goto out;
edid = new;
- for (j = 1; j <= edid[0x7e]; j++) {
- u8 *block = edid + j * EDID_LENGTH;
+ for (j = 1; j <= edid->extensions; j++) {
+ void *block = edid + j;
+ int try;
- for (i = 0; i < 4; i++) {
+ for (try = 0; try < 4; try++) {
if (get_edid_block(data, block, j, EDID_LENGTH))
goto out;
if (drm_edid_block_valid(block, j, false, NULL))
break;
}
- if (i == 4)
- valid_extensions--;
+ if (try == 4)
+ invalid_blocks++;
}
- if (valid_extensions != edid[0x7e]) {
- u8 *base;
+ if (invalid_blocks) {
+ connector_bad_edid(connector, (u8 *)edid, edid->extensions + 1);
- connector_bad_edid(connector, edid, edid[0x7e] + 1);
-
- edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
- edid[0x7e] = valid_extensions;
-
- new = kmalloc_array(valid_extensions + 1, EDID_LENGTH,
- GFP_KERNEL);
- if (!new)
- goto out;
-
- base = new;
- for (i = 0; i <= edid[0x7e]; i++) {
- u8 *block = edid + i * EDID_LENGTH;
-
- if (!drm_edid_block_valid(block, i, false, NULL))
- continue;
-
- memcpy(base, block, EDID_LENGTH);
- base += EDID_LENGTH;
- }
-
- kfree(edid);
- edid = new;
+ edid = edid_filter_invalid_blocks(edid, invalid_blocks);
}
- return (struct edid *)edid;
+ return edid;
out:
kfree(edid);
u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
{
- struct edid *edid;
+ const struct edid *edid;
u32 panel_id;
edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter);
}
EXPORT_SYMBOL(drm_mode_find_dmt);
-static bool is_display_descriptor(const u8 d[18], u8 tag)
+static bool is_display_descriptor(const struct detailed_timing *descriptor, u8 type)
{
- return d[0] == 0x00 && d[1] == 0x00 &&
- d[2] == 0x00 && d[3] == tag;
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0);
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.pad1) != 2);
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.type) != 3);
+
+ return descriptor->pixel_clock == 0 &&
+ descriptor->data.other_data.pad1 == 0 &&
+ descriptor->data.other_data.type == type;
}
-static bool is_detailed_timing_descriptor(const u8 d[18])
+static bool is_detailed_timing_descriptor(const struct detailed_timing *descriptor)
{
- return d[0] != 0x00 || d[1] != 0x00;
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0);
+
+ return descriptor->pixel_clock != 0;
}
-typedef void detailed_cb(struct detailed_timing *timing, void *closure);
+typedef void detailed_cb(const struct detailed_timing *timing, void *closure);
static void
-cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+cea_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
{
int i, n;
u8 d = ext[0x02];
- u8 *det_base = ext + d;
+ const u8 *det_base = ext + d;
if (d < 4 || d > 127)
return;
n = (127 - d) / 18;
for (i = 0; i < n; i++)
- cb((struct detailed_timing *)(det_base + 18 * i), closure);
+ cb((const struct detailed_timing *)(det_base + 18 * i), closure);
}
static void
-vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
{
unsigned int i, n = min((int)ext[0x02], 6);
- u8 *det_base = ext + 5;
+ const u8 *det_base = ext + 5;
if (ext[0x01] != 1)
return; /* unknown version */
for (i = 0; i < n; i++)
- cb((struct detailed_timing *)(det_base + 18 * i), closure);
+ cb((const struct detailed_timing *)(det_base + 18 * i), closure);
}
static void
-drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
+drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *closure)
{
int i;
- struct edid *edid = (struct edid *)raw_edid;
if (edid == NULL)
return;
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
cb(&(edid->detailed_timings[i]), closure);
- for (i = 1; i <= raw_edid[0x7e]; i++) {
- u8 *ext = raw_edid + (i * EDID_LENGTH);
+ for (i = 1; i <= edid->extensions; i++) {
+ const u8 *ext = (const u8 *)edid + (i * EDID_LENGTH);
switch (*ext) {
case CEA_EXT:
}
static void
-is_rb(struct detailed_timing *t, void *data)
+is_rb(const struct detailed_timing *descriptor, void *data)
{
- u8 *r = (u8 *)t;
+ bool *res = data;
- if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE))
+ if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
return;
- if (r[15] & 0x10)
- *(bool *)data = true;
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.cvt.flags) != 15);
+
+ if (descriptor->data.other_data.data.range.flags == DRM_EDID_CVT_SUPPORT_FLAG &&
+ descriptor->data.other_data.data.range.formula.cvt.flags & 0x10)
+ *res = true;
}
/* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */
static bool
-drm_monitor_supports_rb(struct edid *edid)
+drm_monitor_supports_rb(const struct edid *edid)
{
if (edid->revision >= 4) {
bool ret = false;
- drm_for_each_detailed_block((u8 *)edid, is_rb, &ret);
+ drm_for_each_detailed_block(edid, is_rb, &ret);
return ret;
}
}
static void
-find_gtf2(struct detailed_timing *t, void *data)
+find_gtf2(const struct detailed_timing *descriptor, void *data)
{
- u8 *r = (u8 *)t;
+ const struct detailed_timing **res = data;
- if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE))
+ if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
return;
- if (r[10] == 0x02)
- *(u8 **)data = r;
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
+
+ if (descriptor->data.other_data.data.range.flags == 0x02)
+ *res = descriptor;
}
/* Secondary GTF curve kicks in above some break frequency */
static int
-drm_gtf2_hbreak(struct edid *edid)
+drm_gtf2_hbreak(const struct edid *edid)
{
- u8 *r = NULL;
+ const struct detailed_timing *descriptor = NULL;
+
+ drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
- drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
- return r ? (r[12] * 2) : 0;
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12);
+
+ return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.hfreq_start_khz * 2 : 0;
}
static int
-drm_gtf2_2c(struct edid *edid)
+drm_gtf2_2c(const struct edid *edid)
{
- u8 *r = NULL;
+ const struct detailed_timing *descriptor = NULL;
+
+ drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
- drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
- return r ? r[13] : 0;
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13);
+
+ return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.c : 0;
}
static int
-drm_gtf2_m(struct edid *edid)
+drm_gtf2_m(const struct edid *edid)
{
- u8 *r = NULL;
+ const struct detailed_timing *descriptor = NULL;
+
+ drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14);
- drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
- return r ? (r[15] << 8) + r[14] : 0;
+ return descriptor ? le16_to_cpu(descriptor->data.other_data.data.range.formula.gtf2.m) : 0;
}
static int
-drm_gtf2_k(struct edid *edid)
+drm_gtf2_k(const struct edid *edid)
{
- u8 *r = NULL;
+ const struct detailed_timing *descriptor = NULL;
+
+ drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16);
- drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
- return r ? r[16] : 0;
+ return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.k : 0;
}
static int
-drm_gtf2_2j(struct edid *edid)
+drm_gtf2_2j(const struct edid *edid)
{
- u8 *r = NULL;
+ const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
- return r ? r[17] : 0;
+ drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+
+ BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17);
+
+ return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0;
}
/**
* standard_timing_level - get std. timing level(CVT/GTF/DMT)
* @edid: EDID block to scan
*/
-static int standard_timing_level(struct edid *edid)
+static int standard_timing_level(const struct edid *edid)
{
if (edid->revision >= 2) {
if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
* and convert them into a real mode using CVT/GTF/DMT.
*/
static struct drm_display_mode *
-drm_mode_std(struct drm_connector *connector, struct edid *edid,
- struct std_timing *t)
+drm_mode_std(struct drm_connector *connector, const struct edid *edid,
+ const struct std_timing *t)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *m, *mode = NULL;
*/
static void
drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
- struct detailed_pixel_timing *pt)
+ const struct detailed_pixel_timing *pt)
{
int i;
static const struct {
* return a new struct drm_display_mode.
*/
static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
- struct edid *edid,
- struct detailed_timing *timing,
+ const struct edid *edid,
+ const struct detailed_timing *timing,
u32 quirks)
{
struct drm_display_mode *mode;
- struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+ const struct detailed_pixel_timing *pt = &timing->data.pixel_data;
unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo;
unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo;
unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo;
return NULL;
if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
- timing->pixel_clock = cpu_to_le16(1088);
-
- mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
+ mode->clock = 1088 * 10;
+ else
+ mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
mode->hdisplay = hactive;
mode->hsync_start = mode->hdisplay + hsync_offset;
drm_mode_do_interlace_quirk(mode, pt);
if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
- pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
+ mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
+ } else {
+ mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
+ DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
+ DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
}
- mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
- DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
- mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
- DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
-
set_size:
mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
static bool
mode_in_hsync_range(const struct drm_display_mode *mode,
- struct edid *edid, u8 *t)
+ const struct edid *edid, const u8 *t)
{
int hsync, hmin, hmax;
static bool
mode_in_vsync_range(const struct drm_display_mode *mode,
- struct edid *edid, u8 *t)
+ const struct edid *edid, const u8 *t)
{
int vsync, vmin, vmax;
}
static u32
-range_pixel_clock(struct edid *edid, u8 *t)
+range_pixel_clock(const struct edid *edid, const u8 *t)
{
/* unspecified */
if (t[9] == 0 || t[9] == 255)
}
static bool
-mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
- struct detailed_timing *timing)
+mode_in_range(const struct drm_display_mode *mode, const struct edid *edid,
+ const struct detailed_timing *timing)
{
u32 max_clock;
- u8 *t = (u8 *)timing;
+ const u8 *t = (const u8 *)timing;
if (!mode_in_hsync_range(mode, edid, t))
return false;
}
static int
-drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid,
- struct detailed_timing *timing)
+drm_dmt_modes_for_range(struct drm_connector *connector, const struct edid *edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
}
static int
-drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
- struct detailed_timing *timing)
+drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
}
static int
-drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
- struct detailed_timing *timing)
+drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
}
static void
-do_inferred_modes(struct detailed_timing *timing, void *c)
+do_inferred_modes(const struct detailed_timing *timing, void *c)
{
struct detailed_mode_closure *closure = c;
- struct detailed_non_pixel *data = &timing->data.other_data;
- struct detailed_data_monitor_range *range = &data->data.range;
+ const struct detailed_non_pixel *data = &timing->data.other_data;
+ const struct detailed_data_monitor_range *range = &data->data.range;
- if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_MONITOR_RANGE))
+ if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE))
return;
closure->modes += drm_dmt_modes_for_range(closure->connector,
}
static int
-add_inferred_modes(struct drm_connector *connector, struct edid *edid)
+add_inferred_modes(struct drm_connector *connector, const struct edid *edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
};
if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
- &closure);
+ drm_for_each_detailed_block(edid, do_inferred_modes, &closure);
return closure.modes;
}
static int
-drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
+drm_est3_modes(struct drm_connector *connector, const struct detailed_timing *timing)
{
int i, j, m, modes = 0;
struct drm_display_mode *mode;
- u8 *est = ((u8 *)timing) + 6;
+ const u8 *est = ((const u8 *)timing) + 6;
for (i = 0; i < 6; i++) {
for (j = 7; j >= 0; j--) {
}
static void
-do_established_modes(struct detailed_timing *timing, void *c)
+do_established_modes(const struct detailed_timing *timing, void *c)
{
struct detailed_mode_closure *closure = c;
- if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_EST_TIMINGS))
+ if (!is_display_descriptor(timing, EDID_DETAIL_EST_TIMINGS))
return;
closure->modes += drm_est3_modes(closure->connector, timing);
* (defined above). Tease them out and add them to the global modes list.
*/
static int
-add_established_modes(struct drm_connector *connector, struct edid *edid)
+add_established_modes(struct drm_connector *connector, const struct edid *edid)
{
struct drm_device *dev = connector->dev;
unsigned long est_bits = edid->established_timings.t1 |
}
if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block((u8 *)edid,
- do_established_modes, &closure);
+ drm_for_each_detailed_block(edid, do_established_modes,
+ &closure);
return modes + closure.modes;
}
static void
-do_standard_modes(struct detailed_timing *timing, void *c)
+do_standard_modes(const struct detailed_timing *timing, void *c)
{
struct detailed_mode_closure *closure = c;
- struct detailed_non_pixel *data = &timing->data.other_data;
+ const struct detailed_non_pixel *data = &timing->data.other_data;
struct drm_connector *connector = closure->connector;
- struct edid *edid = closure->edid;
+ const struct edid *edid = closure->edid;
int i;
- if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_STD_MODES))
+ if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES))
return;
for (i = 0; i < 6; i++) {
- struct std_timing *std = &data->data.timings[i];
+ const struct std_timing *std = &data->data.timings[i];
struct drm_display_mode *newmode;
newmode = drm_mode_std(connector, edid, std);
* GTF or CVT. Grab them from @edid and add them to the list.
*/
static int
-add_standard_modes(struct drm_connector *connector, struct edid *edid)
+add_standard_modes(struct drm_connector *connector, const struct edid *edid)
{
int i, modes = 0;
struct detailed_mode_closure closure = {
}
if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
+ drm_for_each_detailed_block(edid, do_standard_modes,
&closure);
/* XXX should also look for standard codes in VTB blocks */
}
static int drm_cvt_modes(struct drm_connector *connector,
- struct detailed_timing *timing)
+ const struct detailed_timing *timing)
{
int i, j, modes = 0;
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;
- struct cvt_timing *cvt;
+ const struct cvt_timing *cvt;
const int rates[] = { 60, 85, 75, 60, 50 };
const u8 empty[3] = { 0, 0, 0 };
}
static void
-do_cvt_mode(struct detailed_timing *timing, void *c)
+do_cvt_mode(const struct detailed_timing *timing, void *c)
{
struct detailed_mode_closure *closure = c;
- if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_CVT_3BYTE))
+ if (!is_display_descriptor(timing, EDID_DETAIL_CVT_3BYTE))
return;
closure->modes += drm_cvt_modes(closure->connector, timing);
}
static int
-add_cvt_modes(struct drm_connector *connector, struct edid *edid)
+add_cvt_modes(struct drm_connector *connector, const struct edid *edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
};
if (version_greater(edid, 1, 2))
- drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
+ drm_for_each_detailed_block(edid, do_cvt_mode, &closure);
/* XXX should also look for CVT codes in VTB blocks */
static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode);
static void
-do_detailed_mode(struct detailed_timing *timing, void *c)
+do_detailed_mode(const struct detailed_timing *timing, void *c)
{
struct detailed_mode_closure *closure = c;
struct drm_display_mode *newmode;
- if (!is_detailed_timing_descriptor((const u8 *)timing))
+ if (!is_detailed_timing_descriptor(timing))
return;
newmode = drm_mode_detailed(closure->connector->dev,
* @quirks: quirks to apply
*/
static int
-add_detailed_modes(struct drm_connector *connector, struct edid *edid,
+add_detailed_modes(struct drm_connector *connector, const struct edid *edid,
u32 quirks)
{
struct detailed_mode_closure closure = {
closure.preferred =
(edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
- drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
+ drm_for_each_detailed_block(edid, do_detailed_mode, &closure);
return closure.modes;
}
/* Find CEA extension */
for (i = *ext_index; i < edid->extensions; i++) {
edid_ext = (const u8 *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == ext_id)
+ if (edid_block_tag(edid_ext) == ext_id)
break;
}
}
static int
-add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
+add_alternate_cea_modes(struct drm_connector *connector, const struct edid *edid)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *tmp;
}
static int
-add_cea_modes(struct drm_connector *connector, struct edid *edid)
+add_cea_modes(struct drm_connector *connector, const struct edid *edid)
{
const u8 *cea = drm_find_cea_extension(edid);
const u8 *db, *hdmi = NULL, *video = NULL;
}
static void
-monitor_name(struct detailed_timing *t, void *data)
+monitor_name(const struct detailed_timing *timing, void *data)
{
- if (!is_display_descriptor((const u8 *)t, EDID_DETAIL_MONITOR_NAME))
+ const char **res = data;
+
+ if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_NAME))
return;
- *(u8 **)data = t->data.other_data.data.str.str;
+ *res = timing->data.other_data.data.str.str;
}
-static int get_monitor_name(struct edid *edid, char name[13])
+static int get_monitor_name(const struct edid *edid, char name[13])
{
- char *edid_name = NULL;
+ const char *edid_name = NULL;
int mnl;
if (!edid || !name)
return 0;
- drm_for_each_detailed_block((u8 *)edid, monitor_name, &edid_name);
+ drm_for_each_detailed_block(edid, monitor_name, &edid_name);
for (mnl = 0; edid_name && mnl < 13; mnl++) {
if (edid_name[mnl] == 0x0a)
break;
* @bufsize: The size of the name buffer (should be at least 14 chars.)
*
*/
-void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize)
+void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize)
{
int name_length;
char buf[13];
* Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
* HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
*/
-static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
+static void drm_edid_to_eld(struct drm_connector *connector,
+ const struct edid *edid)
{
uint8_t *eld = connector->eld;
const u8 *cea;
*
* Return: The number of found SADs or negative number on error.
*/
-int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
+int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads)
{
int count = 0;
int i, start, end, dbl;
* Return: The number of found Speaker Allocation Blocks or negative number on
* error.
*/
-int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
+int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb)
{
int count = 0;
int i, start, end, dbl;
*
* Return: True if the monitor is HDMI, false if not or unknown.
*/
-bool drm_detect_hdmi_monitor(struct edid *edid)
+bool drm_detect_hdmi_monitor(const struct edid *edid)
{
const u8 *edid_ext;
int i;
*
* Return: True if the monitor supports audio, false otherwise.
*/
-bool drm_detect_monitor_audio(struct edid *edid)
+bool drm_detect_monitor_audio(const struct edid *edid)
{
const u8 *edid_ext;
int i, j;
if (!edid_ext)
goto end;
- has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+ has_audio = (edid_ext[0] == CEA_EXT &&
+ (edid_ext[3] & EDID_BASIC_AUDIO) != 0);
if (has_audio) {
DRM_DEBUG_KMS("Monitor has basic audio support\n");
/* The existence of a CEA block should imply RGB support */
info->color_formats = DRM_COLOR_FORMAT_RGB444;
- if (edid_ext[3] & EDID_CEA_YCRCB444)
- info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
- if (edid_ext[3] & EDID_CEA_YCRCB422)
- info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
+
+ /* CTA DisplayID Data Block does not have byte #3 */
+ if (edid_ext[0] == CEA_EXT) {
+ if (edid_ext[3] & EDID_CEA_YCRCB444)
+ info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
+ if (edid_ext[3] & EDID_CEA_YCRCB422)
+ info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
+ }
if (cea_db_offsets(edid_ext, &start, &end))
return;
}
static
-void get_monitor_range(struct detailed_timing *timing,
+void get_monitor_range(const struct detailed_timing *timing,
void *info_monitor_range)
{
struct drm_monitor_range_info *monitor_range = info_monitor_range;
const struct detailed_non_pixel *data = &timing->data.other_data;
const struct detailed_data_monitor_range *range = &data->data.range;
- if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_MONITOR_RANGE))
+ if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE))
return;
/*
if (!version_greater(edid, 1, 1))
return;
- drm_for_each_detailed_block((u8 *)edid, get_monitor_range,
+ drm_for_each_detailed_block(edid, get_monitor_range,
&info->monitor_range);
DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n",
if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
goto out;
+ info->color_formats |= DRM_COLOR_FORMAT_RGB444;
drm_parse_cea_ext(connector, edid);
/*
DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
connector->name, info->bpc);
- info->color_formats |= DRM_COLOR_FORMAT_RGB444;
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
}
static int add_displayid_detailed_modes(struct drm_connector *connector,
- struct edid *edid)
+ const struct edid *edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
return num_modes;
}
-/**
- * drm_add_edid_modes - add modes from EDID data, if available
- * @connector: connector we're probing
- * @edid: EDID data
- *
- * Add the specified modes to the connector's mode list. Also fills out the
- * &drm_display_info structure and ELD in @connector with any information which
- * can be derived from the edid.
- *
- * Return: The number of modes added or 0 if we couldn't find any.
- */
-int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
+static int drm_edid_connector_update(struct drm_connector *connector,
+ const struct edid *edid)
{
int num_modes = 0;
u32 quirks;
clear_eld(connector);
return 0;
}
- if (!drm_edid_is_valid(edid)) {
- clear_eld(connector);
- drm_warn(connector->dev, "%s: EDID invalid.\n",
- connector->name);
- return 0;
- }
drm_edid_to_eld(connector, edid);
return num_modes;
}
+
+/**
+ * drm_add_edid_modes - add modes from EDID data, if available
+ * @connector: connector we're probing
+ * @edid: EDID data
+ *
+ * Add the specified modes to the connector's mode list. Also fills out the
+ * &drm_display_info structure and ELD in @connector with any information which
+ * can be derived from the edid.
+ *
+ * Return: The number of modes added or 0 if we couldn't find any.
+ */
+int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
+{
+ if (edid && !drm_edid_is_valid(edid)) {
+ drm_warn(connector->dev, "%s: EDID invalid.\n",
+ connector->name);
+ edid = NULL;
+ }
+
+ return drm_edid_connector_update(connector, edid);
+}
EXPORT_SYMBOL(drm_add_edid_modes);
/**
*
*/
- #include <linux/dma-buf-map.h>
#include <linux/dma-buf.h>
#include <linux/file.h>
#include <linux/fs.h>
+ #include <linux/iosys-map.h>
#include <linux/mem_encrypt.h>
#include <linux/mm.h>
#include <linux/mman.h>
obj->funcs->unpin(obj);
}
- int drm_gem_vmap(struct drm_gem_object *obj, struct dma_buf_map *map)
+ int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
int ret;
ret = obj->funcs->vmap(obj, map);
if (ret)
return ret;
- else if (dma_buf_map_is_null(map))
+ else if (iosys_map_is_null(map))
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL(drm_gem_vmap);
- void drm_gem_vunmap(struct drm_gem_object *obj, struct dma_buf_map *map)
+ void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
{
- if (dma_buf_map_is_null(map))
+ if (iosys_map_is_null(map))
return;
if (obj->funcs->vunmap)
obj->funcs->vunmap(obj, map);
/* Always set the mapping to NULL. Callers may rely on this. */
- dma_buf_map_clear(map);
+ iosys_map_clear(map);
}
EXPORT_SYMBOL(drm_gem_vunmap);
ww_acquire_fini(acquire_ctx);
}
EXPORT_SYMBOL(drm_gem_unlock_reservations);
-
-/**
- * drm_gem_fence_array_add - Adds the fence to an array of fences to be
- * waited on, deduplicating fences from the same context.
- *
- * @fence_array: array of dma_fence * for the job to block on.
- * @fence: the dma_fence to add to the list of dependencies.
- *
- * This functions consumes the reference for @fence both on success and error
- * cases.
- *
- * Returns:
- * 0 on success, or an error on failing to expand the array.
- */
-int drm_gem_fence_array_add(struct xarray *fence_array,
- struct dma_fence *fence)
-{
- struct dma_fence *entry;
- unsigned long index;
- u32 id = 0;
- int ret;
-
- if (!fence)
- return 0;
-
- /* Deduplicate if we already depend on a fence from the same context.
- * This lets the size of the array of deps scale with the number of
- * engines involved, rather than the number of BOs.
- */
- xa_for_each(fence_array, index, entry) {
- if (entry->context != fence->context)
- continue;
-
- if (dma_fence_is_later(fence, entry)) {
- dma_fence_put(entry);
- xa_store(fence_array, index, fence, GFP_KERNEL);
- } else {
- dma_fence_put(fence);
- }
- return 0;
- }
-
- ret = xa_alloc(fence_array, &id, fence, xa_limit_32b, GFP_KERNEL);
- if (ret != 0)
- dma_fence_put(fence);
-
- return ret;
-}
-EXPORT_SYMBOL(drm_gem_fence_array_add);
-
-/**
- * drm_gem_fence_array_add_implicit - Adds the implicit dependencies tracked
- * in the GEM object's reservation object to an array of dma_fences for use in
- * scheduling a rendering job.
- *
- * This should be called after drm_gem_lock_reservations() on your array of
- * GEM objects used in the job but before updating the reservations with your
- * own fences.
- *
- * @fence_array: array of dma_fence * for the job to block on.
- * @obj: the gem object to add new dependencies from.
- * @write: whether the job might write the object (so we need to depend on
- * shared fences in the reservation object).
- */
-int drm_gem_fence_array_add_implicit(struct xarray *fence_array,
- struct drm_gem_object *obj,
- bool write)
-{
- struct dma_resv_iter cursor;
- struct dma_fence *fence;
- int ret = 0;
-
- dma_resv_for_each_fence(&cursor, obj->resv, write, fence) {
- ret = drm_gem_fence_array_add(fence_array, fence);
- if (ret)
- break;
- }
- return ret;
-}
-EXPORT_SYMBOL(drm_gem_fence_array_add_implicit);
// SPDX-License-Identifier: GPL-2.0-or-later
- #include <linux/dma-buf-map.h>
+ #include <linux/iosys-map.h>
#include <linux/module.h>
#include <drm/drm_debugfs.h>
*/
WARN_ON(gbo->vmap_use_count);
- WARN_ON(dma_buf_map_is_set(&gbo->map));
+ WARN_ON(iosys_map_is_set(&gbo->map));
drm_gem_object_release(&gbo->bo.base);
}
EXPORT_SYMBOL(drm_gem_vram_unpin);
static int drm_gem_vram_kmap_locked(struct drm_gem_vram_object *gbo,
- struct dma_buf_map *map)
+ struct iosys_map *map)
{
int ret;
* page mapping might still be around. Only vmap if the there's
* no mapping present.
*/
- if (dma_buf_map_is_null(&gbo->map)) {
+ if (iosys_map_is_null(&gbo->map)) {
ret = ttm_bo_vmap(&gbo->bo, &gbo->map);
if (ret)
return ret;
}
static void drm_gem_vram_kunmap_locked(struct drm_gem_vram_object *gbo,
- struct dma_buf_map *map)
+ struct iosys_map *map)
{
struct drm_device *dev = gbo->bo.base.dev;
if (drm_WARN_ON_ONCE(dev, !gbo->vmap_use_count))
return;
- if (drm_WARN_ON_ONCE(dev, !dma_buf_map_is_equal(&gbo->map, map)))
+ if (drm_WARN_ON_ONCE(dev, !iosys_map_is_equal(&gbo->map, map)))
return; /* BUG: map not mapped from this BO */
if (--gbo->vmap_use_count > 0)
* Returns:
* 0 on success, or a negative error code otherwise.
*/
- int drm_gem_vram_vmap(struct drm_gem_vram_object *gbo, struct dma_buf_map *map)
+ int drm_gem_vram_vmap(struct drm_gem_vram_object *gbo, struct iosys_map *map)
{
int ret;
* A call to drm_gem_vram_vunmap() unmaps and unpins a GEM VRAM buffer. See
* the documentation for drm_gem_vram_vmap() for more information.
*/
- void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo, struct dma_buf_map *map)
+ void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo,
+ struct iosys_map *map)
{
int ret;
return;
ttm_bo_vunmap(bo, &gbo->map);
- dma_buf_map_clear(&gbo->map); /* explicitly clear mapping for next vmap call */
+ iosys_map_clear(&gbo->map); /* explicitly clear mapping for next vmap call */
}
static int drm_gem_vram_bo_driver_move(struct drm_gem_vram_object *gbo,
* Returns:
* 0 on success, or a negative error code otherwise.
*/
- static int drm_gem_vram_object_vmap(struct drm_gem_object *gem, struct dma_buf_map *map)
+ static int drm_gem_vram_object_vmap(struct drm_gem_object *gem,
+ struct iosys_map *map)
{
struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
* @gem: The GEM object to unmap
* @map: Kernel virtual address where the VRAM GEM object was mapped
*/
- static void drm_gem_vram_object_vunmap(struct drm_gem_object *gem, struct dma_buf_map *map)
+ static void drm_gem_vram_object_vunmap(struct drm_gem_object *gem,
+ struct iosys_map *map)
{
struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
if (!tt)
return NULL;
- ret = ttm_tt_init(tt, bo, page_flags, ttm_cached);
+ ret = ttm_tt_init(tt, bo, page_flags, ttm_cached, 0);
if (ret < 0)
goto err_ttm_tt_init;
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
-#include <drm/drm_fb_helper.h>
#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
struct exynos_dsi {
struct drm_encoder encoder;
struct mipi_dsi_host dsi_host;
- struct drm_connector connector;
- struct drm_panel *panel;
- struct list_head bridge_chain;
+ struct drm_bridge bridge;
struct drm_bridge *out_bridge;
struct device *dev;
struct drm_display_mode mode;
};
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
-#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
-static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
+static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b)
{
- return container_of(e, struct exynos_dsi, encoder);
+ return container_of(b, struct exynos_dsi, bridge);
}
enum reg_idx {
int ret;
int te_gpio_irq;
- dsi->te_gpio = devm_gpiod_get_optional(dsi->dev, "te", GPIOD_IN);
- if (IS_ERR(dsi->te_gpio)) {
+ dsi->te_gpio = gpiod_get_optional(panel, "te", GPIOD_IN);
+ if (!dsi->te_gpio) {
+ return 0;
+ } else if (IS_ERR(dsi->te_gpio)) {
dev_err(dsi->dev, "gpio request failed with %ld\n",
PTR_ERR(dsi->te_gpio));
return PTR_ERR(dsi->te_gpio);
}
}
-static void exynos_dsi_enable(struct drm_encoder *encoder)
+static void exynos_dsi_atomic_pre_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
{
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct drm_bridge *iter;
+ struct exynos_dsi *dsi = bridge_to_dsi(bridge);
int ret;
if (dsi->state & DSIM_STATE_ENABLED)
}
dsi->state |= DSIM_STATE_ENABLED;
+}
- if (dsi->panel) {
- ret = drm_panel_prepare(dsi->panel);
- if (ret < 0)
- goto err_put_sync;
- } else {
- list_for_each_entry_reverse(iter, &dsi->bridge_chain,
- chain_node) {
- if (iter->funcs->pre_enable)
- iter->funcs->pre_enable(iter);
- }
- }
+static void exynos_dsi_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct exynos_dsi *dsi = bridge_to_dsi(bridge);
exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true);
- if (dsi->panel) {
- ret = drm_panel_enable(dsi->panel);
- if (ret < 0)
- goto err_display_disable;
- } else {
- list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->enable)
- iter->funcs->enable(iter);
- }
- }
-
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
- return;
-err_display_disable:
- exynos_dsi_set_display_enable(dsi, false);
- drm_panel_unprepare(dsi->panel);
-
-err_put_sync:
- dsi->state &= ~DSIM_STATE_ENABLED;
- pm_runtime_put(dsi->dev);
+ return;
}
-static void exynos_dsi_disable(struct drm_encoder *encoder)
+static void exynos_dsi_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
{
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct drm_bridge *iter;
+ struct exynos_dsi *dsi = bridge_to_dsi(bridge);
if (!(dsi->state & DSIM_STATE_ENABLED))
return;
dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+}
- drm_panel_disable(dsi->panel);
-
- list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->disable)
- iter->funcs->disable(iter);
- }
+static void exynos_dsi_atomic_post_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct exynos_dsi *dsi = bridge_to_dsi(bridge);
exynos_dsi_set_display_enable(dsi, false);
- drm_panel_unprepare(dsi->panel);
-
- list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->post_disable)
- iter->funcs->post_disable(iter);
- }
dsi->state &= ~DSIM_STATE_ENABLED;
pm_runtime_put_sync(dsi->dev);
}
-static void exynos_dsi_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void exynos_dsi_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
{
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+ struct exynos_dsi *dsi = bridge_to_dsi(bridge);
drm_mode_copy(&dsi->mode, adjusted_mode);
}
-static enum drm_connector_status
-exynos_dsi_detect(struct drm_connector *connector, bool force)
-{
- return connector->status;
-}
-
-static void exynos_dsi_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- connector->dev = NULL;
-}
-
-static const struct drm_connector_funcs exynos_dsi_connector_funcs = {
- .detect = exynos_dsi_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = exynos_dsi_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int exynos_dsi_get_modes(struct drm_connector *connector)
+static int exynos_dsi_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
{
- struct exynos_dsi *dsi = connector_to_dsi(connector);
+ struct exynos_dsi *dsi = bridge_to_dsi(bridge);
- if (dsi->panel)
- return drm_panel_get_modes(dsi->panel, connector);
-
- return 0;
+ return drm_bridge_attach(bridge->encoder, dsi->out_bridge, NULL, flags);
}
-static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
- .get_modes = exynos_dsi_get_modes,
-};
-
-static int exynos_dsi_create_connector(struct drm_encoder *encoder)
-{
- struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct drm_connector *connector = &dsi->connector;
- struct drm_device *drm = encoder->dev;
- int ret;
-
- connector->polled = DRM_CONNECTOR_POLL_HPD;
-
- ret = drm_connector_init(drm, connector, &exynos_dsi_connector_funcs,
- DRM_MODE_CONNECTOR_DSI);
- if (ret) {
- DRM_DEV_ERROR(dsi->dev,
- "Failed to initialize connector with drm\n");
- return ret;
- }
-
- connector->status = connector_status_disconnected;
- drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
- drm_connector_attach_encoder(connector, encoder);
- if (!drm->registered)
- return 0;
-
- connector->funcs->reset(connector);
- drm_connector_register(connector);
- return 0;
-}
-
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
- .enable = exynos_dsi_enable,
- .disable = exynos_dsi_disable,
- .mode_set = exynos_dsi_mode_set,
+static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_pre_enable = exynos_dsi_atomic_pre_enable,
+ .atomic_enable = exynos_dsi_atomic_enable,
+ .atomic_disable = exynos_dsi_atomic_disable,
+ .atomic_post_disable = exynos_dsi_atomic_post_disable,
+ .mode_set = exynos_dsi_mode_set,
+ .attach = exynos_dsi_attach,
};
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
struct mipi_dsi_device *device)
{
struct exynos_dsi *dsi = host_to_dsi(host);
+ struct device *dev = dsi->dev;
struct drm_encoder *encoder = &dsi->encoder;
struct drm_device *drm = encoder->dev;
- struct drm_bridge *out_bridge;
-
- out_bridge = of_drm_find_bridge(device->dev.of_node);
- if (out_bridge) {
- drm_bridge_attach(encoder, out_bridge, NULL, 0);
- dsi->out_bridge = out_bridge;
- list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
- } else {
- int ret = exynos_dsi_create_connector(encoder);
-
- if (ret) {
- DRM_DEV_ERROR(dsi->dev,
- "failed to create connector ret = %d\n",
- ret);
- drm_encoder_cleanup(encoder);
- return ret;
- }
+ int ret;
- dsi->panel = of_drm_find_panel(device->dev.of_node);
- if (IS_ERR(dsi->panel))
- dsi->panel = NULL;
- else
- dsi->connector.status = connector_status_connected;
+ dsi->out_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+ if (IS_ERR(dsi->out_bridge)) {
+ ret = PTR_ERR(dsi->out_bridge);
+ DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
+ return ret;
}
+ DRM_DEV_INFO(dev, "Attached %s device\n", device->name);
+
+ drm_bridge_add(&dsi->bridge);
+
+ drm_bridge_attach(encoder, &dsi->bridge, NULL, 0);
+
/*
* This is a temporary solution and should be made by more generic way.
*
* TE interrupt handler.
*/
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
- int ret = exynos_dsi_register_te_irq(dsi, &device->dev);
+ ret = exynos_dsi_register_te_irq(dsi, &device->dev);
if (ret)
return ret;
}
struct exynos_dsi *dsi = host_to_dsi(host);
struct drm_device *drm = dsi->encoder.dev;
- if (dsi->panel) {
- mutex_lock(&drm->mode_config.mutex);
- exynos_dsi_disable(&dsi->encoder);
- dsi->panel = NULL;
- dsi->connector.status = connector_status_disconnected;
- mutex_unlock(&drm->mode_config.mutex);
- } else {
- if (dsi->out_bridge->funcs->detach)
- dsi->out_bridge->funcs->detach(dsi->out_bridge);
- dsi->out_bridge = NULL;
- INIT_LIST_HEAD(&dsi->bridge_chain);
- }
+ if (dsi->out_bridge->funcs->detach)
+ dsi->out_bridge->funcs->detach(dsi->out_bridge);
+ dsi->out_bridge = NULL;
if (drm->mode_config.poll_enabled)
drm_kms_helper_hotplug_event(drm);
exynos_dsi_unregister_te_irq(dsi);
+ drm_bridge_remove(&dsi->bridge);
+
return 0;
}
return ret;
}
-enum {
- DSI_PORT_IN,
- DSI_PORT_OUT
-};
-
static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
{
struct device *dev = dsi->dev;
struct exynos_dsi *dsi = dev_get_drvdata(dev);
struct drm_encoder *encoder = &dsi->encoder;
struct drm_device *drm_dev = data;
- struct device_node *in_bridge_node;
- struct drm_bridge *in_bridge;
int ret;
drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
- drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
-
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
if (ret < 0)
return ret;
- in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
- if (in_bridge_node) {
- in_bridge = of_drm_find_bridge(in_bridge_node);
- if (in_bridge)
- drm_bridge_attach(encoder, in_bridge, NULL, 0);
- of_node_put(in_bridge_node);
- }
-
return mipi_dsi_host_register(&dsi->dsi_host);
}
void *data)
{
struct exynos_dsi *dsi = dev_get_drvdata(dev);
- struct drm_encoder *encoder = &dsi->encoder;
- exynos_dsi_disable(encoder);
+ exynos_dsi_atomic_disable(&dsi->bridge, NULL);
mipi_dsi_host_unregister(&dsi->dsi_host);
}
init_completion(&dsi->completed);
spin_lock_init(&dsi->transfer_lock);
INIT_LIST_HEAD(&dsi->transfer_list);
- INIT_LIST_HEAD(&dsi->bridge_chain);
dsi->dsi_host.ops = &exynos_dsi_ops;
dsi->dsi_host.dev = dev;
pm_runtime_enable(dev);
+ dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
+ dsi->bridge.of_node = dev->of_node;
+ dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
+
ret = component_add(dev, &exynos_dsi_component_ops);
if (ret)
goto err_disable_runtime;
* Copyright © 2021 Intel Corporation
*/
+ #include <linux/shmem_fs.h>
+
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_placement.h>
+ #include <drm/drm_buddy.h>
#include "i915_drv.h"
+ #include "i915_ttm_buddy_manager.h"
#include "intel_memory_region.h"
#include "intel_region_ttm.h"
#define I915_TTM_PRIO_PURGE 0
#define I915_TTM_PRIO_NO_PAGES 1
#define I915_TTM_PRIO_HAS_PAGES 2
+ #define I915_TTM_PRIO_NEEDS_CPU_ACCESS 3
/*
* Size of struct ttm_place vector in on-stack struct ttm_placement allocs
place->mem_type = intel_region_to_ttm_type(mr);
if (flags & I915_BO_ALLOC_CONTIGUOUS)
- place->flags = TTM_PL_FLAG_CONTIGUOUS;
+ place->flags |= TTM_PL_FLAG_CONTIGUOUS;
+ if (mr->io_size && mr->io_size < mr->total) {
+ if (flags & I915_BO_ALLOC_GPU_ONLY) {
+ place->flags |= TTM_PL_FLAG_TOPDOWN;
+ } else {
+ place->fpfn = 0;
+ place->lpfn = mr->io_size >> PAGE_SHIFT;
+ }
+ }
}
static void
i915_tt->is_shmem = true;
}
- ret = ttm_tt_init(&i915_tt->ttm, bo, page_flags, caching);
+ ret = ttm_tt_init(&i915_tt->ttm, bo, page_flags, caching, 0);
if (ret)
goto err_free;
const struct ttm_place *place)
{
struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
+ struct ttm_resource *res = bo->resource;
if (!obj)
return false;
return false;
/* Will do for now. Our pinned objects are still on TTM's LRU lists */
- return i915_gem_object_evictable(obj);
+ if (!i915_gem_object_evictable(obj))
+ return false;
+
+ switch (res->mem_type) {
+ case I915_PL_LMEM0: {
+ struct ttm_resource_manager *man =
+ ttm_manager_type(bo->bdev, res->mem_type);
+ struct i915_ttm_buddy_resource *bman_res =
+ to_ttm_buddy_resource(res);
+ struct drm_buddy *mm = bman_res->mm;
+ struct drm_buddy_block *block;
+
+ if (!place->fpfn && !place->lpfn)
+ return true;
+
+ GEM_BUG_ON(!place->lpfn);
+
+ /*
+ * If we just want something mappable then we can quickly check
+ * if the current victim resource is using any of the CPU
+ * visible portion.
+ */
+ if (!place->fpfn &&
+ place->lpfn == i915_ttm_buddy_man_visible_size(man))
+ return bman_res->used_visible_size > 0;
+
+ /* Real range allocation */
+ list_for_each_entry(block, &bman_res->blocks, link) {
+ unsigned long fpfn =
+ drm_buddy_block_offset(block) >> PAGE_SHIFT;
+ unsigned long lpfn = fpfn +
+ (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
+
+ if (place->fpfn < lpfn && place->lpfn > fpfn)
+ return true;
+ }
+ return false;
+ } default:
+ break;
+ }
+
+ return true;
}
static void i915_ttm_evict_flags(struct ttm_buffer_object *bo,
return 0;
}
- static int i915_ttm_shrinker_release_pages(struct drm_i915_gem_object *obj,
- bool no_wait_gpu,
- bool should_writeback)
+ static int i915_ttm_shrink(struct drm_i915_gem_object *obj, unsigned int flags)
{
struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
struct i915_ttm_tt *i915_tt =
container_of(bo->ttm, typeof(*i915_tt), ttm);
struct ttm_operation_ctx ctx = {
.interruptible = true,
- .no_wait_gpu = no_wait_gpu,
+ .no_wait_gpu = flags & I915_GEM_OBJECT_SHRINK_NO_GPU_WAIT,
};
struct ttm_placement place = {};
int ret;
return ret;
}
- if (should_writeback)
+ if (flags & I915_GEM_OBJECT_SHRINK_WRITEBACK)
__shmem_writeback(obj->base.size, i915_tt->filp->f_mapping);
return 0;
i915_ttm_purge(obj);
}
+ static bool i915_ttm_resource_mappable(struct ttm_resource *res)
+ {
+ struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
+
+ if (!i915_ttm_cpu_maps_iomem(res))
+ return true;
+
+ return bman_res->used_visible_size == bman_res->base.num_pages;
+ }
+
static int i915_ttm_io_mem_reserve(struct ttm_device *bdev, struct ttm_resource *mem)
{
if (!i915_ttm_cpu_maps_iomem(mem))
return 0;
+ if (!i915_ttm_resource_mappable(mem))
+ return -EINVAL;
+
mem->bus.caching = ttm_write_combined;
mem->bus.is_iomem = true;
* Gem forced migration using the i915_ttm_migrate() op, is allowed even
* to regions that are not in the object's list of allowable placements.
*/
- static int i915_ttm_migrate(struct drm_i915_gem_object *obj,
- struct intel_memory_region *mr)
+ static int __i915_ttm_migrate(struct drm_i915_gem_object *obj,
+ struct intel_memory_region *mr,
+ unsigned int flags)
{
struct ttm_place requested;
struct ttm_placement placement;
int ret;
- i915_ttm_place_from_region(mr, &requested, obj->flags);
+ i915_ttm_place_from_region(mr, &requested, flags);
placement.num_placement = 1;
placement.num_busy_placement = 1;
placement.placement = &requested;
return 0;
}
+ static int i915_ttm_migrate(struct drm_i915_gem_object *obj,
+ struct intel_memory_region *mr)
+ {
+ return __i915_ttm_migrate(obj, mr, obj->flags);
+ }
+
static void i915_ttm_put_pages(struct drm_i915_gem_object *obj,
struct sg_table *st)
{
} else if (obj->mm.madv != I915_MADV_WILLNEED) {
bo->priority = I915_TTM_PRIO_PURGE;
} else if (!i915_gem_object_has_pages(obj)) {
- if (bo->priority < I915_TTM_PRIO_HAS_PAGES)
- bo->priority = I915_TTM_PRIO_HAS_PAGES;
+ bo->priority = I915_TTM_PRIO_NO_PAGES;
} else {
- if (bo->priority > I915_TTM_PRIO_NO_PAGES)
- bo->priority = I915_TTM_PRIO_NO_PAGES;
+ struct ttm_resource_manager *man =
+ ttm_manager_type(bo->bdev, bo->resource->mem_type);
+
+ /*
+ * If we need to place an LMEM resource which doesn't need CPU
+ * access then we should try not to victimize mappable objects
+ * first, since we likely end up stealing more of the mappable
+ * portion. And likewise when we try to find space for a mappble
+ * object, we know not to ever victimize objects that don't
+ * occupy any mappable pages.
+ */
+ if (i915_ttm_cpu_maps_iomem(bo->resource) &&
+ i915_ttm_buddy_man_visible_size(man) < man->size &&
+ !(obj->flags & I915_BO_ALLOC_GPU_ONLY))
+ bo->priority = I915_TTM_PRIO_NEEDS_CPU_ACCESS;
+ else
+ bo->priority = I915_TTM_PRIO_HAS_PAGES;
}
- ttm_bo_move_to_lru_tail(bo, bo->resource, NULL);
+ ttm_bo_move_to_lru_tail(bo);
spin_unlock(&bo->bdev->lru_lock);
}
return VM_FAULT_SIGBUS;
}
+ if (!i915_ttm_resource_mappable(bo->resource)) {
+ int err = -ENODEV;
+ int i;
+
+ for (i = 0; i < obj->mm.n_placements; i++) {
+ struct intel_memory_region *mr = obj->mm.placements[i];
+ unsigned int flags;
+
+ if (!mr->io_size && mr->type != INTEL_MEMORY_SYSTEM)
+ continue;
+
+ flags = obj->flags;
+ flags &= ~I915_BO_ALLOC_GPU_ONLY;
+ err = __i915_ttm_migrate(obj, mr, flags);
+ if (!err)
+ break;
+ }
+
+ if (err) {
+ drm_dbg(dev, "Unable to make resource CPU accessible\n");
+ dma_resv_unlock(bo->base.resv);
+ return VM_FAULT_SIGBUS;
+ }
+ }
+
if (drm_dev_enter(dev, &idx)) {
ret = ttm_bo_vm_fault_reserved(vmf, vmf->vma->vm_page_prot,
TTM_BO_VM_NUM_PREFAULT);
.get_pages = i915_ttm_get_pages,
.put_pages = i915_ttm_put_pages,
.truncate = i915_ttm_truncate,
- .shrinker_release_pages = i915_ttm_shrinker_release_pages,
+ .shrink = i915_ttm_shrink,
.adjust_lru = i915_ttm_adjust_lru,
.delayed_free = i915_ttm_delayed_free,
mr = intel_memory_region_create(i915, 0,
totalram_pages() << PAGE_SHIFT,
- PAGE_SIZE, 0,
+ PAGE_SIZE, 0, 0,
type, instance,
&ttm_system_region_ops);
if (IS_ERR(mr))
DRM_WARN("HFP + HBP less than d-phy, FPS will under 60Hz\n");
}
+ if ((dsi->mode_flags & MIPI_DSI_HS_PKT_END_ALIGNED) &&
+ (dsi->lanes == 4)) {
+ horizontal_sync_active_byte =
+ roundup(horizontal_sync_active_byte, dsi->lanes) - 2;
+ horizontal_frontporch_byte =
+ roundup(horizontal_frontporch_byte, dsi->lanes) - 2;
+ horizontal_backporch_byte =
+ roundup(horizontal_backporch_byte, dsi->lanes) - 2;
+ horizontal_backporch_byte -=
+ (vm->hactive * dsi_tmp_buf_bpp + 2) % dsi->lanes;
+ }
+
writel(horizontal_sync_active_byte, dsi->regs + DSI_HSA_WC);
writel(horizontal_backporch_byte, dsi->regs + DSI_HBP_WC);
writel(horizontal_frontporch_byte, dsi->regs + DSI_HFP_WC);
mtk_dsi_poweroff(dsi);
}
+ 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);
+ if (ret) {
+ DRM_ERROR("Failed to encoder init to drm\n");
+ return ret;
+ }
+
+ dsi->encoder.possible_crtcs = mtk_drm_find_possible_crtc_by_comp(drm, dsi->host.dev);
+
+ ret = drm_bridge_attach(&dsi->encoder, &dsi->bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret)
+ goto err_cleanup_encoder;
+
+ dsi->connector = drm_bridge_connector_init(drm, &dsi->encoder);
+ if (IS_ERR(dsi->connector)) {
+ DRM_ERROR("Unable to create bridge connector\n");
+ ret = PTR_ERR(dsi->connector);
+ goto err_cleanup_encoder;
+ }
+ drm_connector_attach_encoder(dsi->connector, &dsi->encoder);
+
+ return 0;
+
+ err_cleanup_encoder:
+ drm_encoder_cleanup(&dsi->encoder);
+ return ret;
+ }
+
+ static int mtk_dsi_bind(struct device *dev, struct device *master, void *data)
+ {
+ int ret;
+ struct drm_device *drm = data;
+ struct mtk_dsi *dsi = dev_get_drvdata(dev);
+
+ ret = mtk_dsi_encoder_init(drm, dsi);
+ if (ret)
+ return ret;
+
+ return device_reset_optional(dev);
+ }
+
+ static void mtk_dsi_unbind(struct device *dev, struct device *master,
+ void *data)
+ {
+ struct mtk_dsi *dsi = dev_get_drvdata(dev);
+
+ drm_encoder_cleanup(&dsi->encoder);
+ }
+
+ static const struct component_ops mtk_dsi_component_ops = {
+ .bind = mtk_dsi_bind,
+ .unbind = mtk_dsi_unbind,
+ };
+
static int mtk_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct mtk_dsi *dsi = host_to_dsi(host);
+ struct device *dev = host->dev;
+ int ret;
dsi->lanes = device->lanes;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
+ dsi->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
+ if (IS_ERR(dsi->next_bridge))
+ return PTR_ERR(dsi->next_bridge);
+
+ drm_bridge_add(&dsi->bridge);
+
+ ret = component_add(host->dev, &mtk_dsi_component_ops);
+ if (ret) {
+ DRM_ERROR("failed to add dsi_host component: %d\n", ret);
+ drm_bridge_remove(&dsi->bridge);
+ return ret;
+ }
return 0;
}
+ static int mtk_dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+ {
+ struct mtk_dsi *dsi = host_to_dsi(host);
+
+ component_del(host->dev, &mtk_dsi_component_ops);
+ drm_bridge_remove(&dsi->bridge);
+ return 0;
+ }
+
static void mtk_dsi_wait_for_idle(struct mtk_dsi *dsi)
{
int ret;
u8 read_data[16];
void *src_addr;
u8 irq_flag = CMD_DONE_INT_FLAG;
+ u32 dsi_mode;
+ int ret;
- if (readl(dsi->regs + DSI_MODE_CTRL) & MODE) {
- DRM_ERROR("dsi engine is not command mode\n");
- return -EINVAL;
+ dsi_mode = readl(dsi->regs + DSI_MODE_CTRL);
+ if (dsi_mode & MODE) {
+ mtk_dsi_stop(dsi);
+ ret = mtk_dsi_switch_to_cmd_mode(dsi, VM_DONE_INT_FLAG, 500);
+ if (ret)
+ goto restore_dsi_mode;
}
if (MTK_DSI_HOST_IS_READ(msg->type))
irq_flag |= LPRX_RD_RDY_INT_FLAG;
- if (mtk_dsi_host_send_cmd(dsi, msg, irq_flag) < 0)
- return -ETIME;
+ ret = mtk_dsi_host_send_cmd(dsi, msg, irq_flag);
+ if (ret)
+ goto restore_dsi_mode;
- if (!MTK_DSI_HOST_IS_READ(msg->type))
- return 0;
+ if (!MTK_DSI_HOST_IS_READ(msg->type)) {
+ recv_cnt = 0;
+ goto restore_dsi_mode;
+ }
if (!msg->rx_buf) {
DRM_ERROR("dsi receive buffer size may be NULL\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto restore_dsi_mode;
}
for (i = 0; i < 16; i++)
DRM_INFO("dsi get %d byte data from the panel address(0x%x)\n",
recv_cnt, *((u8 *)(msg->tx_buf)));
- return recv_cnt;
+ restore_dsi_mode:
+ if (dsi_mode & MODE) {
+ mtk_dsi_set_mode(dsi);
+ mtk_dsi_start(dsi);
+ }
+
+ return ret < 0 ? ret : recv_cnt;
}
static const struct mipi_dsi_host_ops mtk_dsi_ops = {
.attach = mtk_dsi_host_attach,
+ .detach = mtk_dsi_host_detach,
.transfer = mtk_dsi_host_transfer,
};
- 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);
- if (ret) {
- DRM_ERROR("Failed to encoder init to drm\n");
- return ret;
- }
-
- dsi->encoder.possible_crtcs = mtk_drm_find_possible_crtc_by_comp(drm, dsi->host.dev);
-
- ret = drm_bridge_attach(&dsi->encoder, &dsi->bridge, NULL,
- DRM_BRIDGE_ATTACH_NO_CONNECTOR);
- if (ret)
- goto err_cleanup_encoder;
-
- dsi->connector = drm_bridge_connector_init(drm, &dsi->encoder);
- if (IS_ERR(dsi->connector)) {
- DRM_ERROR("Unable to create bridge connector\n");
- ret = PTR_ERR(dsi->connector);
- goto err_cleanup_encoder;
- }
- drm_connector_attach_encoder(dsi->connector, &dsi->encoder);
-
- return 0;
-
- err_cleanup_encoder:
- drm_encoder_cleanup(&dsi->encoder);
- return ret;
- }
-
- static int mtk_dsi_bind(struct device *dev, struct device *master, void *data)
- {
- int ret;
- struct drm_device *drm = data;
- struct mtk_dsi *dsi = dev_get_drvdata(dev);
-
- ret = mtk_dsi_encoder_init(drm, dsi);
- if (ret)
- return ret;
-
- return device_reset_optional(dev);
- }
-
- static void mtk_dsi_unbind(struct device *dev, struct device *master,
- void *data)
- {
- struct mtk_dsi *dsi = dev_get_drvdata(dev);
-
- drm_encoder_cleanup(&dsi->encoder);
- }
-
- static const struct component_ops mtk_dsi_component_ops = {
- .bind = mtk_dsi_bind,
- .unbind = mtk_dsi_unbind,
- };
-
static int mtk_dsi_probe(struct platform_device *pdev)
{
struct mtk_dsi *dsi;
return ret;
}
- dsi->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
- if (IS_ERR(dsi->next_bridge)) {
- ret = PTR_ERR(dsi->next_bridge);
- goto err_unregister_host;
- }
-
dsi->driver_data = of_device_get_match_data(dev);
dsi->engine_clk = devm_clk_get(dev, "engine");
dsi->bridge.of_node = dev->of_node;
dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
- drm_bridge_add(&dsi->bridge);
-
- ret = component_add(&pdev->dev, &mtk_dsi_component_ops);
- if (ret) {
- dev_err(&pdev->dev, "failed to add component: %d\n", ret);
- goto err_unregister_host;
- }
-
return 0;
err_unregister_host:
struct mtk_dsi *dsi = platform_get_drvdata(pdev);
mtk_output_dsi_disable(dsi);
- drm_bridge_remove(&dsi->bridge);
- component_del(&pdev->dev, &mtk_dsi_component_ops);
mipi_dsi_host_unregister(&dsi->host);
return 0;
},
.attrs = (const struct soc_device_attribute []) {
{ .soc_id = "GXL (S805*)", },
- { /* sentinel */ },
+ { /* sentinel */ }
}
},
};
return drm_mode_config_helper_resume(priv->drm);
}
- static int compare_of(struct device *dev, void *data)
- {
- DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n",
- dev->of_node, data);
-
- return dev->of_node == data;
- }
-
static void meson_drv_shutdown(struct platform_device *pdev)
{
struct meson_drm *priv = dev_get_drvdata(&pdev->dev);
dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n",
np, remote, dev_name(&pdev->dev));
- component_match_add(&pdev->dev, &match, compare_of, remote);
+ component_match_add(&pdev->dev, &match, component_compare_of, remote);
of_node_put(remote);
#define SSD130X_CONTRAST 0x81
#define SSD130X_SET_LOOKUP_TABLE 0x91
#define SSD130X_CHARGE_PUMP 0x8d
-#define SSD130X_SEG_REMAP_ON 0xa1
+#define SSD130X_SET_SEG_REMAP 0xa0
#define SSD130X_DISPLAY_OFF 0xae
#define SSD130X_SET_MULTIPLEX_RATIO 0xa8
#define SSD130X_DISPLAY_ON 0xaf
#define SSD130X_SET_COM_PINS_CONFIG 0xda
#define SSD130X_SET_VCOMH 0xdb
-#define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 2)
+#define SSD130X_SET_SEG_REMAP_MASK GENMASK(0, 0)
+#define SSD130X_SET_SEG_REMAP_SET(val) FIELD_PREP(SSD130X_SET_SEG_REMAP_MASK, (val))
+#define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 3)
#define SSD130X_SET_COM_SCAN_DIR_SET(val) FIELD_PREP(SSD130X_SET_COM_SCAN_DIR_MASK, (val))
#define SSD130X_SET_CLOCK_DIV_MASK GENMASK(3, 0)
#define SSD130X_SET_CLOCK_DIV_SET(val) FIELD_PREP(SSD130X_SET_CLOCK_DIV_MASK, (val))
static int ssd130x_init(struct ssd130x_device *ssd130x)
{
- u32 precharge, dclk, com_invdir, compins, chargepump;
+ u32 precharge, dclk, com_invdir, compins, chargepump, seg_remap;
int ret;
/* Set initial contrast */
return ret;
/* Set segment re-map */
- if (ssd130x->seg_remap) {
- ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_SEG_REMAP_ON);
- if (ret < 0)
- return ret;
- }
+ seg_remap = (SSD130X_SET_SEG_REMAP |
+ SSD130X_SET_SEG_REMAP_SET(ssd130x->seg_remap));
+ ret = ssd130x_write_cmd(ssd130x, 1, seg_remap);
+ if (ret < 0)
+ return ret;
/* Set COM direction */
com_invdir = (SSD130X_SET_COM_SCAN_DIR |
unsigned int width = drm_rect_width(rect);
unsigned int height = drm_rect_height(rect);
unsigned int line_length = DIV_ROUND_UP(width, 8);
- unsigned int pages = DIV_ROUND_UP(y % 8 + height, 8);
+ unsigned int pages = DIV_ROUND_UP(height, 8);
+ struct drm_device *drm = &ssd130x->drm;
u32 array_idx = 0;
int ret, i, j, k;
u8 *data_array = NULL;
+ drm_WARN_ONCE(drm, y % 8 != 0, "y must be aligned to screen page\n");
+
data_array = kcalloc(width, pages, GFP_KERNEL);
if (!data_array)
return -ENOMEM;
if (ret < 0)
goto out_free;
- for (i = y / 8; i < y / 8 + pages; i++) {
+ for (i = 0; i < pages; i++) {
int m = 8;
/* Last page may be partial */
- if (8 * (i + 1) > ssd130x->height)
+ if (8 * (y / 8 + i + 1) > ssd130x->height)
m = ssd130x->height % 8;
- for (j = x; j < x + width; j++) {
+ for (j = 0; j < width; j++) {
u8 data = 0;
for (k = 0; k < m; k++) {
.y2 = ssd130x->height,
};
- buf = kcalloc(ssd130x->width, ssd130x->height, GFP_KERNEL);
+ buf = kcalloc(DIV_ROUND_UP(ssd130x->width, 8), ssd130x->height,
+ GFP_KERNEL);
if (!buf)
return;
kfree(buf);
}
- static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf_map *map,
+ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *map,
struct drm_rect *rect)
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */
+ unsigned int dst_pitch;
int ret = 0;
u8 *buf = NULL;
- buf = kcalloc(fb->width, fb->height, GFP_KERNEL);
+ /* Align y to display page boundaries */
+ rect->y1 = round_down(rect->y1, 8);
+ rect->y2 = min_t(unsigned int, round_up(rect->y2, 8), ssd130x->height);
+
+ dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
+ buf = kcalloc(dst_pitch, drm_rect_height(rect), GFP_KERNEL);
if (!buf)
return -ENOMEM;
- drm_fb_xrgb8888_to_mono_reversed(buf, 0, vmap, fb, rect);
+ drm_fb_xrgb8888_to_mono(buf, dst_pitch, vmap, fb, rect);
ssd130x_update_rect(ssd130x, buf, rect);
if (ret)
goto out_free;
- drm_fb_xrgb8888_to_mono_reversed(buf, 0, cma_obj->vaddr, fb, &clip);
+ drm_fb_xrgb8888_to_mono(buf, 0, cma_obj->vaddr, fb, &clip);
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
return 0;
}
- static int repaper_remove(struct spi_device *spi)
+ static void repaper_remove(struct spi_device *spi)
{
struct drm_device *drm = spi_get_drvdata(spi);
drm_dev_unplug(drm);
drm_atomic_helper_shutdown(drm);
-
- return 0;
}
static void repaper_shutdown(struct spi_device *spi)
#include <drm/ttm/ttm_placement.h>
#include <drm/drm_cache.h>
#include <drm/drm_vma_manager.h>
- #include <linux/dma-buf-map.h>
+ #include <linux/iosys-map.h>
#include <linux/io.h>
#include <linux/highmem.h>
#include <linux/wait.h>
{
const struct ttm_kmap_iter_ops *dst_ops = dst_iter->ops;
const struct ttm_kmap_iter_ops *src_ops = src_iter->ops;
- struct dma_buf_map src_map, dst_map;
+ struct iosys_map src_map, dst_map;
pgoff_t i;
/* Single TTM move. NOP */
atomic_inc(&ttm_glob.bo_count);
INIT_LIST_HEAD(&fbo->base.ddestroy);
- INIT_LIST_HEAD(&fbo->base.lru);
fbo->base.moving = NULL;
drm_vma_node_reset(&fbo->base.base.vma_node);
}
EXPORT_SYMBOL(ttm_bo_kunmap);
- int ttm_bo_vmap(struct ttm_buffer_object *bo, struct dma_buf_map *map)
+ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map)
{
struct ttm_resource *mem = bo->resource;
int ret;
if (!vaddr_iomem)
return -ENOMEM;
- dma_buf_map_set_vaddr_iomem(map, vaddr_iomem);
+ iosys_map_set_vaddr_iomem(map, vaddr_iomem);
} else {
struct ttm_operation_ctx ctx = {
if (!vaddr)
return -ENOMEM;
- dma_buf_map_set_vaddr(map, vaddr);
+ iosys_map_set_vaddr(map, vaddr);
}
return 0;
}
EXPORT_SYMBOL(ttm_bo_vmap);
- void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct dma_buf_map *map)
+ void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map)
{
struct ttm_resource *mem = bo->resource;
- if (dma_buf_map_is_null(map))
+ if (iosys_map_is_null(map))
return;
if (!map->is_iomem)
vunmap(map->vaddr);
else if (!mem->bus.addr)
iounmap(map->vaddr_iomem);
- dma_buf_map_clear(map);
+ iosys_map_clear(map);
ttm_mem_io_free(bo->bdev, bo->resource);
}
* Authors: Christian König
*/
- #include <linux/dma-buf-map.h>
+ #include <linux/iosys-map.h>
#include <linux/io-mapping.h>
#include <linux/scatterlist.h>
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_bo_driver.h>
+/**
+ * ttm_lru_bulk_move_init - initialize a bulk move structure
+ * @bulk: the structure to init
+ *
+ * For now just memset the structure to zero.
+ */
+void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk)
+{
+ memset(bulk, 0, sizeof(*bulk));
+}
+EXPORT_SYMBOL(ttm_lru_bulk_move_init);
+
+/**
+ * ttm_lru_bulk_move_tail - bulk move range of resources to the LRU tail.
+ *
+ * @bulk: bulk move structure
+ *
+ * Bulk move BOs to the LRU tail, only valid to use when driver makes sure that
+ * resource order never changes. Should be called with &ttm_device.lru_lock held.
+ */
+void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk)
+{
+ unsigned i, j;
+
+ for (i = 0; i < TTM_NUM_MEM_TYPES; ++i) {
+ for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) {
+ struct ttm_lru_bulk_move_pos *pos = &bulk->pos[i][j];
+ struct ttm_resource_manager *man;
+
+ if (!pos->first)
+ continue;
+
+ lockdep_assert_held(&pos->first->bo->bdev->lru_lock);
+ dma_resv_assert_held(pos->first->bo->base.resv);
+ dma_resv_assert_held(pos->last->bo->base.resv);
+
+ man = ttm_manager_type(pos->first->bo->bdev, i);
+ list_bulk_move_tail(&man->lru[j], &pos->first->lru,
+ &pos->last->lru);
+ }
+ }
+}
+EXPORT_SYMBOL(ttm_lru_bulk_move_tail);
+
+/* Return the bulk move pos object for this resource */
+static struct ttm_lru_bulk_move_pos *
+ttm_lru_bulk_move_pos(struct ttm_lru_bulk_move *bulk, struct ttm_resource *res)
+{
+ return &bulk->pos[res->mem_type][res->bo->priority];
+}
+
+/* Move the resource to the tail of the bulk move range */
+static void ttm_lru_bulk_move_pos_tail(struct ttm_lru_bulk_move_pos *pos,
+ struct ttm_resource *res)
+{
+ if (pos->last != res) {
+ list_move(&res->lru, &pos->last->lru);
+ pos->last = res;
+ }
+}
+
+/* Add the resource to a bulk_move cursor */
+void ttm_lru_bulk_move_add(struct ttm_lru_bulk_move *bulk,
+ struct ttm_resource *res)
+{
+ struct ttm_lru_bulk_move_pos *pos = ttm_lru_bulk_move_pos(bulk, res);
+
+ if (!pos->first) {
+ pos->first = res;
+ pos->last = res;
+ } else {
+ ttm_lru_bulk_move_pos_tail(pos, res);
+ }
+}
+
+/* Remove the resource from a bulk_move range */
+void ttm_lru_bulk_move_del(struct ttm_lru_bulk_move *bulk,
+ struct ttm_resource *res)
+{
+ struct ttm_lru_bulk_move_pos *pos = ttm_lru_bulk_move_pos(bulk, res);
+
+ if (unlikely(pos->first == res && pos->last == res)) {
+ pos->first = NULL;
+ pos->last = NULL;
+ } else if (pos->first == res) {
+ pos->first = list_next_entry(res, lru);
+ } else if (pos->last == res) {
+ pos->last = list_prev_entry(res, lru);
+ } else {
+ list_move(&res->lru, &pos->last->lru);
+ }
+}
+
+/* Move a resource to the LRU or bulk tail */
+void ttm_resource_move_to_lru_tail(struct ttm_resource *res)
+{
+ struct ttm_buffer_object *bo = res->bo;
+ struct ttm_device *bdev = bo->bdev;
+
+ lockdep_assert_held(&bo->bdev->lru_lock);
+
+ if (bo->pin_count) {
+ list_move_tail(&res->lru, &bdev->pinned);
+
+ } else if (bo->bulk_move) {
+ struct ttm_lru_bulk_move_pos *pos =
+ ttm_lru_bulk_move_pos(bo->bulk_move, res);
+
+ ttm_lru_bulk_move_pos_tail(pos, res);
+ } else {
+ struct ttm_resource_manager *man;
+
+ man = ttm_manager_type(bdev, res->mem_type);
+ list_move_tail(&res->lru, &man->lru[bo->priority]);
+ }
+}
+
/**
* ttm_resource_init - resource object constructure
* @bo: buffer object this resources is allocated for
* @place: placement of the resource
* @res: the resource object to inistilize
*
- * Initialize a new resource object. Counterpart of &ttm_resource_fini.
+ * Initialize a new resource object. Counterpart of ttm_resource_fini().
*/
void ttm_resource_init(struct ttm_buffer_object *bo,
const struct ttm_place *place,
res->bus.is_iomem = false;
res->bus.caching = ttm_cached;
res->bo = bo;
+ INIT_LIST_HEAD(&res->lru);
man = ttm_manager_type(bo->bdev, place->mem_type);
spin_lock(&bo->bdev->lru_lock);
- man->usage += bo->base.size;
+ man->usage += res->num_pages << PAGE_SHIFT;
+ if (bo->bulk_move)
+ ttm_lru_bulk_move_add(bo->bulk_move, res);
+ else
+ ttm_resource_move_to_lru_tail(res);
spin_unlock(&bo->bdev->lru_lock);
}
EXPORT_SYMBOL(ttm_resource_init);
* @res: the resource to clean up
*
* Should be used by resource manager backends to clean up the TTM resource
- * objects before freeing the underlying structure. Counterpart of
- * &ttm_resource_init
+ * objects before freeing the underlying structure. Makes sure the resource is
+ * removed from the LRU before destruction.
+ * Counterpart of ttm_resource_init().
*/
void ttm_resource_fini(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
- spin_lock(&man->bdev->lru_lock);
- man->usage -= res->bo->base.size;
- spin_unlock(&man->bdev->lru_lock);
+ struct ttm_device *bdev = man->bdev;
+
+ spin_lock(&bdev->lru_lock);
+ list_del_init(&res->lru);
+ man->usage -= res->num_pages << PAGE_SHIFT;
+ spin_unlock(&bdev->lru_lock);
}
EXPORT_SYMBOL(ttm_resource_fini);
if (!*res)
return;
+ if (bo->bulk_move) {
+ spin_lock(&bo->bdev->lru_lock);
+ ttm_lru_bulk_move_del(bo->bulk_move, *res);
+ spin_unlock(&bo->bdev->lru_lock);
+ }
+
man = ttm_manager_type(bo->bdev, (*res)->mem_type);
man->func->free(man, *res);
*res = NULL;
}
EXPORT_SYMBOL(ttm_resource_manager_debug);
+/**
+ * ttm_resource_manager_first
+ *
+ * @man: resource manager to iterate over
+ * @cursor: cursor to record the position
+ *
+ * Returns the first resource from the resource manager.
+ */
+struct ttm_resource *
+ttm_resource_manager_first(struct ttm_resource_manager *man,
+ struct ttm_resource_cursor *cursor)
+{
+ struct ttm_resource *res;
+
+ lockdep_assert_held(&man->bdev->lru_lock);
+
+ for (cursor->priority = 0; cursor->priority < TTM_MAX_BO_PRIORITY;
+ ++cursor->priority)
+ list_for_each_entry(res, &man->lru[cursor->priority], lru)
+ return res;
+
+ return NULL;
+}
+
+/**
+ * ttm_resource_manager_next
+ *
+ * @man: resource manager to iterate over
+ * @cursor: cursor to record the position
+ * @res: the current resource pointer
+ *
+ * Returns the next resource from the resource manager.
+ */
+struct ttm_resource *
+ttm_resource_manager_next(struct ttm_resource_manager *man,
+ struct ttm_resource_cursor *cursor,
+ struct ttm_resource *res)
+{
+ lockdep_assert_held(&man->bdev->lru_lock);
+
+ list_for_each_entry_continue(res, &man->lru[cursor->priority], lru)
+ return res;
+
+ for (++cursor->priority; cursor->priority < TTM_MAX_BO_PRIORITY;
+ ++cursor->priority)
+ list_for_each_entry(res, &man->lru[cursor->priority], lru)
+ return res;
+
+ return NULL;
+}
+
static void ttm_kmap_iter_iomap_map_local(struct ttm_kmap_iter *iter,
- struct dma_buf_map *dmap,
+ struct iosys_map *dmap,
pgoff_t i)
{
struct ttm_kmap_iter_iomap *iter_io =
addr = io_mapping_map_local_wc(iter_io->iomap, iter_io->cache.offs +
(((resource_size_t)i - iter_io->cache.i)
<< PAGE_SHIFT));
- dma_buf_map_set_vaddr_iomem(dmap, addr);
+ iosys_map_set_vaddr_iomem(dmap, addr);
}
static void ttm_kmap_iter_iomap_unmap_local(struct ttm_kmap_iter *iter,
- struct dma_buf_map *map)
+ struct iosys_map *map)
{
io_mapping_unmap_local(map->vaddr_iomem);
}
*/
static void ttm_kmap_iter_linear_io_map_local(struct ttm_kmap_iter *iter,
- struct dma_buf_map *dmap,
+ struct iosys_map *dmap,
pgoff_t i)
{
struct ttm_kmap_iter_linear_io *iter_io =
container_of(iter, typeof(*iter_io), base);
*dmap = iter_io->dmap;
- dma_buf_map_incr(dmap, i * PAGE_SIZE);
+ iosys_map_incr(dmap, i * PAGE_SIZE);
}
static const struct ttm_kmap_iter_ops ttm_kmap_iter_linear_io_ops = {
}
if (mem->bus.addr) {
- dma_buf_map_set_vaddr(&iter_io->dmap, mem->bus.addr);
+ iosys_map_set_vaddr(&iter_io->dmap, mem->bus.addr);
iter_io->needs_unmap = false;
} else {
size_t bus_size = (size_t)mem->num_pages << PAGE_SHIFT;
iter_io->needs_unmap = true;
memset(&iter_io->dmap, 0, sizeof(iter_io->dmap));
if (mem->bus.caching == ttm_write_combined)
- dma_buf_map_set_vaddr_iomem(&iter_io->dmap,
- ioremap_wc(mem->bus.offset,
- bus_size));
+ iosys_map_set_vaddr_iomem(&iter_io->dmap,
+ ioremap_wc(mem->bus.offset,
+ bus_size));
else if (mem->bus.caching == ttm_cached)
- dma_buf_map_set_vaddr(&iter_io->dmap,
- memremap(mem->bus.offset, bus_size,
- MEMREMAP_WB |
- MEMREMAP_WT |
- MEMREMAP_WC));
+ iosys_map_set_vaddr(&iter_io->dmap,
+ memremap(mem->bus.offset, bus_size,
+ MEMREMAP_WB |
+ MEMREMAP_WT |
+ MEMREMAP_WC));
/* If uncached requested or if mapping cached or wc failed */
- if (dma_buf_map_is_null(&iter_io->dmap))
- dma_buf_map_set_vaddr_iomem(&iter_io->dmap,
- ioremap(mem->bus.offset,
- bus_size));
+ if (iosys_map_is_null(&iter_io->dmap))
+ iosys_map_set_vaddr_iomem(&iter_io->dmap,
+ ioremap(mem->bus.offset,
+ bus_size));
- if (dma_buf_map_is_null(&iter_io->dmap)) {
+ if (iosys_map_is_null(&iter_io->dmap)) {
ret = -ENOMEM;
goto out_io_free;
}
struct ttm_device *bdev,
struct ttm_resource *mem)
{
- if (iter_io->needs_unmap && dma_buf_map_is_set(&iter_io->dmap)) {
+ if (iter_io->needs_unmap && iosys_map_is_set(&iter_io->dmap)) {
if (iter_io->dmap.is_iomem)
iounmap(iter_io->dmap.vaddr_iomem);
else
static void ttm_tt_init_fields(struct ttm_tt *ttm,
struct ttm_buffer_object *bo,
uint32_t page_flags,
- enum ttm_caching caching)
+ enum ttm_caching caching,
+ unsigned long extra_pages)
{
- ttm->num_pages = PAGE_ALIGN(bo->base.size) >> PAGE_SHIFT;
+ ttm->num_pages = (PAGE_ALIGN(bo->base.size) >> PAGE_SHIFT) + extra_pages;
ttm->caching = ttm_cached;
ttm->page_flags = page_flags;
ttm->dma_address = NULL;
}
int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo,
- uint32_t page_flags, enum ttm_caching caching)
+ uint32_t page_flags, enum ttm_caching caching,
+ unsigned long extra_pages)
{
- ttm_tt_init_fields(ttm, bo, page_flags, caching);
+ ttm_tt_init_fields(ttm, bo, page_flags, caching, extra_pages);
if (ttm_tt_alloc_page_directory(ttm)) {
pr_err("Failed allocating page table\n");
{
int ret;
- ttm_tt_init_fields(ttm, bo, page_flags, caching);
+ ttm_tt_init_fields(ttm, bo, page_flags, caching, 0);
if (page_flags & TTM_TT_FLAG_EXTERNAL)
ret = ttm_sg_tt_alloc_page_directory(ttm);
}
static void ttm_kmap_iter_tt_map_local(struct ttm_kmap_iter *iter,
- struct dma_buf_map *dmap,
+ struct iosys_map *dmap,
pgoff_t i)
{
struct ttm_kmap_iter_tt *iter_tt =
container_of(iter, typeof(*iter_tt), base);
- dma_buf_map_set_vaddr(dmap, kmap_local_page_prot(iter_tt->tt->pages[i],
- iter_tt->prot));
+ iosys_map_set_vaddr(dmap, kmap_local_page_prot(iter_tt->tt->pages[i],
+ iter_tt->prot));
}
static void ttm_kmap_iter_tt_unmap_local(struct ttm_kmap_iter *iter,
- struct dma_buf_map *map)
+ struct iosys_map *map)
{
kunmap_local(map->vaddr);
}
#define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000)
-static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode)
+static const char * const output_format_str[] = {
+ [VC4_HDMI_OUTPUT_RGB] = "RGB",
+ [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0",
+ [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2",
+ [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4",
+};
+
+static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt)
{
- return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK;
+ if (fmt >= ARRAY_SIZE(output_format_str))
+ return "invalid";
+
+ return output_format_str[fmt];
+}
+
+static unsigned long long
+vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
+ unsigned int bpc, enum vc4_hdmi_output_format fmt);
+
+static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
+ unsigned int bpc,
+ enum vc4_hdmi_output_format fmt)
+{
+ unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
+
+ return clock > HDMI_14_MAX_TMDS_CLK;
}
static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi,
if (gpiod_get_value_cansleep(vc4_hdmi->hpd_gpio))
connected = true;
} else {
- unsigned long flags;
- u32 hotplug;
-
- spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
- hotplug = HDMI_READ(HDMI_HOTPLUG);
- spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
-
- if (hotplug & VC4_HDMI_HOTPLUG_CONNECTED)
+ if (vc4_hdmi->variant->hp_detect &&
+ vc4_hdmi->variant->hp_detect(vc4_hdmi))
connected = true;
}
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
- if (vc4_hdmi_mode_needs_scrambling(mode)) {
+ if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) {
drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
}
new_state->base.max_bpc = 8;
new_state->base.max_requested_bpc = 8;
+ new_state->output_format = VC4_HDMI_OUTPUT_RGB;
drm_atomic_helper_connector_tv_reset(connector);
}
if (!new_state)
return NULL;
- new_state->pixel_rate = vc4_state->pixel_rate;
+ new_state->tmds_char_rate = vc4_state->tmds_char_rate;
+ new_state->output_bpc = vc4_state->output_bpc;
+ new_state->output_format = vc4_state->output_format;
__drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
return &new_state->base;
DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
}
+static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
+ enum vc4_hdmi_output_format fmt)
+{
+ switch (fmt) {
+ case VC4_HDMI_OUTPUT_RGB:
+ frame->colorspace = HDMI_COLORSPACE_RGB;
+ break;
+
+ case VC4_HDMI_OUTPUT_YUV420:
+ frame->colorspace = HDMI_COLORSPACE_YUV420;
+ break;
+
+ case VC4_HDMI_OUTPUT_YUV422:
+ frame->colorspace = HDMI_COLORSPACE_YUV422;
+ break;
+
+ case VC4_HDMI_OUTPUT_YUV444:
+ frame->colorspace = HDMI_COLORSPACE_YUV444;
+ break;
+
+ default:
+ break;
+ }
+}
+
static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *cstate = connector->state;
+ struct vc4_hdmi_connector_state *vc4_state =
+ conn_state_to_vc4_hdmi_conn_state(cstate);
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
union hdmi_infoframe frame;
int ret;
HDMI_QUANTIZATION_RANGE_FULL :
HDMI_QUANTIZATION_RANGE_LIMITED);
drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate);
+ vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format);
drm_hdmi_avi_infoframe_bars(&frame.avi, cstate);
vc4_hdmi_write_infoframe(encoder, &frame);
if (!vc4_hdmi_supports_scrambling(encoder, mode))
return;
- if (!vc4_hdmi_mode_needs_scrambling(mode))
+ if (!vc4_hdmi_mode_needs_scrambling(mode,
+ vc4_hdmi->output_bpc,
+ vc4_hdmi->output_format))
return;
drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
{ 0x0000, 0x0000, 0x1b80, 0x0400 },
};
+/*
+ * Conversion between Full Range RGB and Full Range YUV422 using the
+ * BT.709 Colorspace
+ *
+ *
+ * [ 0.181906 0.611804 0.061758 16 ]
+ * [ -0.100268 -0.337232 0.437500 128 ]
+ * [ 0.437500 -0.397386 -0.040114 128 ]
+ *
+ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
+ */
+static const u16 vc5_hdmi_csc_full_rgb_to_limited_yuv422_bt709[3][4] = {
+ { 0x05d2, 0x1394, 0x01fa, 0x0400 },
+ { 0xfccc, 0xf536, 0x0e00, 0x2000 },
+ { 0x0e00, 0xf34a, 0xfeb8, 0x2000 },
+};
+
+/*
+ * Conversion between Full Range RGB and Full Range YUV444 using the
+ * BT.709 Colorspace
+ *
+ * [ -0.100268 -0.337232 0.437500 128 ]
+ * [ 0.437500 -0.397386 -0.040114 128 ]
+ * [ 0.181906 0.611804 0.061758 16 ]
+ *
+ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
+ */
+static const u16 vc5_hdmi_csc_full_rgb_to_limited_yuv444_bt709[3][4] = {
+ { 0xfccc, 0xf536, 0x0e00, 0x2000 },
+ { 0x0e00, 0xf34a, 0xfeb8, 0x2000 },
+ { 0x05d2, 0x1394, 0x01fa, 0x0400 },
+};
+
static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi,
const u16 coeffs[3][4])
{
struct drm_connector_state *state,
const struct drm_display_mode *mode)
{
+ struct vc4_hdmi_connector_state *vc4_state =
+ conn_state_to_vc4_hdmi_conn_state(state);
unsigned long flags;
+ u32 if_cfg = 0;
+ u32 if_xbar = 0x543210;
+ u32 csc_chan_ctl = 0;
u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
VC5_MT_CP_CSC_CTL_MODE);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
- HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
+ switch (vc4_state->output_format) {
+ case VC4_HDMI_OUTPUT_YUV444:
+ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_yuv444_bt709);
+ break;
- if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode))
- vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb);
- else
- vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity);
+ case VC4_HDMI_OUTPUT_YUV422:
+ csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD,
+ VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) |
+ VC5_MT_CP_CSC_CTL_USE_444_TO_422 |
+ VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION;
+
+ csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE,
+ VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP);
+
+ if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY,
+ VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422);
+ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_yuv422_bt709);
+ break;
+
+ case VC4_HDMI_OUTPUT_RGB:
+ if_xbar = 0x354021;
+
+ if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode))
+ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb);
+ else
+ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity);
+ break;
+
+ default:
+ break;
+ }
+
+ HDMI_WRITE(HDMI_VEC_INTERFACE_CFG, if_cfg);
+ HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, if_xbar);
+ HDMI_WRITE(HDMI_CSC_CHANNEL_CTL, csc_chan_ctl);
HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
struct drm_connector_state *state,
struct drm_display_mode *mode)
{
+ const struct vc4_hdmi_connector_state *vc4_state =
+ conn_state_to_vc4_hdmi_conn_state(state);
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
HDMI_WRITE(HDMI_VERTB0, vertb_even);
HDMI_WRITE(HDMI_VERTB1, vertb);
- switch (state->max_bpc) {
+ switch (vc4_state->output_bpc) {
case 12:
gcp = 6;
gcp_en = true;
break;
}
+ /*
+ * YCC422 is always 36-bit and not considered deep colour so
+ * doesn't signal in GCP.
+ */
+ if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) {
+ gcp = 4;
+ gcp_en = false;
+ }
+
reg = HDMI_READ(HDMI_DEEP_COLOR_CONFIG_1);
reg &= ~(VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK |
VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK);
struct vc4_hdmi_connector_state *vc4_conn_state =
conn_state_to_vc4_hdmi_conn_state(conn_state);
struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
- unsigned long pixel_rate = vc4_conn_state->pixel_rate;
+ unsigned long tmds_char_rate = vc4_conn_state->tmds_char_rate;
unsigned long bvb_rate, hsm_rate;
unsigned long flags;
int ret;
* Additionally, the AXI clock needs to be at least 25% of
* pixel clock, but HSM ends up being the limiting factor.
*/
- hsm_rate = max_t(unsigned long, 120000000, (pixel_rate / 100) * 101);
+ hsm_rate = max_t(unsigned long, 120000000, (tmds_char_rate / 100) * 101);
ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
if (ret) {
DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
goto out;
}
- ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate);
+ ret = clk_set_rate(vc4_hdmi->pixel_clock, tmds_char_rate);
if (ret) {
DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
goto err_put_runtime_pm;
vc4_hdmi_cec_update_clk_div(vc4_hdmi);
- if (pixel_rate > 297000000)
+ if (tmds_char_rate > 297000000)
bvb_rate = 300000000;
- else if (pixel_rate > 148500000)
+ else if (tmds_char_rate > 148500000)
bvb_rate = 150000000;
else
bvb_rate = 75000000;
struct drm_connector_state *conn_state)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct vc4_hdmi_connector_state *vc4_state =
+ conn_state_to_vc4_hdmi_conn_state(conn_state);
mutex_lock(&vc4_hdmi->mutex);
drm_mode_copy(&vc4_hdmi->saved_adjusted_mode,
&crtc_state->adjusted_mode);
+ vc4_hdmi->output_bpc = vc4_state->output_bpc;
+ vc4_hdmi->output_format = vc4_state->output_format;
mutex_unlock(&vc4_hdmi->mutex);
}
+static bool
+vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode,
+ unsigned int format, unsigned int bpc)
+{
+ struct drm_device *dev = vc4_hdmi->connector.dev;
+ u8 vic = drm_match_cea_mode(mode);
+
+ if (vic == 1 && bpc != 8) {
+ drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
+ return false;
+ }
+
+ if (!info->is_hdmi &&
+ (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) {
+ drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
+ return false;
+ }
+
+ switch (format) {
+ case VC4_HDMI_OUTPUT_RGB:
+ drm_dbg(dev, "RGB Format, checking the constraints.\n");
+
+ if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
+ return false;
+
+ if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
+ drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
+ return false;
+ }
+
+ if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
+ drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
+ return false;
+ }
+
+ drm_dbg(dev, "RGB format supported in that configuration.\n");
+
+ return true;
+
+ case VC4_HDMI_OUTPUT_YUV422:
+ drm_dbg(dev, "YUV422 format, checking the constraints.\n");
+
+ if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
+ drm_dbg(dev, "Sink doesn't support YUV422.\n");
+ return false;
+ }
+
+ if (bpc != 12) {
+ drm_dbg(dev, "YUV422 only supports 12 bpc.\n");
+ return false;
+ }
+
+ drm_dbg(dev, "YUV422 format supported in that configuration.\n");
+
+ return true;
+
+ case VC4_HDMI_OUTPUT_YUV444:
+ drm_dbg(dev, "YUV444 format, checking the constraints.\n");
+
+ if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) {
+ drm_dbg(dev, "Sink doesn't support YUV444.\n");
+ return false;
+ }
+
+ if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) {
+ drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
+ return false;
+ }
+
+ if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) {
+ drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
+ return false;
+ }
+
+ drm_dbg(dev, "YUV444 format supported in that configuration.\n");
+
+ return true;
+ }
+
+ return false;
+}
+
+static enum drm_mode_status
+vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
+ unsigned long long clock)
+{
+ const struct drm_connector *connector = &vc4_hdmi->connector;
+ const struct drm_display_info *info = &connector->display_info;
+
+ if (clock > vc4_hdmi->variant->max_pixel_clock)
+ return MODE_CLOCK_HIGH;
+
+ if (vc4_hdmi->disable_4kp60 && clock > HDMI_14_MAX_TMDS_CLK)
+ return MODE_CLOCK_HIGH;
+
+ if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000))
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static unsigned long long
+vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
+ unsigned int bpc,
+ enum vc4_hdmi_output_format fmt)
+{
+ unsigned long long clock = mode->clock * 1000;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ clock = clock * 2;
+
+ if (fmt == VC4_HDMI_OUTPUT_YUV422)
+ bpc = 8;
+
+ clock = clock * bpc;
+ do_div(clock, 8);
+
+ return clock;
+}
+
+static int
+vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi,
+ struct vc4_hdmi_connector_state *vc4_state,
+ const struct drm_display_mode *mode,
+ unsigned int bpc, unsigned int fmt)
+{
+ unsigned long long clock;
+
+ clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
+ if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK)
+ return -EINVAL;
+
+ vc4_state->tmds_char_rate = clock;
+
+ return 0;
+}
+
+static int
+vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi,
+ struct vc4_hdmi_connector_state *vc4_state,
+ const struct drm_display_mode *mode,
+ unsigned int bpc)
+{
+ struct drm_device *dev = vc4_hdmi->connector.dev;
+ const struct drm_connector *connector = &vc4_hdmi->connector;
+ const struct drm_display_info *info = &connector->display_info;
+ unsigned int format;
+
+ drm_dbg(dev, "Trying with an RGB output\n");
+
+ format = VC4_HDMI_OUTPUT_RGB;
+ if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
+ int ret;
+
+ ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
+ mode, bpc, format);
+ if (!ret) {
+ vc4_state->output_format = format;
+ return 0;
+ }
+ }
+
+ drm_dbg(dev, "Failed, Trying with an YUV422 output\n");
+
+ format = VC4_HDMI_OUTPUT_YUV422;
+ if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
+ int ret;
+
+ ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
+ mode, bpc, format);
+ if (!ret) {
+ vc4_state->output_format = format;
+ return 0;
+ }
+ }
+
+ drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n");
+
+ return -EINVAL;
+}
+
+static int
+vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi,
+ struct vc4_hdmi_connector_state *vc4_state,
+ const struct drm_display_mode *mode)
+{
+ struct drm_device *dev = vc4_hdmi->connector.dev;
+ struct drm_connector_state *conn_state = &vc4_state->base;
+ unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12);
+ unsigned int bpc;
+ int ret;
+
+ for (bpc = max_bpc; bpc >= 8; bpc -= 2) {
+ drm_dbg(dev, "Trying with a %d bpc output\n", bpc);
+
+ ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state,
+ mode, bpc);
+ if (ret)
+ continue;
+
+ vc4_state->output_bpc = bpc;
+
+ drm_dbg(dev,
+ "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n",
+ mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
+ vc4_state->output_bpc,
+ vc4_hdmi_output_fmt_str(vc4_state->output_format),
+ vc4_state->tmds_char_rate);
+
+ break;
+ }
+
+ return ret;
+}
+
#define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
#define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL
struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- unsigned long long pixel_rate = mode->clock * 1000;
- unsigned long long tmds_rate;
+ unsigned long long tmds_char_rate = mode->clock * 1000;
+ unsigned long long tmds_bit_rate;
+ int ret;
if (vc4_hdmi->variant->unsupported_odd_h_timings &&
+ !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
(mode->hsync_end % 2) || (mode->htotal % 2)))
return -EINVAL;
* bandwidth). Slightly lower the frequency to bring it out of
* the WiFi range.
*/
- tmds_rate = pixel_rate * 10;
+ tmds_bit_rate = tmds_char_rate * 10;
if (vc4_hdmi->disable_wifi_frequencies &&
- (tmds_rate >= WIFI_2_4GHz_CH1_MIN_FREQ &&
- tmds_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) {
+ (tmds_bit_rate >= WIFI_2_4GHz_CH1_MIN_FREQ &&
+ tmds_bit_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) {
mode->clock = 238560;
- pixel_rate = mode->clock * 1000;
+ tmds_char_rate = mode->clock * 1000;
}
- if (conn_state->max_bpc == 12) {
- pixel_rate = pixel_rate * 150;
- do_div(pixel_rate, 100);
- } else if (conn_state->max_bpc == 10) {
- pixel_rate = pixel_rate * 125;
- do_div(pixel_rate, 100);
- }
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- pixel_rate = pixel_rate * 2;
-
- if (pixel_rate > vc4_hdmi->variant->max_pixel_clock)
- return -EINVAL;
-
- if (vc4_hdmi->disable_4kp60 && (pixel_rate > HDMI_14_MAX_TMDS_CLK))
- return -EINVAL;
-
- vc4_state->pixel_rate = pixel_rate;
+ ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode);
+ if (ret)
+ return ret;
return 0;
}
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
if (vc4_hdmi->variant->unsupported_odd_h_timings &&
+ !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
(mode->hsync_end % 2) || (mode->htotal % 2)))
return MODE_H_ILLEGAL;
- if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock)
- return MODE_CLOCK_HIGH;
-
- if (vc4_hdmi->disable_4kp60 && vc4_hdmi_mode_needs_scrambling(mode))
- return MODE_CLOCK_HIGH;
-
- return MODE_OK;
+ return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode->clock * 1000);
}
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
return channel_map;
}
+ static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi)
+ {
+ unsigned long flags;
+ u32 hotplug;
+
+ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+ hotplug = HDMI_READ(HDMI_HOTPLUG);
+ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ return !!(hotplug & VC4_HDMI_HOTPLUG_CONNECTED);
+ }
+
/* HDMI audio codec callbacks */
static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi,
unsigned int samplerate)
dev_err(dev, "Couldn't register the HDMI codec: %ld\n", PTR_ERR(codec_pdev));
return PTR_ERR(codec_pdev);
}
+ vc4_hdmi->audio.codec_pdev = codec_pdev;
dai_link->cpus = &vc4_hdmi->audio.cpu;
dai_link->codecs = &vc4_hdmi->audio.codec;
}
+ static void vc4_hdmi_audio_exit(struct vc4_hdmi *vc4_hdmi)
+ {
+ platform_device_unregister(vc4_hdmi->audio.codec_pdev);
+ vc4_hdmi->audio.codec_pdev = NULL;
+ }
+
static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv)
{
struct vc4_hdmi *vc4_hdmi = priv;
* vc4_hdmi_disable_scrambling() will thus run at boot, make
* sure it's disabled, and avoid any inconsistency.
*/
- vc4_hdmi->scdc_enabled = true;
+ if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK)
+ vc4_hdmi->scdc_enabled = true;
ret = variant->init_resources(vc4_hdmi);
if (ret)
kfree(vc4_hdmi->hdmi_regset.regs);
kfree(vc4_hdmi->hd_regset.regs);
+ vc4_hdmi_audio_exit(vc4_hdmi);
vc4_hdmi_cec_exit(vc4_hdmi);
vc4_hdmi_hotplug_exit(vc4_hdmi);
vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
.phy_rng_disable = vc5_hdmi_phy_rng_disable,
.channel_map = vc5_hdmi_channel_map,
.supports_hdr = true,
+ .hp_detect = vc5_hdmi_hp_detect,
};
static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
.phy_rng_disable = vc5_hdmi_phy_rng_disable,
.channel_map = vc5_hdmi_channel_map,
.supports_hdr = true,
+ .hp_detect = vc5_hdmi_hp_detect,
};
static const struct of_device_id vc4_hdmi_dt_match[] = {
/* Enables HDR metadata */
bool supports_hdr;
+
+ /* Callback for hardware specific hotplug detect */
+ bool (*hp_detect)(struct vc4_hdmi *vc4_hdmi);
};
/* HDMI audio information */
struct snd_soc_dai_link_component platform;
struct snd_dmaengine_dai_dma_data dma_data;
struct hdmi_audio_infoframe infoframe;
+ struct platform_device *codec_pdev;
bool streaming;
};
+enum vc4_hdmi_output_format {
+ VC4_HDMI_OUTPUT_RGB,
+ VC4_HDMI_OUTPUT_YUV422,
+ VC4_HDMI_OUTPUT_YUV444,
+ VC4_HDMI_OUTPUT_YUV420,
+};
+
/* General HDMI hardware state. */
struct vc4_hdmi {
struct vc4_hdmi_audio audio;
* the scrambler on? Protected by @mutex.
*/
bool scdc_enabled;
+
+ /**
+ * @output_bpc: Copy of @vc4_connector_state.output_bpc for use
+ * outside of KMS hooks. Protected by @mutex.
+ */
+ unsigned int output_bpc;
+
+ /**
+ * @output_format: Copy of @vc4_connector_state.output_format
+ * for use outside of KMS hooks. Protected by @mutex.
+ */
+ enum vc4_hdmi_output_format output_format;
};
static inline struct vc4_hdmi *
struct vc4_hdmi_connector_state {
struct drm_connector_state base;
- unsigned long long pixel_rate;
+ unsigned long long tmds_char_rate;
+ unsigned int output_bpc;
+ enum vc4_hdmi_output_format output_format;
};
static inline struct vc4_hdmi_connector_state *
select I2C_ALGOBIT
select I2C
-config FB_BOOT_VESA_SUPPORT
- bool
- depends on FB
- help
- If true, at least one selected framebuffer driver can take advantage
- of VESA video modes set at an early boot stage via the vga= parameter.
-
config FB_CFB_FILLRECT
tristate
depends on FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
- select FB_BOOT_VESA_SUPPORT
+ select SYSFB
help
This is the frame buffer device driver for generic VESA 2.0
compliant graphic cards. The older VESA 1.2 cards are not supported.
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select SYSFB
help
This is the EFI frame buffer device driver. If the firmware on
your platform is EFI 1.10 or UEFI 2.0, select Y to add support for
You can pass several parameters to the driver at boot time or at
module load time. The parameters look like "video=pvr2:XXX", where
the meaning of XXX can be found at the end of the main source file
- (<file:drivers/video/pvr2fb.c>). Please see the file
+ (<file:drivers/video/fbdev/pvr2fb.c>). Please see the file
<file:Documentation/fb/pvr2fb.rst>.
config FB_OPENCORES
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
- select FB_BOOT_VESA_SUPPORT if FB_INTEL = y
+ select BOOT_VESA_SUPPORT if FB_INTEL = y
depends on !DRM_I915
help
This driver supports the on-board graphics built in to the Intel
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
- select FB_BOOT_VESA_SUPPORT if FB_SIS = y
+ select BOOT_VESA_SUPPORT if FB_SIS = y
select FB_SIS_300 if !FB_SIS_315
help
This is the frame buffer device driver for the SiS 300, 315, 330
printk(KERN_ERR "no mapping available\n");
BUG_ON(!page->mapping);
- INIT_LIST_HEAD(&page->lru);
page->index = vmf->pgoff;
vmf->page = page;
.page_mkwrite = fb_deferred_io_mkwrite,
};
- static int fb_deferred_io_set_page_dirty(struct page *page)
- {
- if (!PageDirty(page))
- SetPageDirty(page);
- return 0;
- }
-
static const struct address_space_operations fb_deferred_io_aops = {
- .set_page_dirty = fb_deferred_io_set_page_dirty,
+ .dirty_folio = noop_dirty_folio,
};
int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
void fb_deferred_io_init(struct fb_info *info)
{
struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct page *page;
+ unsigned int i;
BUG_ON(!fbdefio);
mutex_init(&fbdefio->lock);
INIT_LIST_HEAD(&fbdefio->pagelist);
if (fbdefio->delay == 0) /* set a default of 1 s */
fbdefio->delay = HZ;
+
+ /* initialize all the page lists one time */
+ for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) {
+ page = fb_deferred_io_page(info, i);
+ INIT_LIST_HEAD(&page->lru);
+ }
}
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
#include <drm/drm_vma_manager.h>
- struct dma_buf_map;
+ struct iosys_map;
struct drm_gem_object;
/**
*
* This callback is optional.
*/
- int (*vmap)(struct drm_gem_object *obj, struct dma_buf_map *map);
+ int (*vmap)(struct drm_gem_object *obj, struct iosys_map *map);
/**
* @vunmap:
*
* This callback is optional.
*/
- void (*vunmap)(struct drm_gem_object *obj, struct dma_buf_map *map);
+ void (*vunmap)(struct drm_gem_object *obj, struct iosys_map *map);
/**
* @mmap:
struct ww_acquire_ctx *acquire_ctx);
void drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
struct ww_acquire_ctx *acquire_ctx);
-int drm_gem_fence_array_add(struct xarray *fence_array,
- struct dma_fence *fence);
-int drm_gem_fence_array_add_implicit(struct xarray *fence_array,
- struct drm_gem_object *obj,
- bool write);
int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
u32 handle, u64 *offset);
struct ttm_device;
- struct dma_buf_map;
+ struct iosys_map;
struct drm_mm_node;
struct ttm_place;
-struct ttm_lru_bulk_move;
-
/**
* enum ttm_bo_type
*
* @ttm: TTM structure holding system pages.
* @evicted: Whether the object was evicted without user-space knowing.
* @deleted: True if the object is only a zombie and already deleted.
- * @lru: List head for the lru list.
* @ddestroy: List head for the delayed destroy list.
* @swap: List head for swap LRU list.
* @moving: Fence set when BO is moving
struct ttm_resource *resource;
struct ttm_tt *ttm;
bool deleted;
+ struct ttm_lru_bulk_move *bulk_move;
/**
* Members protected by the bdev::lru_lock.
*/
- struct list_head lru;
struct list_head ddestroy;
/**
*/
void ttm_bo_put(struct ttm_buffer_object *bo);
-/**
- * ttm_bo_move_to_lru_tail
- *
- * @bo: The buffer object.
- * @mem: Resource object.
- * @bulk: optional bulk move structure to remember BO positions
- *
- * Move this BO to the tail of all lru lists used to lookup and reserve an
- * object. This function must be called with struct ttm_global::lru_lock
- * held, and is used to make a BO less likely to be considered for eviction.
- */
-void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo,
- struct ttm_resource *mem,
- struct ttm_lru_bulk_move *bulk);
-
-/**
- * ttm_bo_bulk_move_lru_tail
- *
- * @bulk: bulk move structure
- *
- * Bulk move BOs to the LRU tail, only valid to use when driver makes sure that
- * BO order never changes. Should be called with ttm_global::lru_lock held.
- */
-void ttm_bo_bulk_move_lru_tail(struct ttm_lru_bulk_move *bulk);
+void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo);
+void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo,
+ struct ttm_lru_bulk_move *bulk);
/**
* ttm_bo_lock_delayed_workqueue
* ttm_bo_vmap
*
* @bo: The buffer object.
- * @map: pointer to a struct dma_buf_map representing the map.
+ * @map: pointer to a struct iosys_map representing the map.
*
* Sets up a kernel virtual mapping, using ioremap or vmap to the
* data in the buffer object. The parameter @map returns the virtual
- * address as struct dma_buf_map. Unmap the buffer with ttm_bo_vunmap().
+ * address as struct iosys_map. Unmap the buffer with ttm_bo_vunmap().
*
* Returns
* -ENOMEM: Out of memory.
* -EINVAL: Invalid range.
*/
- int ttm_bo_vmap(struct ttm_buffer_object *bo, struct dma_buf_map *map);
+ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map);
/**
* ttm_bo_vunmap
*
* Unmaps a kernel map set up by ttm_bo_vmap().
*/
- void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct dma_buf_map *map);
+ void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map);
/**
* ttm_bo_mmap_obj - mmap memory backed by a ttm buffer object.
int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx,
gfp_t gfp_flags);
-/**
- * ttm_bo_pin - Pin the buffer object.
- * @bo: The buffer object to pin
- *
- * Make sure the buffer is not evicted any more during memory pressure.
- */
-static inline void ttm_bo_pin(struct ttm_buffer_object *bo)
-{
- dma_resv_assert_held(bo->base.resv);
- WARN_ON_ONCE(!kref_read(&bo->kref));
- ++bo->pin_count;
-}
-
-/**
- * ttm_bo_unpin - Unpin the buffer object.
- * @bo: The buffer object to unpin
- *
- * Allows the buffer object to be evicted again during memory pressure.
- */
-static inline void ttm_bo_unpin(struct ttm_buffer_object *bo)
-{
- dma_resv_assert_held(bo->base.resv);
- WARN_ON_ONCE(!kref_read(&bo->kref));
- if (bo->pin_count)
- --bo->pin_count;
- else
- WARN_ON_ONCE(true);
-}
+void ttm_bo_pin(struct ttm_buffer_object *bo);
+void ttm_bo_unpin(struct ttm_buffer_object *bo);
int ttm_mem_evict_first(struct ttm_device *bdev,
struct ttm_resource_manager *man,
#define _TTM_RESOURCE_H_
#include <linux/types.h>
+#include <linux/list.h>
#include <linux/mutex.h>
- #include <linux/atomic.h>
- #include <linux/dma-buf-map.h>
+ #include <linux/iosys-map.h>
#include <linux/dma-fence.h>
+
#include <drm/drm_print.h>
#include <drm/ttm/ttm_caching.h>
#include <drm/ttm/ttm_kmap_iter.h>
#define TTM_MAX_BO_PRIORITY 4U
+#define TTM_NUM_MEM_TYPES 8
struct ttm_device;
struct ttm_resource_manager;
struct ttm_place;
struct ttm_buffer_object;
struct ttm_placement;
- struct dma_buf_map;
+ struct iosys_map;
struct io_mapping;
struct sg_table;
struct scatterlist;
uint32_t placement;
struct ttm_bus_placement bus;
struct ttm_buffer_object *bo;
+
+ /**
+ * @lru: Least recently used list, see &ttm_resource_manager.lru
+ */
+ struct list_head lru;
+};
+
+/**
+ * struct ttm_resource_cursor
+ *
+ * @priority: the current priority
+ *
+ * Cursor to iterate over the resources in a manager.
+ */
+struct ttm_resource_cursor {
+ unsigned int priority;
+};
+
+/**
+ * struct ttm_lru_bulk_move_pos
+ *
+ * @first: first res in the bulk move range
+ * @last: last res in the bulk move range
+ *
+ * Range of resources for a lru bulk move.
+ */
+struct ttm_lru_bulk_move_pos {
+ struct ttm_resource *first;
+ struct ttm_resource *last;
+};
+
+/**
+ * struct ttm_lru_bulk_move
+ *
+ * @tt: first/last lru entry for resources in the TT domain
+ * @vram: first/last lru entry for resources in the VRAM domain
+ *
+ * Container for the current bulk move state. Should be used with
+ * ttm_lru_bulk_move_init() and ttm_bo_set_bulk_move().
+ */
+struct ttm_lru_bulk_move {
+ struct ttm_lru_bulk_move_pos pos[TTM_NUM_MEM_TYPES][TTM_MAX_BO_PRIORITY];
};
/**
*/
struct ttm_kmap_iter_linear_io {
struct ttm_kmap_iter base;
- struct dma_buf_map dmap;
+ struct iosys_map dmap;
bool needs_unmap;
};
man->move = NULL;
}
+void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk);
+void ttm_lru_bulk_move_add(struct ttm_lru_bulk_move *bulk,
+ struct ttm_resource *res);
+void ttm_lru_bulk_move_del(struct ttm_lru_bulk_move *bulk,
+ struct ttm_resource *res);
+void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk);
+
+void ttm_resource_move_to_lru_tail(struct ttm_resource *res);
+
void ttm_resource_init(struct ttm_buffer_object *bo,
const struct ttm_place *place,
struct ttm_resource *res);
void ttm_resource_manager_debug(struct ttm_resource_manager *man,
struct drm_printer *p);
+struct ttm_resource *
+ttm_resource_manager_first(struct ttm_resource_manager *man,
+ struct ttm_resource_cursor *cursor);
+struct ttm_resource *
+ttm_resource_manager_next(struct ttm_resource_manager *man,
+ struct ttm_resource_cursor *cursor,
+ struct ttm_resource *res);
+
+/**
+ * ttm_resource_manager_for_each_res - iterate over all resources
+ * @man: the resource manager
+ * @cursor: struct ttm_resource_cursor for the current position
+ * @res: the current resource
+ *
+ * Iterate over all the evictable resources in a resource manager.
+ */
+#define ttm_resource_manager_for_each_res(man, cursor, res) \
+ for (res = ttm_resource_manager_first(man, cursor); res; \
+ res = ttm_resource_manager_next(man, cursor, res))
+
struct ttm_kmap_iter *
ttm_kmap_iter_iomap_init(struct ttm_kmap_iter_iomap *iter_io,
struct io_mapping *iomap,
#ifndef __DMA_BUF_H__
#define __DMA_BUF_H__
- #include <linux/dma-buf-map.h>
+ #include <linux/iosys-map.h>
#include <linux/file.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
*/
int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);
- int (*vmap)(struct dma_buf *dmabuf, struct dma_buf_map *map);
- void (*vunmap)(struct dma_buf *dmabuf, struct dma_buf_map *map);
+ int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
+ void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
};
/**
* @vmap_ptr:
* The current vmap ptr if @vmapping_counter > 0. Protected by @lock.
*/
- struct dma_buf_map vmap_ptr;
+ struct iosys_map vmap_ptr;
/**
* @exp_name:
* IMPORTANT:
*
* All drivers must obey the struct dma_resv rules, specifically the
- * rules for updating fences, see &dma_resv.fence_excl and
- * &dma_resv.fence. If these dependency rules are broken access tracking
- * can be lost resulting in use after free issues.
+ * rules for updating and obeying fences.
*/
struct dma_resv *resv;
int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *,
unsigned long);
- int dma_buf_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map);
- void dma_buf_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map);
+ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map);
+ void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map);
#endif /* __DMA_BUF_H__ */