summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2026-02-09 17:39:11 +0100
committerTakashi Iwai <tiwai@suse.de>2026-02-09 17:39:11 +0100
commitdd03dd60e8cdd5ef0f0cbc18276c45009bcc51f4 (patch)
tree45204c4b4ccc48dede2d0bcb5f8387f5a7edfd9a
parentfe7cd89f0e29f0852316857b4861309f9b891370 (diff)
parentf8f774913b4b599169381073f6674e20976e5529 (diff)
Merge tag 'asoc-v6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v7.0 This release is almost all abut driers, there's very little core work here, although some of that driver work is in more generic areas like SDCA and SOF: - Generic SDCA support for reporting jack events. - Continuing platform support, cleanup and feature improements for the AMD, Intel, Qualcomm and SOF code. - Platform description improvements for the Cirrus drivers. - Support for NXP i.MX952, Realtek RT1320 and RT5575, and Sophogo CV1800B. We also pulled in one small SPI API update and some more substantial regmap work (cache description improvements) for use in drivers.
-rw-r--r--.mailmap4
-rw-r--r--CREDITS4
-rw-r--r--Documentation/admin-guide/laptops/alienware-wmi.rst2
-rw-r--r--Documentation/admin-guide/sysctl/vm.rst6
-rw-r--r--Documentation/arch/riscv/uabi.rst4
-rw-r--r--Documentation/arch/x86/amd_hsmp.rst4
-rw-r--r--Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml2
-rw-r--r--Documentation/devicetree/bindings/goldfish/audio.txt17
-rw-r--r--Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml31
-rw-r--r--Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/awinic,aw87390.yaml34
-rw-r--r--Documentation/devicetree/bindings/sound/awinic,aw88395.yaml13
-rw-r--r--Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml71
-rw-r--r--Documentation/devicetree/bindings/sound/everest,es8389.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,audmix.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,mqs.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml22
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,sai.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml38
-rw-r--r--Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml176
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt5575.yaml61
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt5651.yaml100
-rw-r--r--Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/rt5651.txt63
-rw-r--r--Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml46
-rw-r--r--Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml67
-rw-r--r--Documentation/devicetree/bindings/sound/tas2552.txt36
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas2552.yaml62
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml7
-rw-r--r--Documentation/misc-devices/amd-sbi.rst2
-rw-r--r--Documentation/mm/allocation-profiling.rst10
-rw-r--r--Documentation/netlink/specs/fou.yaml2
-rw-r--r--Documentation/process/conclave.rst41
-rw-r--r--Documentation/process/index.rst1
-rw-r--r--Documentation/process/maintainer-netdev.rst12
-rw-r--r--MAINTAINERS22
-rw-r--r--Makefile5
-rw-r--r--arch/arm/boot/dts/microchip/lan966x-pcb8290.dts1
-rw-r--r--arch/arm/boot/dts/microchip/sama7d65.dtsi4
-rw-r--r--arch/arm/mach-npcm/Kconfig1
-rw-r--r--arch/arm64/boot/dts/nvidia/tegra210.dtsi24
-rw-r--r--arch/arm64/boot/dts/qcom/sc8280xp.dtsi16
-rw-r--r--arch/arm64/boot/dts/qcom/sdm845-oneplus-enchilada.dts4
-rw-r--r--arch/arm64/boot/dts/qcom/sm8550.dtsi2
-rw-r--r--arch/arm64/boot/dts/qcom/sm8650.dtsi3
-rw-r--r--arch/arm64/boot/dts/qcom/talos.dtsi4
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3326-odroid-go3.dts3
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-kobol-helios64.dts2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dtsi1
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts1
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts4
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts4
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-display-vz.dtso3
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts12
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3576.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588-base.dtsi4
-rw-r--r--arch/arm64/configs/defconfig1
-rw-r--r--arch/arm64/include/asm/kvm_asm.h2
-rw-r--r--arch/arm64/include/asm/kvm_emulate.h16
-rw-r--r--arch/arm64/include/asm/kvm_pgtable.h16
-rw-r--r--arch/arm64/include/asm/sysreg.h3
-rw-r--r--arch/arm64/kernel/hibernate.c2
-rw-r--r--arch/arm64/kernel/image-vars.h1
-rw-r--r--arch/arm64/kernel/ptrace.c26
-rw-r--r--arch/arm64/kernel/signal.c26
-rw-r--r--arch/arm64/kvm/arm.c1
-rw-r--r--arch/arm64/kvm/at.c8
-rw-r--r--arch/arm64/kvm/hyp/entry.S4
-rw-r--r--arch/arm64/kvm/hyp/include/hyp/switch.h2
-rw-r--r--arch/arm64/kvm/hyp/nvhe/hyp-main.c3
-rw-r--r--arch/arm64/kvm/hyp/nvhe/pkvm.c1
-rw-r--r--arch/arm64/kvm/hyp/nvhe/switch.c2
-rw-r--r--arch/arm64/kvm/hyp/pgtable.c5
-rw-r--r--arch/arm64/kvm/hyp/vhe/switch.c2
-rw-r--r--arch/arm64/kvm/mmu.c12
-rw-r--r--arch/arm64/kvm/sys_regs.c5
-rw-r--r--arch/arm64/kvm/va_layout.c28
-rw-r--r--arch/powerpc/kvm/book3s_hv_uvmem.c2
-rw-r--r--arch/riscv/Kconfig.errata1
-rw-r--r--arch/riscv/errata/sifive/errata.c18
-rw-r--r--arch/riscv/include/asm/compat.h2
-rw-r--r--arch/riscv/include/asm/syscall.h2
-rw-r--r--arch/riscv/include/asm/uaccess.h14
-rw-r--r--arch/riscv/kernel/signal.c6
-rw-r--r--arch/riscv/kernel/suspend.c3
-rw-r--r--arch/riscv/kvm/vcpu_timer.c6
-rw-r--r--arch/s390/boot/vmlinux.lds.S17
-rw-r--r--arch/s390/kernel/vdso/Makefile2
-rw-r--r--arch/x86/events/perf_event.h13
-rw-r--r--arch/x86/include/asm/kfence.h29
-rw-r--r--arch/x86/mm/fault.c15
-rw-r--r--block/blk-mq.c2
-rw-r--r--block/blk-zoned.c1
-rw-r--r--crypto/authencesn.c6
-rw-r--r--drivers/ata/ahci.c10
-rw-r--r--drivers/ata/libata-core.c8
-rw-r--r--drivers/ata/libata-sata.c2
-rw-r--r--drivers/base/dd.c2
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regcache-flat.c19
-rw-r--r--drivers/base/regmap/regcache-maple.c11
-rw-r--r--drivers/base/regmap/regcache.c3
-rw-r--r--drivers/base/regmap/regmap-kunit.c91
-rw-r--r--drivers/base/regmap/regmap.c6
-rw-r--r--drivers/block/rnbd/rnbd-clt.c1
-rw-r--r--drivers/block/ublk_drv.c39
-rw-r--r--drivers/bluetooth/hci_ldisc.c4
-rw-r--r--drivers/bus/simple-pm-bus.c6
-rw-r--r--drivers/clocksource/timer-riscv.c3
-rw-r--r--drivers/comedi/comedi_fops.c2
-rw-r--r--drivers/comedi/drivers/dmm32at.c32
-rw-r--r--drivers/comedi/range.c2
-rw-r--r--drivers/cpufreq/qcom-cpufreq-nvmem.c1
-rw-r--r--drivers/dpll/dpll_core.c12
-rw-r--r--drivers/firewire/core-transaction.c19
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c50
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_bin.c38
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c8
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c16
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_bin.c1033
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c14
-rw-r--r--drivers/firmware/efi/efi.c2
-rw-r--r--drivers/gpio/gpio-brcmstb.c8
-rw-r--r--drivers/gpio/gpio-omap.c22
-rw-r--r--drivers/gpio/gpio-pca953x.c2
-rw-r--r--drivers/gpio/gpio-rockchip.c8
-rw-r--r--drivers/gpio/gpio-sprd.c8
-rw-r--r--drivers/gpio/gpio-virtuser.c8
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c21
-rw-r--r--drivers/gpio/gpiolib-cdev.c12
-rw-r--r--drivers/gpio/gpiolib-shared.c16
-rw-r--r--drivers/gpio/gpiolib.c4
-rw-r--r--drivers/gpio/gpiolib.h2
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c25
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c37
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc21.c8
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_debug.h3
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_migrate.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c10
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c4
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c11
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c10
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c13
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c7
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c31
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c1
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c20
-rw-r--r--drivers/gpu/drm/drm_gem.c18
-rw-r--r--drivers/gpu/drm/drm_pagemap.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_pipeline.c36
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_trace.c8
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-tve.c13
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dpi.c23
-rw-r--r--drivers/gpu/drm/mediatek/mtk_gem.c264
-rw-r--r--drivers/gpu/drm/mediatek/mtk_gem.h33
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.h2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c58
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_v2.c7
-rw-r--r--drivers/gpu/drm/mediatek/mtk_plane.c8
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_catalog.c2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h95
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dmem.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c73
-rw-r--r--drivers/gpu/drm/tyr/Kconfig1
-rw-r--r--drivers/gpu/drm/vkms/vkms_colorop.c15
-rw-r--r--drivers/gpu/drm/xe/Kconfig5
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c9
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.c3
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.c72
-rw-r--r--drivers/gpu/drm/xe/xe_device.c2
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h18
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c6
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c32
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h1
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h6
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c14
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.h5
-rw-r--r--drivers/gpu/drm/xe/xe_late_bind_fw_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c5
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c4
-rw-r--r--drivers/gpu/drm/xe/xe_nvm.c55
-rw-r--r--drivers/gpu/drm/xe/xe_nvm.h2
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c6
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.c2
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c7
-rw-r--r--drivers/gpu/drm/xe/xe_vm.h2
-rw-r--r--drivers/hv/hv_common.c12
-rw-r--r--drivers/hv/hyperv_vmbus.h2
-rw-r--r--drivers/hv/mshv_eventfd.c2
-rw-r--r--drivers/hv/mshv_regions.c93
-rw-r--r--drivers/hv/mshv_root_main.c17
-rw-r--r--drivers/hwtracing/intel_th/core.c25
-rw-r--r--drivers/i2c/busses/i2c-k1.c2
-rw-r--r--drivers/iio/accel/adxl380.c6
-rw-r--r--drivers/iio/accel/st_accel_core.c72
-rw-r--r--drivers/iio/adc/ad7280a.c4
-rw-r--r--drivers/iio/adc/ad7606_par.c3
-rw-r--r--drivers/iio/adc/ad9467.c2
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c1
-rw-r--r--drivers/iio/adc/exynos_adc.c15
-rw-r--r--drivers/iio/adc/pac1934.c6
-rw-r--r--drivers/iio/chemical/scd4x.c6
-rw-r--r--drivers/iio/dac/ad3552r-hs.c5
-rw-r--r--drivers/iio/dac/ad5686.c6
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_core.c9
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c15
-rw-r--r--drivers/iio/industrialio-core.c4
-rw-r--r--drivers/input/serio/i8042-acpipnpio.h18
-rw-r--r--drivers/interconnect/debugfs-client.c5
-rw-r--r--drivers/iommu/amd/iommu.c3
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c3
-rw-r--r--drivers/iommu/generic_pt/iommu_pt.h13
-rw-r--r--drivers/iommu/io-pgtable-arm.c2
-rw-r--r--drivers/iommu/iommufd/pages.c1
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c8
-rw-r--r--drivers/irqchip/irq-ls-extirq.c75
-rw-r--r--drivers/irqchip/irq-renesas-rzv2h.c9
-rw-r--r--drivers/isdn/mISDN/timerdev.c13
-rw-r--r--drivers/leds/led-class.c10
-rw-r--r--drivers/md/bcache/bcache.h9
-rw-r--r--drivers/md/bcache/request.c75
-rw-r--r--drivers/md/bcache/super.c12
-rw-r--r--drivers/misc/mei/mei-trace.h18
-rw-r--r--drivers/misc/uacce/uacce.c48
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c41
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c14
-rw-r--r--drivers/mtd/nand/spi/esmt.c2
-rw-r--r--drivers/mux/mmio.c8
-rw-r--r--drivers/net/bonding/bond_main.c39
-rw-r--r--drivers/net/bonding/bond_options.c8
-rw-r--r--drivers/net/can/at91_can.c2
-rw-r--r--drivers/net/can/dev/dev.c1
-rw-r--r--drivers/net/can/usb/ems_usb.c8
-rw-r--r--drivers/net/can/usb/esd_usb.c9
-rw-r--r--drivers/net/can/usb/gs_usb.c9
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c9
-rw-r--r--drivers/net/can/usb/mcba_usb.c8
-rw-r--r--drivers/net/can/usb/usb_8dev.c8
-rw-r--r--drivers/net/dsa/yt921x.c15
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c5
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp.c5
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp.h1
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c5
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c3
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c8
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c13
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c4
-rw-r--r--drivers/net/ethernet/google/gve/gve.h5
-rw-r--r--drivers/net/ethernet/google/gve/gve_ethtool.c2
-rw-r--r--drivers/net/ethernet/google/gve/gve_main.c12
-rw-r--r--drivers/net/ethernet/google/gve/gve_ptp.c8
-rw-r--r--drivers/net/ethernet/google/gve/gve_rx_dqo.c2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c69
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c2
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_irq.c22
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/devlink.c1
-rw-r--r--drivers/net/ethernet/intel/ice/ice.h1
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.c2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool.c6
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lib.c39
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c32
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_ptp.c2
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_txrx.c16
-rw-r--r--drivers/net/ethernet/intel/igc/igc_defines.h5
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ethtool.c4
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c5
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ptp.c43
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c26
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c2
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_main.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.c86
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c3
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h7
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/debugfs.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/dev.c14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c26
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c1
-rw-r--r--drivers/net/ethernet/rocker/rocker_main.c5
-rw-r--r--drivers/net/ethernet/sfc/mcdi_filters.c7
-rw-r--r--drivers/net/ethernet/spacemit/k1_emac.c34
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c12
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c4
-rw-r--r--drivers/net/ipvlan/ipvlan.h2
-rw-r--r--drivers/net/ipvlan/ipvlan_core.c16
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c49
-rw-r--r--drivers/net/netdevsim/bpf.c6
-rw-r--r--drivers/net/netdevsim/dev.c2
-rw-r--r--drivers/net/netdevsim/netdevsim.h1
-rw-r--r--drivers/net/pcs/pcs-mtk-lynxi.c4
-rw-r--r--drivers/net/phy/intel-xway.c7
-rw-r--r--drivers/net/phy/micrel.c17
-rw-r--r--drivers/net/phy/sfp.c2
-rw-r--r--drivers/net/usb/dm9601.c4
-rw-r--r--drivers/net/usb/usbnet.c10
-rw-r--r--drivers/net/veth.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c16
-rw-r--r--drivers/net/wireless/ath/ath12k/ce.c12
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c16
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c9
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c6
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c1
-rw-r--r--drivers/net/wwan/mhi_wwan_mbim.c17
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c9
-rw-r--r--drivers/nfc/virtual_ncidev.c4
-rw-r--r--drivers/ntb/ntb_transport.c1
-rw-r--r--drivers/nvme/host/pci.c4
-rw-r--r--drivers/nvme/target/io-cmd-bdev.c3
-rw-r--r--drivers/of/base.c8
-rw-r--r--drivers/of/of_reserved_mem.c19
-rw-r--r--drivers/of/platform.c2
-rw-r--r--drivers/pci/rebar.c18
-rw-r--r--drivers/pci/setup-bus.c23
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.c2
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c9
-rw-r--r--drivers/pinctrl/pinctrl-th1520.c2
-rw-r--r--drivers/pinctrl/qcom/Kconfig15
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpass-lpi.c17
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c3
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c151
-rw-r--r--drivers/platform/mellanox/mlx-platform.c2
-rw-r--r--drivers/platform/x86/acer-wmi.c13
-rw-r--r--drivers/platform/x86/amd/wbrf.c4
-rw-r--r--drivers/platform/x86/asus-armoury.h224
-rw-r--r--drivers/platform/x86/asus-wmi.c3
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.c8
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.h12
-rw-r--r--drivers/pmdomain/imx/imx8m-blk-ctrl.c11
-rw-r--r--drivers/pmdomain/qcom/rpmhpd.c4
-rw-r--r--drivers/pmdomain/rockchip/pm-domains.c10
-rw-r--r--drivers/pwm/core.c10
-rw-r--r--drivers/pwm/pwm-max7360.c1
-rw-r--r--drivers/regulator/fp9931.c3
-rw-r--r--drivers/s390/crypto/ap_card.c2
-rw-r--r--drivers/s390/crypto/ap_queue.c2
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c1
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c2
-rw-r--r--drivers/scsi/scsi_error.c11
-rw-r--r--drivers/scsi/scsi_lib.c8
-rw-r--r--drivers/scsi/storvsc_drv.c3
-rw-r--r--drivers/slimbus/core.c54
-rw-r--r--drivers/soc/renesas/Kconfig1
-rw-r--r--drivers/spi/spi-cadence.c1
-rw-r--r--drivers/spi/spi-hisi-kunpeng.c4
-rw-r--r--drivers/spi/spi-intel-pci.c1
-rw-r--r--drivers/spi/spi-sprd-adi.c33
-rw-r--r--drivers/spi/spi.c23
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c10
-rw-r--r--drivers/target/sbp/sbp_target.c4
-rw-r--r--drivers/tty/serial/8250/8250_pci.c2
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c13
-rw-r--r--drivers/tty/serial/serial_core.c6
-rw-r--r--drivers/ufs/host/ufs-amd-versal2.c2
-rw-r--r--drivers/uio/uio_pci_generic_sva.c4
-rw-r--r--drivers/vfio/pci/vfio_pci_dmabuf.c12
-rw-r--r--drivers/w1/slaves/w1_therm.c62
-rw-r--r--drivers/w1/w1.c2
-rw-r--r--drivers/xen/xen-scsiback.c1
-rw-r--r--fs/9p/vfs_dir.c2
-rw-r--r--fs/btrfs/disk-io.c41
-rw-r--r--fs/btrfs/extent_io.c3
-rw-r--r--fs/btrfs/extent_io.h3
-rw-r--r--fs/btrfs/fs.h8
-rw-r--r--fs/btrfs/tree-log.c2
-rw-r--r--fs/btrfs/volumes.c42
-rw-r--r--fs/btrfs/volumes.h4
-rw-r--r--fs/btrfs/zlib.c1
-rw-r--r--fs/ceph/dir.c2
-rw-r--r--fs/dcache.c10
-rw-r--r--fs/efivarfs/vars.c2
-rw-r--r--fs/fs-writeback.c23
-rw-r--r--fs/fuse/dir.c66
-rw-r--r--fs/fuse/file.c4
-rw-r--r--fs/gfs2/file.c1
-rw-r--r--fs/iomap/buffered-io.c1
-rw-r--r--fs/nfs/dir.c1
-rw-r--r--fs/nfs/nfs4file.c2
-rw-r--r--fs/readdir.c3
-rw-r--r--fs/romfs/super.c5
-rw-r--r--fs/smb/client/cifsfs.c4
-rw-r--r--fs/smb/server/transport_rdma.c16
-rw-r--r--fs/smb/server/vfs.c2
-rw-r--r--fs/vboxsf/dir.c1
-rw-r--r--include/asm-generic/tlb.h77
-rw-r--r--include/drm/drm_pagemap.h19
-rw-r--r--include/dt-bindings/power/qcom,rpmhpd.h1
-rw-r--r--include/hyperv/hvhdk.h47
-rw-r--r--include/linux/cma.h9
-rw-r--r--include/linux/device/driver.h9
-rw-r--r--include/linux/firmware/cirrus/cs_dsp_test_utils.h6
-rw-r--r--include/linux/firmware/cirrus/wmfw.h7
-rw-r--r--include/linux/firmware/imx/sm.h2
-rw-r--r--include/linux/fs.h6
-rw-r--r--include/linux/hugetlb.h17
-rw-r--r--include/linux/iio/iio-opaque.h2
-rw-r--r--include/linux/kasan.h14
-rw-r--r--include/linux/memfd.h6
-rw-r--r--include/linux/memremap.h9
-rw-r--r--include/linux/mm.h6
-rw-r--r--include/linux/mm_types.h19
-rw-r--r--include/linux/mmzone.h9
-rw-r--r--include/linux/pagemap.h11
-rw-r--r--include/linux/pci_ids.h4
-rw-r--r--include/linux/platform_data/davinci_asp.h3
-rw-r--r--include/linux/regmap.h14
-rw-r--r--include/linux/sched.h5
-rw-r--r--include/linux/soc/qcom/apr.h6
-rw-r--r--include/linux/spi/spi.h9
-rw-r--r--include/net/bonding.h13
-rw-r--r--include/net/cfg80211.h3
-rw-r--r--include/net/nfc/nfc.h2
-rw-r--r--include/sound/ak4641.h23
-rw-r--r--include/sound/cs-amp-lib.h3
-rw-r--r--include/sound/cs35l56.h37
-rw-r--r--include/sound/sdca_function.h4
-rw-r--r--include/sound/sdca_interrupts.h7
-rw-r--r--include/sound/sdca_jack.h32
-rw-r--r--include/sound/soc-acpi-intel-ssp-common.h4
-rw-r--r--include/sound/soc-component.h29
-rw-r--r--include/sound/soc-dapm.h44
-rw-r--r--include/sound/soc.h4
-rw-r--r--include/sound/sof.h3
-rw-r--r--include/sound/sof/ipc4/header.h75
-rw-r--r--include/sound/tas2781.h3
-rw-r--r--include/trace/events/rxrpc.h4
-rw-r--r--include/uapi/linux/blkzoned.h6
-rw-r--r--include/uapi/linux/comedi.h2
-rw-r--r--include/uapi/linux/nl80211.h5
-rw-r--r--include/uapi/sound/sof/tokens.h6
-rw-r--r--io_uring/io-wq.c2
-rw-r--r--io_uring/rw.c15
-rw-r--r--io_uring/waitid.c6
-rw-r--r--kernel/dma/contiguous.c16
-rw-r--r--kernel/dma/pool.c34
-rw-r--r--kernel/events/callchain.c2
-rw-r--r--kernel/events/core.c15
-rw-r--r--kernel/liveupdate/kexec_handover.c12
-rw-r--r--kernel/panic.c4
-rw-r--r--kernel/sched/deadline.c12
-rw-r--r--kernel/sched/fair.c16
-rw-r--r--kernel/sched/features.h2
-rw-r--r--kernel/sched/idle.c6
-rw-r--r--kernel/time/clocksource.c2
-rw-r--r--kernel/time/timekeeping.c2
-rw-r--r--kernel/trace/trace.c8
-rw-r--r--kernel/trace/trace_events_hist.c9
-rw-r--r--kernel/trace/trace_events_synth.c8
-rw-r--r--kernel/trace/trace_functions_graph.c2
-rw-r--r--kernel/vmcore_info.c6
-rw-r--r--lib/flex_proportions.c5
-rw-r--r--lib/test_hmm.c4
-rw-r--r--mm/hugetlb.c131
-rw-r--r--mm/init-mm.c5
-rw-r--r--mm/internal.h8
-rw-r--r--mm/kasan/common.c21
-rw-r--r--mm/kfence/core.c40
-rw-r--r--mm/memfd.c4
-rw-r--r--mm/memfd_luo.c10
-rw-r--r--mm/memory-failure.c99
-rw-r--r--mm/memory.c11
-rw-r--r--mm/memremap.c35
-rw-r--r--mm/migrate.c12
-rw-r--r--mm/mm_init.c12
-rw-r--r--mm/mmu_gather.c33
-rw-r--r--mm/page_alloc.c8
-rw-r--r--mm/rmap.c45
-rw-r--r--mm/shmem.c45
-rw-r--r--mm/slub.c8
-rw-r--r--mm/swap.h2
-rw-r--r--mm/swap_state.c3
-rw-r--r--mm/vma.c11
-rw-r--r--mm/vmalloc.c7
-rw-r--r--mm/vmscan.c13
-rw-r--r--net/bluetooth/mgmt.c3
-rw-r--r--net/bridge/br_input.c2
-rw-r--r--net/core/filter.c2
-rw-r--r--net/core/skbuff.c7
-rw-r--r--net/dsa/dsa.c2
-rw-r--r--net/ipv4/fou_core.c3
-rw-r--r--net/ipv4/fou_nl.c2
-rw-r--r--net/ipv4/tcp_offload.c3
-rw-r--r--net/ipv4/udp_offload.c3
-rw-r--r--net/ipv6/icmp.c4
-rw-r--r--net/ipv6/ndisc.c4
-rw-r--r--net/ipv6/tcpv6_offload.c3
-rw-r--r--net/l2tp/l2tp_core.c8
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c8
-rw-r--r--net/mac80211/key.c3
-rw-r--r--net/mac80211/mlme.c217
-rw-r--r--net/mac80211/scan.c9
-rw-r--r--net/mptcp/pm_kernel.c16
-rw-r--r--net/mptcp/protocol.c13
-rw-r--r--net/netrom/nr_route.c13
-rw-r--r--net/nfc/core.c27
-rw-r--r--net/nfc/llcp_commands.c17
-rw-r--r--net/nfc/llcp_core.c4
-rw-r--r--net/nfc/nci/core.c4
-rw-r--r--net/openvswitch/vport.c11
-rw-r--r--net/rxrpc/ar-internal.h9
-rw-r--r--net/rxrpc/conn_event.c2
-rw-r--r--net/rxrpc/output.c14
-rw-r--r--net/rxrpc/peer_event.c17
-rw-r--r--net/rxrpc/proc.c4
-rw-r--r--net/rxrpc/recvmsg.c19
-rw-r--r--net/rxrpc/rxgk.c2
-rw-r--r--net/rxrpc/rxkad.c2
-rw-r--r--net/sched/act_ife.c6
-rw-r--r--net/sched/sch_qfq.c2
-rw-r--r--net/sched/sch_teql.c5
-rw-r--r--net/sctp/sm_statefuns.c10
-rw-r--r--net/vmw_vsock/virtio_transport_common.c36
-rw-r--r--net/wireless/nl80211.c10
-rw-r--r--net/wireless/util.c8
-rw-r--r--rust/Makefile1
-rw-r--r--rust/kernel/auxiliary.rs41
-rw-r--r--rust/kernel/bits.rs6
-rw-r--r--rust/kernel/device.rs20
-rw-r--r--rust/kernel/driver.rs86
-rw-r--r--rust/kernel/fmt.rs2
-rw-r--r--rust/kernel/i2c.rs31
-rw-r--r--rust/kernel/io.rs9
-rw-r--r--rust/kernel/io/resource.rs2
-rw-r--r--rust/kernel/irq/flags.rs2
-rw-r--r--rust/kernel/num/bounded.rs49
-rw-r--r--rust/kernel/pci.rs27
-rw-r--r--rust/kernel/platform.rs27
-rw-r--r--rust/kernel/rbtree.rs4
-rw-r--r--rust/kernel/sync/atomic/predefine.rs11
-rw-r--r--rust/kernel/sync/refcount.rs3
-rw-r--r--rust/kernel/usb.rs27
-rw-r--r--rust/macros/fmt.rs2
-rw-r--r--rust/macros/lib.rs2
-rw-r--r--rust/proc-macro2/lib.rs4
-rw-r--r--scripts/Makefile.build4
-rw-r--r--scripts/Makefile.vmlinux3
-rwxr-xr-xscripts/check-function-names.sh2
-rwxr-xr-xscripts/generate_rust_analyzer.py45
-rwxr-xr-xscripts/kconfig/nconf-cfg.sh11
-rwxr-xr-xscripts/livepatch/klp-build8
-rw-r--r--scripts/package/kernel.spec65
-rw-r--r--scripts/rustdoc_test_gen.rs2
-rw-r--r--scripts/tracepoint-update.c2
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c4
-rw-r--r--sound/arm/Kconfig10
-rw-r--r--sound/arm/Makefile3
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c50
-rw-r--r--sound/arm/pxa2xx-ac97.c286
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda.c9
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_i2c.c13
-rw-r--r--sound/hda/controllers/intel.c3
-rw-r--r--sound/hda/core/intel-dsp-config.c8
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/amd/Kconfig2
-rw-r--r--sound/soc/amd/acp/Kconfig9
-rw-r--r--sound/soc/amd/acp/Makefile2
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c8
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c8
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c380
-rw-r--r--sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c42
-rw-r--r--sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h25
-rw-r--r--sound/soc/codecs/Kconfig50
-rw-r--r--sound/soc/codecs/Makefile9
-rw-r--r--sound/soc/codecs/ak4641.c641
-rw-r--r--sound/soc/codecs/alc5623.c5
-rw-r--r--sound/soc/codecs/aw87390.c175
-rw-r--r--sound/soc/codecs/aw87390.h86
-rw-r--r--sound/soc/codecs/aw88261.c16
-rw-r--r--sound/soc/codecs/aw88261.h2
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c169
-rw-r--r--sound/soc/codecs/cs-amp-lib.c158
-rw-r--r--sound/soc/codecs/cs35l56-shared-test.c680
-rw-r--r--sound/soc/codecs/cs35l56-shared.c172
-rw-r--r--sound/soc/codecs/cs35l56-test.c639
-rw-r--r--sound/soc/codecs/cs35l56.c215
-rw-r--r--sound/soc/codecs/cs35l56.h6
-rw-r--r--sound/soc/codecs/cs4271.c12
-rw-r--r--sound/soc/codecs/da7213.c4
-rw-r--r--sound/soc/codecs/es8323.c294
-rw-r--r--sound/soc/codecs/es8323.h105
-rw-r--r--sound/soc/codecs/es8326.c18
-rw-r--r--sound/soc/codecs/es8326.h3
-rw-r--r--sound/soc/codecs/es8328.c160
-rw-r--r--sound/soc/codecs/es8375.c2
-rw-r--r--sound/soc/codecs/es8389.c246
-rw-r--r--sound/soc/codecs/es8389.h8
-rw-r--r--sound/soc/codecs/max98390.c3
-rw-r--r--sound/soc/codecs/nau8821.c48
-rw-r--r--sound/soc/codecs/nau8821.h1
-rw-r--r--sound/soc/codecs/rt1320-sdw.c1227
-rw-r--r--sound/soc/codecs/rt1320-sdw.h66
-rw-r--r--sound/soc/codecs/rt274.c12
-rw-r--r--sound/soc/codecs/rt286.c12
-rw-r--r--sound/soc/codecs/rt298.c12
-rw-r--r--sound/soc/codecs/rt5575-spi.c118
-rw-r--r--sound/soc/codecs/rt5575-spi.h27
-rw-r--r--sound/soc/codecs/rt5575.c352
-rw-r--r--sound/soc/codecs/rt5575.h58
-rw-r--r--sound/soc/codecs/rt5640.c4
-rw-r--r--sound/soc/codecs/rt5651.c2
-rw-r--r--sound/soc/codecs/rt5659.c2
-rw-r--r--sound/soc/codecs/rt5663.c4
-rw-r--r--sound/soc/codecs/rt5665.c2
-rw-r--r--sound/soc/codecs/rt5668.c2
-rw-r--r--sound/soc/codecs/rt5682-i2c.c2
-rw-r--r--sound/soc/codecs/rtq9128.c114
-rw-r--r--sound/soc/codecs/sdw-mockup.c6
-rw-r--r--sound/soc/codecs/tas2780.c31
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c138
-rw-r--r--sound/soc/codecs/tas2781-i2c.c11
-rw-r--r--sound/soc/codecs/tas2783-sdw.c356
-rw-r--r--sound/soc/codecs/tas2783.h1
-rw-r--r--sound/soc/codecs/tlv320adcx140.c136
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c12
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c19
-rw-r--r--sound/soc/codecs/wm0010.c2
-rw-r--r--sound/soc/codecs/wm8731.c11
-rw-r--r--sound/soc/codecs/wm8962.c47
-rw-r--r--sound/soc/fsl/Kconfig6
-rw-r--r--sound/soc/fsl/fsl_asrc.c38
-rw-r--r--sound/soc/fsl/fsl_asrc.h18
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h4
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c48
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c8
-rw-r--r--sound/soc/fsl/fsl_audmix.c3
-rw-r--r--sound/soc/fsl/fsl_sai.c21
-rw-r--r--sound/soc/fsl/fsl_sai.h4
-rw-r--r--sound/soc/fsl/fsl_ssi.c2
-rw-r--r--sound/soc/fsl/imx-rpmsg.c2
-rw-r--r--sound/soc/intel/avs/board_selection.c2
-rw-r--r--sound/soc/intel/avs/core.c2
-rw-r--r--sound/soc/intel/avs/path.c2
-rw-r--r--sound/soc/intel/boards/Kconfig5
-rw-r--r--sound/soc/intel/boards/Makefile3
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c6
-rw-r--r--sound/soc/intel/boards/sof_sdw.c43
-rw-r--r--sound/soc/intel/boards/sof_ti_common.c77
-rw-r--r--sound/soc/intel/boards/sof_ti_common.h24
-rw-r--r--sound/soc/intel/catpt/core.h7
-rw-r--r--sound/soc/intel/catpt/device.c6
-rw-r--r--sound/soc/intel/catpt/ipc.c12
-rw-r--r--sound/soc/intel/catpt/loader.c8
-rw-r--r--sound/soc/intel/catpt/messages.c89
-rw-r--r--sound/soc/intel/catpt/pcm.c46
-rw-r--r--sound/soc/intel/catpt/sysfs.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c23
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c55
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ssp-common.c3
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-nau8825.c6
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c44
-rw-r--r--sound/soc/pxa/Kconfig1
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c5
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c229
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h55
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c8
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c26
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c19
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c33
-rw-r--r--sound/soc/qcom/qdsp6/q6core.c6
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c22
-rw-r--r--sound/soc/qcom/qdsp6/topology.c118
-rw-r--r--sound/soc/renesas/rz-ssi.c143
-rw-r--r--sound/soc/rockchip/Kconfig1
-rw-r--r--sound/soc/rockchip/rockchip_sai.c5
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c225
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h57
-rw-r--r--sound/soc/sdca/Kconfig2
-rw-r--r--sound/soc/sdca/Makefile2
-rw-r--r--sound/soc/sdca/sdca_asoc.c91
-rw-r--r--sound/soc/sdca/sdca_class.c34
-rw-r--r--sound/soc/sdca/sdca_class.h2
-rw-r--r--sound/soc/sdca/sdca_class_function.c141
-rw-r--r--sound/soc/sdca/sdca_fdl.c8
-rw-r--r--sound/soc/sdca/sdca_functions.c75
-rw-r--r--sound/soc/sdca/sdca_interrupts.c188
-rw-r--r--sound/soc/sdca/sdca_jack.c248
-rw-r--r--sound/soc/sdca/sdca_regmap.c14
-rw-r--r--sound/soc/sdca/sdca_ump.c2
-rw-r--r--sound/soc/sdw_utils/soc_sdw_ti_amp.c4
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c39
-rw-r--r--sound/soc/soc-component.c82
-rw-r--r--sound/soc/soc-core.c10
-rw-r--r--sound/soc/soc-dapm.c352
-rw-r--r--sound/soc/sof/amd/acp-ipc.c4
-rw-r--r--sound/soc/sof/compress.c8
-rw-r--r--sound/soc/sof/control.c13
-rw-r--r--sound/soc/sof/core.c1
-rw-r--r--sound/soc/sof/debug.c7
-rw-r--r--sound/soc/sof/imx/imx-common.c8
-rw-r--r--sound/soc/sof/intel/Kconfig1
-rw-r--r--sound/soc/sof/intel/atom.c7
-rw-r--r--sound/soc/sof/intel/bdw.c7
-rw-r--r--sound/soc/sof/intel/cnl.c11
-rw-r--r--sound/soc/sof/intel/hda-codec.c1
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c25
-rw-r--r--sound/soc/sof/intel/hda-dai.c22
-rw-r--r--sound/soc/sof/intel/hda-ipc.c11
-rw-r--r--sound/soc/sof/intel/hda-loader.c94
-rw-r--r--sound/soc/sof/intel/hda-mlink.c29
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c16
-rw-r--r--sound/soc/sof/intel/hda-stream.c168
-rw-r--r--sound/soc/sof/intel/hda.c46
-rw-r--r--sound/soc/sof/intel/hda.h16
-rw-r--r--sound/soc/sof/intel/mtl.c5
-rw-r--r--sound/soc/sof/intel/nvl.c24
-rw-r--r--sound/soc/sof/intel/pci-apl.c2
-rw-r--r--sound/soc/sof/intel/pci-lnl.c1
-rw-r--r--sound/soc/sof/intel/pci-nvl.c32
-rw-r--r--sound/soc/sof/intel/pci-ptl.c2
-rw-r--r--sound/soc/sof/ipc.c9
-rw-r--r--sound/soc/sof/ipc3-dtrace.c7
-rw-r--r--sound/soc/sof/ipc3-topology.c6
-rw-r--r--sound/soc/sof/ipc3.c8
-rw-r--r--sound/soc/sof/ipc4-control.c198
-rw-r--r--sound/soc/sof/ipc4-loader.c19
-rw-r--r--sound/soc/sof/ipc4-mtrace.c24
-rw-r--r--sound/soc/sof/ipc4-pcm.c3
-rw-r--r--sound/soc/sof/ipc4-topology.c214
-rw-r--r--sound/soc/sof/ipc4-topology.h13
-rw-r--r--sound/soc/sof/ipc4.c64
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c4
-rw-r--r--sound/soc/sof/ops.c34
-rw-r--r--sound/soc/sof/ops.h10
-rw-r--r--sound/soc/sof/pcm.c91
-rw-r--r--sound/soc/sof/pm.c149
-rw-r--r--sound/soc/sof/sof-audio.c231
-rw-r--r--sound/soc/sof/sof-audio.h20
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c7
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c4
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c14
-rw-r--r--sound/soc/sof/sof-client-probes.c26
-rw-r--r--sound/soc/sof/sof-client.c46
-rw-r--r--sound/soc/sof/sof-client.h3
-rw-r--r--sound/soc/sof/sof-priv.h3
-rw-r--r--sound/soc/sophgo/Kconfig47
-rw-r--r--sound/soc/sophgo/Makefile5
-rw-r--r--sound/soc/sophgo/cv1800b-sound-adc.c319
-rw-r--r--sound/soc/sophgo/cv1800b-sound-dac.c208
-rw-r--r--sound/soc/sophgo/cv1800b-tdm.c716
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c4
-rw-r--r--sound/soc/sunxi/sun50i-dmic.c3
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1
-rw-r--r--sound/soc/tegra/tegra186_dspk.c1
-rw-r--r--sound/soc/tegra/tegra210_admaif.c3
-rw-r--r--sound/soc/tegra/tegra210_adx.c2
-rw-r--r--sound/soc/tegra/tegra210_ahub.c60
-rw-r--r--sound/soc/tegra/tegra210_ahub.h30
-rw-r--r--sound/soc/tegra/tegra210_amx.c3
-rw-r--r--sound/soc/tegra/tegra210_dmic.c1
-rw-r--r--sound/soc/tegra/tegra210_i2s.c2
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c1
-rw-r--r--sound/soc/tegra/tegra210_mixer.c1
-rw-r--r--sound/soc/tegra/tegra210_mvc.c1
-rw-r--r--sound/soc/tegra/tegra210_ope.c1
-rw-r--r--sound/soc/tegra/tegra210_peq.c1
-rw-r--r--sound/soc/tegra/tegra210_sfc.c1
-rw-r--r--sound/soc/ti/davinci-mcasp.c517
-rw-r--r--sound/soc/ti/davinci-mcasp.h10
-rw-r--r--tools/include/io_uring/mini_liburing.h59
-rw-r--r--tools/net/ynl/Makefile3
-rwxr-xr-xtools/net/ynl/ynl-regen.sh2
-rw-r--r--tools/objtool/Makefile21
-rw-r--r--tools/objtool/check.c3
-rw-r--r--tools/objtool/disas.c14
-rw-r--r--tools/objtool/elf.c13
-rw-r--r--tools/objtool/klp-diff.c14
-rw-r--r--tools/perf/util/parse-events.c7
-rw-r--r--tools/testing/selftests/net/Makefile1
-rwxr-xr-xtools/testing/selftests/net/amt.sh7
-rw-r--r--tools/testing/selftests/net/config2
-rwxr-xr-xtools/testing/selftests/net/fcnal-test.sh7
-rwxr-xr-xtools/testing/selftests/net/fib-onlink-tests.sh71
-rwxr-xr-xtools/testing/selftests/net/ipvtap_test.sh168
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_join.sh81
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json25
-rw-r--r--tools/testing/selftests/ublk/kublk.c11
-rw-r--r--tools/testing/selftests/vDSO/vgetrandom-chacha.S2
-rw-r--r--tools/testing/vsock/util.h2
-rw-r--r--tools/testing/vsock/vsock_test.c117
-rw-r--r--tools/testing/vsock/vsock_test_zerocopy.c74
-rw-r--r--tools/testing/vsock/vsock_test_zerocopy.h3
816 files changed, 17653 insertions, 6566 deletions
diff --git a/.mailmap b/.mailmap
index 4a8a160f28ed..22db8cfc18fc 100644
--- a/.mailmap
+++ b/.mailmap
@@ -12,6 +12,7 @@
#
Aaron Durbin <adurbin@google.com>
Abel Vesa <abelvesa@kernel.org> <abel.vesa@nxp.com>
+Abel Vesa <abelvesa@kernel.org> <abel.vesa@linaro.org>
Abel Vesa <abelvesa@kernel.org> <abelvesa@gmail.com>
Abhijeet Dharmapurikar <quic_adharmap@quicinc.com> <adharmap@codeaurora.org>
Abhinav Kumar <quic_abhinavk@quicinc.com> <abhinavk@codeaurora.org>
@@ -850,6 +851,7 @@ Valentin Schneider <vschneid@redhat.com> <valentin.schneider@arm.com>
Veera Sundaram Sankaran <quic_veeras@quicinc.com> <veeras@codeaurora.org>
Veerabhadrarao Badiganti <quic_vbadigan@quicinc.com> <vbadigan@codeaurora.org>
Venkateswara Naralasetty <quic_vnaralas@quicinc.com> <vnaralas@codeaurora.org>
+Viacheslav Bocharov <v@baodeep.com> <adeep@lexina.in>
Vikash Garodia <vikash.garodia@oss.qualcomm.com> <vgarodia@codeaurora.org>
Vikash Garodia <vikash.garodia@oss.qualcomm.com> <quic_vgarodia@quicinc.com>
Vincent Mailhol <mailhol@kernel.org> <mailhol.vincent@wanadoo.fr>
@@ -878,6 +880,8 @@ Wolfram Sang <wsa@kernel.org> <wsa@the-dreams.de>
Yakir Yang <kuankuan.y@gmail.com> <ykk@rock-chips.com>
Yanteng Si <si.yanteng@linux.dev> <siyanteng@loongson.cn>
Ying Huang <huang.ying.caritas@gmail.com> <ying.huang@intel.com>
+Yixun Lan <dlan@kernel.org> <dlan@gentoo.org>
+Yixun Lan <dlan@kernel.org> <yixun.lan@amlogic.com>
Yosry Ahmed <yosry.ahmed@linux.dev> <yosryahmed@google.com>
Yu-Chun Lin <eleanor.lin@realtek.com> <eleanor15x@gmail.com>
Yusuke Goda <goda.yusuke@renesas.com>
diff --git a/CREDITS b/CREDITS
index 383809bc4b7a..ec8a2acf1947 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2231,6 +2231,10 @@ S: Markham, Ontario
S: L3R 8B2
S: Canada
+N: Krzysztof Kozlowski
+E: krzk@kernel.org
+D: NFC network subsystem and drivers maintainer
+
N: Christian Krafft
D: PowerPC Cell support
diff --git a/Documentation/admin-guide/laptops/alienware-wmi.rst b/Documentation/admin-guide/laptops/alienware-wmi.rst
index 27a32a8057da..e532c60db8e2 100644
--- a/Documentation/admin-guide/laptops/alienware-wmi.rst
+++ b/Documentation/admin-guide/laptops/alienware-wmi.rst
@@ -105,7 +105,7 @@ information.
Manual fan control on the other hand, is not exposed directly by the AWCC
interface. Instead it let's us control a fan `boost` value. This `boost` value
-has the following aproximate behavior over the fan pwm:
+has the following approximate behavior over the fan pwm:
::
diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst
index 4d71211fdad8..06d0ebddeefa 100644
--- a/Documentation/admin-guide/sysctl/vm.rst
+++ b/Documentation/admin-guide/sysctl/vm.rst
@@ -231,6 +231,8 @@ eventually gets pushed out to disk. This tunable is used to define when dirty
inode is old enough to be eligible for writeback by the kernel flusher threads.
And, it is also used as the interval to wakeup dirtytime_writeback thread.
+Setting this to zero disables periodic dirtytime writeback.
+
dirty_writeback_centisecs
=========================
@@ -494,6 +496,10 @@ memory allocations.
The default value depends on CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT.
+When CONFIG_MEM_ALLOC_PROFILING_DEBUG=y, this control is read-only to avoid
+warnings produced by allocations made while profiling is disabled and freed
+when it's enabled.
+
memory_failure_early_kill
=========================
diff --git a/Documentation/arch/riscv/uabi.rst b/Documentation/arch/riscv/uabi.rst
index 243e40062e34..0c5299e00762 100644
--- a/Documentation/arch/riscv/uabi.rst
+++ b/Documentation/arch/riscv/uabi.rst
@@ -7,7 +7,9 @@ ISA string ordering in /proc/cpuinfo
------------------------------------
The canonical order of ISA extension names in the ISA string is defined in
-chapter 27 of the unprivileged specification.
+Chapter 27 of the RISC-V Instruction Set Manual Volume I Unprivileged ISA
+(Document Version 20191213).
+
The specification uses vague wording, such as should, when it comes to ordering,
so for our purposes the following rules apply:
diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst
index a094f55c10b0..8bb411f0d70d 100644
--- a/Documentation/arch/x86/amd_hsmp.rst
+++ b/Documentation/arch/x86/amd_hsmp.rst
@@ -14,7 +14,7 @@ set of mailbox registers.
More details on the interface can be found in chapter
"7 Host System Management Port (HSMP)" of the family/model PPR
-Eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
+Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50
HSMP interface is supported on EPYC line of server CPUs and MI300A (APU).
@@ -185,7 +185,7 @@ what happened. The transaction returns 0 on success.
More details on the interface and message definitions can be found in chapter
"7 Host System Management Port (HSMP)" of the respective family/model PPR
-eg: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
+eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50
User space C-APIs are made available by linking against the esmi library,
which is provided by the E-SMS project https://www.amd.com/en/developer/e-sms.html.
diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
index 274f590807ca..8f4bd9fb560b 100644
--- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dp.yaml
@@ -11,7 +11,7 @@ maintainers:
- Jitao shi <jitao.shi@mediatek.com>
description: |
- MediaTek DP and eDP are different hardwares and there are some features
+ MediaTek DP and eDP are different hardware and there are some features
which are not supported for eDP. For example, audio is not supported for
eDP. Therefore, we need to use two different compatibles to describe them.
In addition, We just need to enable the power domain of DP, so the clock
diff --git a/Documentation/devicetree/bindings/goldfish/audio.txt b/Documentation/devicetree/bindings/goldfish/audio.txt
deleted file mode 100644
index d043fda433ba..000000000000
--- a/Documentation/devicetree/bindings/goldfish/audio.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Android Goldfish Audio
-
-Android goldfish audio device generated by android emulator.
-
-Required properties:
-
-- compatible : should contain "google,goldfish-audio" to match emulator
-- reg : <registers mapping>
-- interrupts : <interrupt mapping>
-
-Example:
-
- goldfish_audio@9030000 {
- compatible = "google,goldfish-audio";
- reg = <0x9030000 0x100>;
- interrupts = <0x4>;
- };
diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml
index 71428d2cce18..3dbe83e2de3d 100644
--- a/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml
+++ b/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml
@@ -74,6 +74,37 @@ allOf:
- description: aggre UFS CARD AXI clock
- description: RPMH CC IPA clock
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,sa8775p-config-noc
+ - qcom,sa8775p-dc-noc
+ - qcom,sa8775p-gem-noc
+ - qcom,sa8775p-gpdsp-anoc
+ - qcom,sa8775p-lpass-ag-noc
+ - qcom,sa8775p-mmss-noc
+ - qcom,sa8775p-nspa-noc
+ - qcom,sa8775p-nspb-noc
+ - qcom,sa8775p-pcie-anoc
+ - qcom,sa8775p-system-noc
+ then:
+ properties:
+ clocks: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,sa8775p-clk-virt
+ - qcom,sa8775p-mc-virt
+ then:
+ properties:
+ reg: false
+ clocks: false
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml
index 51bad2e8d6f1..4f9013d36874 100644
--- a/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml
@@ -88,7 +88,7 @@ patternProperties:
pcie1_clkreq, pcie1_wakeup, pmic0, pmic1, ptp, ptp_clk,
ptp_trig, pwm0, pwm1, pwm2, pwm3, rgmii, sdio0, sdio_sb, smi,
spi_cs1, spi_cs2, spi_cs3, spi_quad, uart1, uart2,
- usb2_drvvbus1, usb32_drvvbus ]
+ usb2_drvvbus1, usb32_drvvbus0 ]
function:
enum: [ drvbus, emmc, gpio, i2c, jtag, led, mii, mii_err, onewire,
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
index ba9d8767c5d5..9c1baae767c4 100644
--- a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
+++ b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml
@@ -15,12 +15,15 @@ description:
sound quallity, which is a new high efficiency, low
noise, constant large volume, 6th Smart K audio amplifier.
-allOf:
- - $ref: dai-common.yaml#
-
properties:
compatible:
- const: awinic,aw87390
+ oneOf:
+ - enum:
+ - awinic,aw87390
+ - items:
+ - enum:
+ - anbernic,rgds-amp
+ - const: awinic,aw87391
reg:
maxItems: 1
@@ -40,10 +43,31 @@ required:
- compatible
- reg
- "#sound-dai-cells"
- - awinic,audio-channel
unevaluatedProperties: false
+allOf:
+ - $ref: dai-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - awinic,aw87390
+ then:
+ required:
+ - awinic,audio-channel
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - anbernic,rgds-amp
+ then:
+ properties:
+ vdd-supply: true
+
examples:
- |
i2c {
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
index bb92d6ca3144..994d68c074a9 100644
--- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
+++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
@@ -33,6 +33,8 @@ properties:
reset-gpios:
maxItems: 1
+ dvdd-supply: true
+
awinic,audio-channel:
description:
It is used to distinguish multiple PA devices, so that different
@@ -65,6 +67,17 @@ allOf:
then:
properties:
reset-gpios: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: awinic,aw88261
+ then:
+ required:
+ - dvdd-supply
+ else:
+ properties:
+ dvdd-supply: false
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
index beef193aaaeb..87559d0d079a 100644
--- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
@@ -40,11 +40,33 @@ properties:
tdm-slots:
$ref: /schemas/types.yaml#/definitions/uint32
description:
- number of channels over one serializer
- the property is ignored in DIT mode
+ Number of channels over one serializer. This property
+ specifies the TX playback TDM slot count, along with default RX slot count
+ if tdm-slots-rx is not specified.
+ The property is ignored in DIT mode.
minimum: 2
maximum: 32
+ tdm-slots-rx:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Number of RX capture channels over one serializer. If specified,
+ allows independent RX TDM slot count separate from TX. Requires
+ ti,async-mode to be enabled for independent TX/RX clock rates.
+ The property is ignored in DIT mode.
+ minimum: 2
+ maximum: 32
+
+ ti,async-mode:
+ description:
+ Specify to allow independent TX & RX clocking,
+ to enable audio playback & record with different sampling rate,
+ and different number of bits per frame.
+ if property is omitted, TX and RX will share same bit clock and frame clock signals,
+ thus RX need to use same bits per frame and sampling rate as TX in synchronous mode.
+ the property is ignored in DIT mode (as DIT is TX-only)
+ type: boolean
+
serial-dir:
description:
A list of serializer configuration
@@ -125,7 +147,21 @@ properties:
auxclk-fs-ratio:
$ref: /schemas/types.yaml#/definitions/uint32
- description: ratio of AUCLK and FS rate if applicable
+ description:
+ Ratio of AUCLK and FS rate if applicable. This property specifies
+ the TX ratio, along with default RX ratio if auxclk-fs-ratio-rx
+ is not specified.
+ When not specified, the inputted system clock frequency via set_sysclk
+ callback by the machine driver is used for divider calculation.
+
+ auxclk-fs-ratio-rx:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Ratio of AUCLK and FS rate for RX. If specified, allows
+ for a different RX ratio. Requires ti,async-mode to be
+ enabled when the ratio differs from auxclk-fs-ratio.
+ When not specified, it defaults to the value of auxclk-fs-ratio.
+ The property is ignored in DIT mode.
gpio-controller: true
@@ -170,14 +206,38 @@ allOf:
- $ref: dai-common.yaml#
- if:
properties:
- opmode:
+ op-mode:
enum:
- 0
-
then:
required:
- tdm-slots
+ - if:
+ properties:
+ op-mode:
+ const: 1
+ then:
+ properties:
+ tdm-slots: false
+ tdm-slots-rx: false
+ ti,async-mode: false
+ auxclk-fs-ratio-rx: false
+
+ - if:
+ required:
+ - tdm-slots-rx
+ then:
+ required:
+ - ti,async-mode
+
+ - if:
+ required:
+ - auxclk-fs-ratio-rx
+ then:
+ required:
+ - ti,async-mode
+
unevaluatedProperties: false
examples:
@@ -190,6 +250,7 @@ examples:
interrupt-names = "tx", "rx";
op-mode = <0>; /* MCASP_IIS_MODE */
tdm-slots = <2>;
+ ti,async-mode;
dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>;
dma-names = "tx", "rx";
serial-dir = <
diff --git a/Documentation/devicetree/bindings/sound/everest,es8389.yaml b/Documentation/devicetree/bindings/sound/everest,es8389.yaml
index a673df485ab3..75ce0bc48904 100644
--- a/Documentation/devicetree/bindings/sound/everest,es8389.yaml
+++ b/Documentation/devicetree/bindings/sound/everest,es8389.yaml
@@ -30,10 +30,20 @@ properties:
"#sound-dai-cells":
const: 0
+ vdda-supply:
+ description:
+ Analogue power supply.
+
+ vddd-supply:
+ description:
+ Interface power supply.
+
required:
- compatible
- reg
- "#sound-dai-cells"
+ - vddd-supply
+ - vdda-supply
additionalProperties: false
@@ -46,5 +56,7 @@ examples:
compatible = "everest,es8389";
reg = <0x10>;
#sound-dai-cells = <0>;
+ vddd-supply = <&vdd3v3>;
+ vdda-supply = <&vdd3v3>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/fsl,audmix.yaml b/Documentation/devicetree/bindings/sound/fsl,audmix.yaml
index 3ad197b3c82c..07b9a38761f2 100644
--- a/Documentation/devicetree/bindings/sound/fsl,audmix.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,audmix.yaml
@@ -34,7 +34,9 @@ description: |
properties:
compatible:
- const: fsl,imx8qm-audmix
+ enum:
+ - fsl,imx8qm-audmix
+ - fsl,imx952-audmix
reg:
maxItems: 1
@@ -80,7 +82,17 @@ required:
- reg
- clocks
- clock-names
- - power-domains
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,imx8qm-audmix
+ then:
+ required:
+ - power-domains
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
index c9152bac7421..608defc93c1e 100644
--- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
@@ -25,6 +25,7 @@ properties:
- fsl,imx53-asrc
- fsl,imx8qm-asrc
- fsl,imx8qxp-asrc
+ - fsl,imx952-asrc
- items:
- enum:
- fsl,imx6sx-asrc
diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml
index 1415247c92c8..bcc265a742c7 100644
--- a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml
@@ -68,6 +68,16 @@ allOf:
compatible:
contains:
enum:
+ - fsl,imx6sx-mqs
+ - fsl,imx93-mqs
+ then:
+ required:
+ - gpr
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- fsl,imx8qm-mqs
- fsl,imx8qxp-mqs
then:
@@ -91,8 +101,6 @@ allOf:
clock-names:
items:
- const: mclk
- required:
- - gpr
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
index 3d5d435c765b..3a32f7517d0c 100644
--- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
@@ -22,14 +22,20 @@ allOf:
properties:
compatible:
- enum:
- - fsl,imx7ulp-rpmsg-audio
- - fsl,imx8mn-rpmsg-audio
- - fsl,imx8mm-rpmsg-audio
- - fsl,imx8mp-rpmsg-audio
- - fsl,imx8ulp-rpmsg-audio
- - fsl,imx93-rpmsg-audio
- - fsl,imx95-rpmsg-audio
+ oneOf:
+ - enum:
+ - fsl,imx7ulp-rpmsg-audio
+ - fsl,imx8mn-rpmsg-audio
+ - fsl,imx8mm-rpmsg-audio
+ - fsl,imx8mp-rpmsg-audio
+ - fsl,imx8ulp-rpmsg-audio
+ - fsl,imx93-rpmsg-audio
+ - fsl,imx95-rpmsg-audio
+ - items:
+ - enum:
+ - fsl,imx94-rpmsg-audio
+ - fsl,imx952-rpmsg-audio
+ - const: fsl,imx95-rpmsg-audio
clocks:
items:
diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
index d838ee0b61cb..83b5ea5f3d70 100644
--- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
@@ -133,6 +133,13 @@ properties:
- description: dataline mask for 'rx'
- description: dataline mask for 'tx'
+ fsl,sai-amix-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ The audmix module is bypassed from hardware or not.
+ enum: [none, bypass, audmix]
+ default: none
+
fsl,sai-mclk-direction-output:
description: SAI will output the SAI MCLK clock.
type: boolean
@@ -180,6 +187,15 @@ allOf:
properties:
fsl,sai-synchronous-rx: false
+ - if:
+ required:
+ - fsl,sai-amix-mode
+ then:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx952-sai
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml b/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml
new file mode 100644
index 000000000000..d395a5cbc945
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/google,goldfish-audio.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/google,goldfish-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Android Goldfish Audio
+
+maintainers:
+ - Kuan-Wei Chiu <visitorckw@gmail.com>
+
+description:
+ Android goldfish audio device generated by Android emulator.
+
+properties:
+ compatible:
+ const: google,goldfish-audio
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ sound@9030000 {
+ compatible = "google,goldfish-audio";
+ reg = <0x9030000 0x100>;
+ interrupts = <4>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
index 8ddf49b0040d..16ae3328f70d 100644
--- a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml
@@ -47,16 +47,118 @@ properties:
- description: AFE clock
- description: ADDA DAC clock
- description: ADDA DAC pre-distortion clock
- - description: audio infra sys clock
- - description: audio infra 26M clock
+ - description: ADDA ADC clock
+ - description: ADDA6 ADC clock
+ - description: Audio low-jitter 22.5792m clock
+ - description: Audio low-jitter 24.576m clock
+ - description: Audio PLL1 tuner clock
+ - description: Audio PLL2 tuner clock
+ - description: Audio Time-Division Multiplexing interface clock
+ - description: ADDA ADC Sine Generator clock
+ - description: audio Non-LE clock
+ - description: Audio DAC High-Resolution clock
+ - description: Audio High-Resolution ADC clock
+ - description: Audio High-Resolution ADC SineGen clock
+ - description: Audio ADDA6 High-Resolution ADC clock
+ - description: Tertiary ADDA DAC clock
+ - description: Tertiary ADDA DAC pre-distortion clock
+ - description: Tertiary ADDA DAC Sine Generator clock
+ - description: Tertiary ADDA DAC High-Resolution clock
+ - description: Audio infra sys clock
+ - description: Audio infra 26M clock
+ - description: Mux for audio clock
+ - description: Mux for audio internal bus clock
+ - description: Mux main divider by 4
+ - description: Primary audio mux
+ - description: Primary audio PLL
+ - description: Secondary audio mux
+ - description: Secondary audio PLL
+ - description: Primary audio en-generator clock
+ - description: Primary PLL divider by 4 for IEC
+ - description: Secondary audio en-generator clock
+ - description: Secondary PLL divider by 4 for IEC
+ - description: Mux selector for I2S port 0
+ - description: Mux selector for I2S port 1
+ - description: Mux selector for I2S port 2
+ - description: Mux selector for I2S port 3
+ - description: Mux selector for I2S port 4
+ - description: Mux selector for I2S port 5
+ - description: Mux selector for I2S port 6
+ - description: Mux selector for I2S port 7
+ - description: Mux selector for I2S port 8
+ - description: Mux selector for I2S port 9
+ - description: APLL1 and APLL2 divider for I2S port 0
+ - description: APLL1 and APLL2 divider for I2S port 1
+ - description: APLL1 and APLL2 divider for I2S port 2
+ - description: APLL1 and APLL2 divider for I2S port 3
+ - description: APLL1 and APLL2 divider for I2S port 4
+ - description: APLL1 and APLL2 divider for IEC
+ - description: APLL1 and APLL2 divider for I2S port 5
+ - description: APLL1 and APLL2 divider for I2S port 6
+ - description: APLL1 and APLL2 divider for I2S port 7
+ - description: APLL1 and APLL2 divider for I2S port 8
+ - description: APLL1 and APLL2 divider for I2S port 9
+ - description: Top mux for audio subsystem
+ - description: 26MHz clock for audio subsystem
clock-names:
items:
- const: aud_afe_clk
- const: aud_dac_clk
- const: aud_dac_predis_clk
+ - const: aud_adc_clk
+ - const: aud_adda6_adc_clk
+ - const: aud_apll22m_clk
+ - const: aud_apll24m_clk
+ - const: aud_apll1_tuner_clk
+ - const: aud_apll2_tuner_clk
+ - const: aud_tdm_clk
+ - const: aud_tml_clk
+ - const: aud_nle
+ - const: aud_dac_hires_clk
+ - const: aud_adc_hires_clk
+ - const: aud_adc_hires_tml
+ - const: aud_adda6_adc_hires_clk
+ - const: aud_3rd_dac_clk
+ - const: aud_3rd_dac_predis_clk
+ - const: aud_3rd_dac_tml
+ - const: aud_3rd_dac_hires_clk
- const: aud_infra_clk
- const: aud_infra_26m_clk
+ - const: top_mux_audio
+ - const: top_mux_audio_int
+ - const: top_mainpll_d4_d4
+ - const: top_mux_aud_1
+ - const: top_apll1_ck
+ - const: top_mux_aud_2
+ - const: top_apll2_ck
+ - const: top_mux_aud_eng1
+ - const: top_apll1_d4
+ - const: top_mux_aud_eng2
+ - const: top_apll2_d4
+ - const: top_i2s0_m_sel
+ - const: top_i2s1_m_sel
+ - const: top_i2s2_m_sel
+ - const: top_i2s3_m_sel
+ - const: top_i2s4_m_sel
+ - const: top_i2s5_m_sel
+ - const: top_i2s6_m_sel
+ - const: top_i2s7_m_sel
+ - const: top_i2s8_m_sel
+ - const: top_i2s9_m_sel
+ - const: top_apll12_div0
+ - const: top_apll12_div1
+ - const: top_apll12_div2
+ - const: top_apll12_div3
+ - const: top_apll12_div4
+ - const: top_apll12_divb
+ - const: top_apll12_div5
+ - const: top_apll12_div6
+ - const: top_apll12_div7
+ - const: top_apll12_div8
+ - const: top_apll12_div9
+ - const: top_mux_audio_h
+ - const: top_clk26m_clk
required:
- compatible
@@ -83,23 +185,69 @@ examples:
afe: mt8192-afe-pcm {
compatible = "mediatek,mt8192-audio";
interrupts = <GIC_SPI 202 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&audsys CLK_AUD_AFE>, <&audsys CLK_AUD_DAC>,
+ <&audsys CLK_AUD_DAC_PREDIS>, <&audsys CLK_AUD_ADC>,
+ <&audsys CLK_AUD_ADDA6_ADC>, <&audsys CLK_AUD_22M>,
+ <&audsys CLK_AUD_24M>, <&audsys CLK_AUD_APLL_TUNER>,
+ <&audsys CLK_AUD_APLL2_TUNER>, <&audsys CLK_AUD_TDM>,
+ <&audsys CLK_AUD_TML>, <&audsys CLK_AUD_NLE>,
+ <&audsys CLK_AUD_DAC_HIRES>, <&audsys CLK_AUD_ADC_HIRES>,
+ <&audsys CLK_AUD_ADC_HIRES_TML>, <&audsys CLK_AUD_ADDA6_ADC_HIRES>,
+ <&audsys CLK_AUD_3RD_DAC>, <&audsys CLK_AUD_3RD_DAC_PREDIS>,
+ <&audsys CLK_AUD_3RD_DAC_TML>, <&audsys CLK_AUD_3RD_DAC_HIRES>,
+ <&infracfg CLK_INFRA_AUDIO>, <&infracfg CLK_INFRA_AUDIO_26M_B>,
+ <&topckgen CLK_TOP_AUDIO_SEL>, <&topckgen CLK_TOP_AUD_INTBUS_SEL>,
+ <&topckgen CLK_TOP_MAINPLL_D4_D4>, <&topckgen CLK_TOP_AUD_1_SEL>,
+ <&topckgen CLK_TOP_APLL1>, <&topckgen CLK_TOP_AUD_2_SEL>,
+ <&topckgen CLK_TOP_APLL2>, <&topckgen CLK_TOP_AUD_ENGEN1_SEL>,
+ <&topckgen CLK_TOP_APLL1_D4>, <&topckgen CLK_TOP_AUD_ENGEN2_SEL>,
+ <&topckgen CLK_TOP_APLL2_D4>, <&topckgen CLK_TOP_APLL_I2S0_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S1_M_SEL>, <&topckgen CLK_TOP_APLL_I2S2_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S3_M_SEL>, <&topckgen CLK_TOP_APLL_I2S4_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S5_M_SEL>, <&topckgen CLK_TOP_APLL_I2S6_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S7_M_SEL>, <&topckgen CLK_TOP_APLL_I2S8_M_SEL>,
+ <&topckgen CLK_TOP_APLL_I2S9_M_SEL>, <&topckgen CLK_TOP_APLL12_DIV0>,
+ <&topckgen CLK_TOP_APLL12_DIV1>, <&topckgen CLK_TOP_APLL12_DIV2>,
+ <&topckgen CLK_TOP_APLL12_DIV3>, <&topckgen CLK_TOP_APLL12_DIV4>,
+ <&topckgen CLK_TOP_APLL12_DIVB>, <&topckgen CLK_TOP_APLL12_DIV5>,
+ <&topckgen CLK_TOP_APLL12_DIV6>, <&topckgen CLK_TOP_APLL12_DIV7>,
+ <&topckgen CLK_TOP_APLL12_DIV8>, <&topckgen CLK_TOP_APLL12_DIV9>,
+ <&topckgen CLK_TOP_AUDIO_H_SEL>, <&clk26m>;
+ clock-names = "aud_afe_clk", "aud_dac_clk",
+ "aud_dac_predis_clk", "aud_adc_clk",
+ "aud_adda6_adc_clk", "aud_apll22m_clk",
+ "aud_apll24m_clk", "aud_apll1_tuner_clk",
+ "aud_apll2_tuner_clk", "aud_tdm_clk",
+ "aud_tml_clk", "aud_nle",
+ "aud_dac_hires_clk", "aud_adc_hires_clk",
+ "aud_adc_hires_tml", "aud_adda6_adc_hires_clk",
+ "aud_3rd_dac_clk", "aud_3rd_dac_predis_clk",
+ "aud_3rd_dac_tml", "aud_3rd_dac_hires_clk",
+ "aud_infra_clk", "aud_infra_26m_clk",
+ "top_mux_audio", "top_mux_audio_int",
+ "top_mainpll_d4_d4", "top_mux_aud_1",
+ "top_apll1_ck", "top_mux_aud_2",
+ "top_apll2_ck", "top_mux_aud_eng1",
+ "top_apll1_d4", "top_mux_aud_eng2",
+ "top_apll2_d4", "top_i2s0_m_sel",
+ "top_i2s1_m_sel", "top_i2s2_m_sel",
+ "top_i2s3_m_sel", "top_i2s4_m_sel",
+ "top_i2s5_m_sel", "top_i2s6_m_sel",
+ "top_i2s7_m_sel", "top_i2s8_m_sel",
+ "top_i2s9_m_sel", "top_apll12_div0",
+ "top_apll12_div1", "top_apll12_div2",
+ "top_apll12_div3", "top_apll12_div4",
+ "top_apll12_divb", "top_apll12_div5",
+ "top_apll12_div6", "top_apll12_div7",
+ "top_apll12_div8", "top_apll12_div9",
+ "top_mux_audio_h", "top_clk26m_clk";
+ memory-region = <&afe_dma_mem>;
+ power-domains = <&scpsys MT8192_POWER_DOMAIN_AUDIO>;
resets = <&watchdog MT8192_TOPRGU_AUDIO_SW_RST>;
reset-names = "audiosys";
mediatek,apmixedsys = <&apmixedsys>;
mediatek,infracfg = <&infracfg>;
mediatek,topckgen = <&topckgen>;
- power-domains = <&scpsys MT8192_POWER_DOMAIN_AUDIO>;
- clocks = <&audsys CLK_AUD_AFE>,
- <&audsys CLK_AUD_DAC>,
- <&audsys CLK_AUD_DAC_PREDIS>,
- <&infracfg CLK_INFRA_AUDIO>,
- <&infracfg CLK_INFRA_AUDIO_26M_B>;
- clock-names = "aud_afe_clk",
- "aud_dac_clk",
- "aud_dac_predis_clk",
- "aud_infra_clk",
- "aud_infra_26m_clk";
- memory-region = <&afe_dma_mem>;
};
...
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml
new file mode 100644
index 000000000000..981ebc39b195
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,rt5575.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,rt5575.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ALC5575 audio CODEC
+
+maintainers:
+ - Oder Chiou <oder_chiou@realtek.com>
+
+description:
+ The device supports both I2C and SPI. I2C is mandatory, while SPI is
+ optional depending on the hardware configuration. SPI is used for
+ firmware loading if present.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: realtek,rt5575
+
+ reg:
+ maxItems: 1
+
+ spi-parent:
+ description:
+ Optional phandle reference to the SPI controller used for firmware
+ loading. The argument specifies the chip select.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ # I2C-only node
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@57 {
+ compatible = "realtek,rt5575";
+ reg = <0x57>;
+ };
+ };
+
+ # I2C + optional SPI node
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@57 {
+ compatible = "realtek,rt5575";
+ reg = <0x57>;
+ spi-parent = <&spi0 0>; /* chip-select 0 */
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml
new file mode 100644
index 000000000000..dc4f2eef7cf9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,rt5651.yaml
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,rt5651.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RT5651 audio CODEC
+
+maintainers:
+ - Bard Liao <bardliao@realtek.com>
+
+description: >
+ This device supports I2C only.
+
+ Pins on the device (for linking into audio routes) for RT5651:
+
+ * DMIC L1
+ * DMIC R1
+ * IN1P
+ * IN2P
+ * IN2N
+ * IN3P
+ * HPOL
+ * HPOR
+ * LOUTL
+ * LOUTR
+ * PDML
+ * PDMR
+
+allOf:
+ - $ref: /schemas/sound/dai-common.yaml#
+
+properties:
+ compatible:
+ const: realtek,rt5651
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: mclk
+
+ '#sound-dai-cells':
+ const: 0
+
+ realtek,in2-differential:
+ type: boolean
+ description: Indicate MIC2 input are differential, rather than single-ended.
+
+ realtek,dmic-en:
+ type: boolean
+ description: Indicates DMIC is used.
+
+ realtek,jack-detect-source:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Select jack-detect input pin.
+ enum: [1, 2, 3]
+
+ realtek,jack-detect-not-inverted:
+ type: boolean
+ description:
+ Normal jack-detect switches give an inverted (active-low) signal. Set this
+ bool in the rare case you've a jack-detect switch which is not inverted.
+
+ realtek,over-current-threshold-microamp:
+ description: Micbias over-current detection threshold in µA.
+ enum: [600, 1500, 2000]
+
+ realtek,over-current-scale-factor:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Micbias over-current detection scale factor:
+
+ 0: scale current by 0.5
+ 1: scale current by 0.75
+ 2: scale current by 1.0
+ 3: scale current by 1.5
+ enum: [0, 1, 2, 3]
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1a {
+ compatible = "realtek,rt5651";
+ reg = <0x1a>;
+ realtek,dmic-en;
+ realtek,in2-differential;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml b/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml
index d54686a19ab7..a125663988a5 100644
--- a/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml
+++ b/Documentation/devicetree/bindings/sound/richtek,rtq9128.yaml
@@ -14,13 +14,21 @@ description:
class-D audio power amplifier and delivering 4x75W into 4OHm at 10%
THD+N from a 25V supply in automotive applications.
+ The RTQ9154 is the family series of RTQ9128. The major change is to modify
+ the package size. Beside this, whole functions are almost all the same.
+
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
- enum:
- - richtek,rtq9128
+ oneOf:
+ - enum:
+ - richtek,rtq9128
+ - items:
+ - enum:
+ - richtek,rtq9154
+ - const: richtek,rtq9128
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/sound/rt5651.txt b/Documentation/devicetree/bindings/sound/rt5651.txt
deleted file mode 100644
index 56e736a1cba9..000000000000
--- a/Documentation/devicetree/bindings/sound/rt5651.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-RT5651 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-- compatible : "realtek,rt5651".
-
-- reg : The I2C address of the device.
-
-Optional properties:
-
-- realtek,in2-differential
- Boolean. Indicate MIC2 input are differential, rather than single-ended.
-
-- realtek,dmic-en
- Boolean. true if dmic is used.
-
-- realtek,jack-detect-source
- u32. Valid values:
- 1: Use JD1_1 pin for jack-detect
- 2: Use JD1_2 pin for jack-detect
- 3: Use JD2 pin for jack-detect
-
-- realtek,jack-detect-not-inverted
- bool. Normal jack-detect switches give an inverted (active-low) signal,
- set this bool in the rare case you've a jack-detect switch which is not
- inverted.
-
-- realtek,over-current-threshold-microamp
- u32, micbias over-current detection threshold in µA, valid values are
- 600, 1500 and 2000µA.
-
-- realtek,over-current-scale-factor
- u32, micbias over-current detection scale-factor, valid values are:
- 0: Scale current by 0.5
- 1: Scale current by 0.75
- 2: Scale current by 1.0
- 3: Scale current by 1.5
-
-Pins on the device (for linking into audio routes) for RT5651:
-
- * DMIC L1
- * DMIC R1
- * IN1P
- * IN2P
- * IN2N
- * IN3P
- * HPOL
- * HPOR
- * LOUTL
- * LOUTR
- * PDML
- * PDMR
-
-Example:
-
-rt5651: codec@1a {
- compatible = "realtek,rt5651";
- reg = <0x1a>;
- realtek,dmic-en = "true";
- realtek,in2-diff = "false";
-};
diff --git a/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml
new file mode 100644
index 000000000000..7293a98e98c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-codecs.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/sophgo,cv1800b-codecs.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo CV1800B Internal ADC/DAC Codec
+
+maintainers:
+ - Anton D. Stavinskii <stavinsky@gmail.com>
+
+description:
+ Internal ADC and DAC audio codecs integrated in the Sophgo CV1800B SoC.
+ Codecs expose a single DAI and are intended to be connected
+ to an I2S/TDM controller via an ASoC machine driver.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - sophgo,cv1800b-sound-adc
+ - sophgo,cv1800b-sound-dac
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ audio-codec@300a100 {
+ compatible = "sophgo,cv1800b-sound-adc";
+ reg = <0x0300a100 0x100>;
+ #sound-dai-cells = <0>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml
new file mode 100644
index 000000000000..f08362b0ca5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/sophgo,cv1800b-i2s.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/sophgo,cv1800b-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo CV1800B I2S/TDM controller
+
+maintainers:
+ - Anton D. Stavinskii <stavinsky@gmail.com>
+
+description: I2S/TDM controller found in CV1800B / Sophgo SG2002/SG2000 SoCs.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: sophgo,cv1800b-i2s
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: i2s
+ - const: mclk
+
+ dmas:
+ minItems: 1
+ maxItems: 2
+
+ dma-names:
+ minItems: 1
+ items:
+ - enum: [rx, tx]
+ - const: tx
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/sophgo,cv1800.h>
+
+ i2s@4110000 {
+ compatible = "sophgo,cv1800b-i2s";
+ reg = <0x04110000 0x10000>;
+ clocks = <&clk CLK_APB_I2S1>, <&clk CLK_SDMA_AUD1>;
+ clock-names = "i2s", "mclk";
+ dmas = <&dmamux 2 1>, <&dmamux 3 1>;
+ dma-names = "rx", "tx";
+ #sound-dai-cells = <0>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt
deleted file mode 100644
index a7eecad83db1..000000000000
--- a/Documentation/devicetree/bindings/sound/tas2552.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Texas Instruments - tas2552 Codec module
-
-The tas2552 serial control bus communicates through I2C protocols
-
-Required properties:
- - compatible - One of:
- "ti,tas2552" - TAS2552
- - reg - I2C slave address: it can be 0x40 if ADDR pin is 0
- or 0x41 if ADDR pin is 1.
- - supply-*: Required supply regulators are:
- "vbat" battery voltage
- "iovdd" I/O Voltage
- "avdd" Analog DAC Voltage
-
-Optional properties:
- - enable-gpio - gpio pin to enable/disable the device
-
-tas2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or use the
-internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
-reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
-For system integration the dt-bindings/sound/tas2552.h header file provides
-defined values to select and configure the PLL and PDM reference clocks.
-
-Example:
-
-tas2552: tas2552@41 {
- compatible = "ti,tas2552";
- reg = <0x41>;
- vbat-supply = <&reg_vbat>;
- iovdd-supply = <&reg_iovdd>;
- avdd-supply = <&reg_avdd>;
- enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>;
-};
-
-For more product information please see the link below:
-https://www.ti.com/product/TAS2552
diff --git a/Documentation/devicetree/bindings/sound/ti,tas2552.yaml b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml
new file mode 100644
index 000000000000..10369aa5f0a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tas2552.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TAS2552 Codec
+
+maintainers:
+ - Shenghao Ding <shenghao-ding@ti.com>
+ - Kevin Lu <kevin-lu@ti.com>
+ - Baojun Xu <baojun.xu@ti.com>
+
+description: >
+ The TAS2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or
+ use the internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL,
+ the PDM reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
+
+ For system integration the dt-bindings/sound/tas2552.h header file provides
+ defined values to select and configure the PLL and PDM reference clocks.
+
+properties:
+ compatible:
+ const: ti,tas2552
+
+ reg:
+ maxItems: 1
+
+ vbat-supply: true
+ iovdd-supply: true
+ avdd-supply: true
+
+ enable-gpio:
+ maxItems: 1
+ description: gpio pin to enable/disable the device
+
+required:
+ - compatible
+ - reg
+ - vbat-supply
+ - iovdd-supply
+ - avdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@41 {
+ compatible = "ti,tas2552";
+ reg = <0x41>;
+ vbat-supply = <&reg_vbat>;
+ iovdd-supply = <&reg_iovdd>;
+ avdd-supply = <&reg_avdd>;
+ enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
index 876fa97bfbcd..a93de2debbb4 100644
--- a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml
@@ -41,8 +41,11 @@ properties:
areg-supply:
description: |
- Regulator with AVDD at 3.3V. If not defined then the internal regulator
- is enabled.
+ External supply of 1.8V. If not defined then the internal regulator is
+ enabled instead.
+
+ avdd-supply: true
+ iovdd-supply: true
ti,mic-bias-source:
description: |
diff --git a/Documentation/misc-devices/amd-sbi.rst b/Documentation/misc-devices/amd-sbi.rst
index 07ceb44fbe5e..f91ddadefe48 100644
--- a/Documentation/misc-devices/amd-sbi.rst
+++ b/Documentation/misc-devices/amd-sbi.rst
@@ -15,7 +15,7 @@ and SB Temperature Sensor Interface (SB-TSI)).
More details on the interface can be found in chapter
"5 Advanced Platform Management Link (APML)" of the family/model PPR [1]_.
-.. [1] https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55898_B1_pub_0_50.zip
+.. [1] https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50
SBRMI device
diff --git a/Documentation/mm/allocation-profiling.rst b/Documentation/mm/allocation-profiling.rst
index 316311240e6a..5389d241176a 100644
--- a/Documentation/mm/allocation-profiling.rst
+++ b/Documentation/mm/allocation-profiling.rst
@@ -33,6 +33,16 @@ Boot parameter:
sysctl:
/proc/sys/vm/mem_profiling
+ 1: Enable memory profiling.
+
+ 0: Disable memory profiling.
+
+ The default value depends on CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT.
+
+ When CONFIG_MEM_ALLOC_PROFILING_DEBUG=y, this control is read-only to avoid
+ warnings produced by allocations made while profiling is disabled and freed
+ when it's enabled.
+
Runtime info:
/proc/allocinfo
diff --git a/Documentation/netlink/specs/fou.yaml b/Documentation/netlink/specs/fou.yaml
index 8e7974ec453f..331f1b342b3a 100644
--- a/Documentation/netlink/specs/fou.yaml
+++ b/Documentation/netlink/specs/fou.yaml
@@ -39,6 +39,8 @@ attribute-sets:
-
name: ipproto
type: u8
+ checks:
+ min: 1
-
name: type
type: u8
diff --git a/Documentation/process/conclave.rst b/Documentation/process/conclave.rst
new file mode 100644
index 000000000000..6a1234f54612
--- /dev/null
+++ b/Documentation/process/conclave.rst
@@ -0,0 +1,41 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Linux kernel project continuity
+===============================
+
+The Linux kernel development project is widely distributed, with over
+100 maintainers each working to keep changes moving through their own
+repositories. The final step, though, is a centralized one where changes
+are pulled into the mainline repository. That is normally done by Linus
+Torvalds but, as was demonstrated by the 4.19 release in 2018, there are
+others who can do that work when the need arises.
+
+Should the maintainers of that repository become unwilling or unable to
+do that work going forward (including facilitating a transition), the
+project will need to find one or more replacements without delay. The
+process by which that will be done is listed below. $ORGANIZER is the
+last Maintainer Summit organizer or the current Linux Foundation (LF)
+Technical Advisory Board (TAB) Chair as a backup.
+
+- Within 72 hours, $ORGANIZER will open a discussion with the invitees
+ of the most recently concluded Maintainers Summit. A meeting of those
+ invitees and the TAB, either online or in-person, will be set as soon
+ as possible in a way that maximizes the number of people who can
+ participate.
+
+- If there has been no Maintainers Summit in the last 15 months, the set of
+ invitees for this meeting will be determined by the TAB.
+
+- The invitees to this meeting may bring in other maintainers as needed.
+
+- This meeting, chaired by $ORGANIZER, will consider options for the
+ ongoing management of the top-level kernel repository consistent with
+ the expectation that it maximizes the long term health of the project
+ and its community.
+
+- Within two weeks, a representative of this group will communicate to the
+ broader community, using the ksummit@lists.linux.dev mailing list, what
+ the next steps will be.
+
+The Linux Foundation, as guided by the TAB, will take the steps
+necessary to support and implement this plan.
diff --git a/Documentation/process/index.rst b/Documentation/process/index.rst
index aa12f2660194..492b808a6977 100644
--- a/Documentation/process/index.rst
+++ b/Documentation/process/index.rst
@@ -68,6 +68,7 @@ beyond).
stable-kernel-rules
management-style
researcher-guidelines
+ conclave
Dealing with bugs
-----------------
diff --git a/Documentation/process/maintainer-netdev.rst b/Documentation/process/maintainer-netdev.rst
index 989192421cc9..6bce4507d5d3 100644
--- a/Documentation/process/maintainer-netdev.rst
+++ b/Documentation/process/maintainer-netdev.rst
@@ -363,6 +363,18 @@ just do it. As a result, a sequence of smaller series gets merged quicker and
with better review coverage. Re-posting large series also increases the mailing
list traffic.
+Limit patches outstanding on mailing list
+-----------------------------------------
+
+Avoid having more than 15 patches, across all series, outstanding for
+review on the mailing list for a single tree. In other words, a maximum of
+15 patches under review on net, and a maximum of 15 patches under review on
+net-next.
+
+This limit is intended to focus developer effort on testing patches before
+upstream review. Aiding the quality of upstream submissions, and easing the
+load on reviewers.
+
.. _rcs:
Local variable ordering ("reverse xmas tree", "RCS")
diff --git a/MAINTAINERS b/MAINTAINERS
index da9dbc1a4019..f27485e54645 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1030,6 +1030,13 @@ L: dmaengine@vger.kernel.org
S: Supported
F: drivers/dma/amd/ae4dma/
+AMD ASoC DRIVERS
+M: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+R: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
+L: linux-sound@vger.kernel.org
+S: Supported
+F: sound/soc/amd/
+
AMD AXI W1 DRIVER
M: Kris Chaplin <kris.chaplin@amd.com>
R: Thomas Delev <thomas.delev@amd.com>
@@ -3132,6 +3139,7 @@ F: drivers/*/*ma35*
K: ma35d1
ARM/NUVOTON NPCM ARCHITECTURE
+M: Andrew Jeffery <andrew@codeconstruct.com.au>
M: Avi Fishman <avifishman70@gmail.com>
M: Tomer Maimon <tmaimon77@gmail.com>
M: Tali Perry <tali.perry1@gmail.com>
@@ -3140,6 +3148,7 @@ R: Nancy Yuen <yuenn@google.com>
R: Benjamin Fair <benjaminfair@google.com>
L: openbmc@lists.ozlabs.org (moderated for non-subscribers)
S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bmc/linux.git
F: Documentation/devicetree/bindings/*/*/*npcm*
F: Documentation/devicetree/bindings/*/*npcm*
F: Documentation/devicetree/bindings/rtc/nuvoton,nct3018y.yaml
@@ -9258,7 +9267,6 @@ F: drivers/scsi/be2iscsi/
EMULEX 10Gbps NIC BE2, BE3-R, Lancer, Skyhawk-R DRIVER (be2net)
M: Ajit Khaparde <ajit.khaparde@broadcom.com>
M: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
-M: Somnath Kotur <somnath.kotur@broadcom.com>
L: netdev@vger.kernel.org
S: Maintained
W: http://www.emulex.com
@@ -13170,6 +13178,7 @@ F: Documentation/devicetree/bindings/interconnect/
F: Documentation/driver-api/interconnect.rst
F: drivers/interconnect/
F: include/dt-bindings/interconnect/
+F: include/linux/interconnect-clk.h
F: include/linux/interconnect-provider.h
F: include/linux/interconnect.h
@@ -18484,9 +18493,8 @@ F: include/uapi/linux/nexthop.h
F: net/ipv4/nexthop.c
NFC SUBSYSTEM
-M: Krzysztof Kozlowski <krzk@kernel.org>
L: netdev@vger.kernel.org
-S: Maintained
+S: Orphan
F: Documentation/devicetree/bindings/net/nfc/
F: drivers/nfc/
F: include/net/nfc/
@@ -21103,6 +21111,10 @@ S: Maintained
F: rust/helpers/pwm.c
F: rust/kernel/pwm.rs
+PWM SUBSYSTEM DRIVERS [RUST]
+R: Michal Wilczynski <m.wilczynski@samsung.com>
+F: drivers/pwm/*.rs
+
PXA GPIO DRIVER
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-gpio@vger.kernel.org
@@ -22532,7 +22544,7 @@ F: drivers/mailbox/riscv-sbi-mpxy-mbox.c
F: include/linux/mailbox/riscv-rpmi-message.h
RISC-V SPACEMIT SoC Support
-M: Yixun Lan <dlan@gentoo.org>
+M: Yixun Lan <dlan@kernel.org>
L: linux-riscv@lists.infradead.org
L: spacemit@lists.linux.dev
S: Maintained
@@ -25781,7 +25793,7 @@ M: Kevin Lu <kevin-lu@ti.com>
M: Baojun Xu <baojun.xu@ti.com>
L: linux-sound@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/sound/tas2552.txt
+F: Documentation/devicetree/bindings/sound/ti,tas2552.yaml
F: Documentation/devicetree/bindings/sound/ti,tas2562.yaml
F: Documentation/devicetree/bindings/sound/ti,tas2770.yaml
F: Documentation/devicetree/bindings/sound/ti,tas27xx.yaml
diff --git a/Makefile b/Makefile
index 1465f715786d..bde507d5c03d 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 6
PATCHLEVEL = 19
SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION = -rc8
NAME = Baby Opossum Posse
# *DOCUMENTATION*
@@ -1624,7 +1624,8 @@ MRPROPER_FILES += include/config include/generated \
certs/x509.genkey \
vmlinux-gdb.py \
rpmbuild \
- rust/libmacros.so rust/libmacros.dylib
+ rust/libmacros.so rust/libmacros.dylib \
+ rust/libpin_init_internal.so rust/libpin_init_internal.dylib
# clean - Delete most, but leave enough to build external modules
#
diff --git a/arch/arm/boot/dts/microchip/lan966x-pcb8290.dts b/arch/arm/boot/dts/microchip/lan966x-pcb8290.dts
index 3b7577e48b46..50bd29572f3e 100644
--- a/arch/arm/boot/dts/microchip/lan966x-pcb8290.dts
+++ b/arch/arm/boot/dts/microchip/lan966x-pcb8290.dts
@@ -54,6 +54,7 @@
&mdio0 {
pinctrl-0 = <&miim_a_pins>;
pinctrl-names = "default";
+ reset-gpios = <&gpio 53 GPIO_ACTIVE_LOW>;
status = "okay";
ext_phy0: ethernet-phy@7 {
diff --git a/arch/arm/boot/dts/microchip/sama7d65.dtsi b/arch/arm/boot/dts/microchip/sama7d65.dtsi
index cd2cf9a6f40b..868045c650a7 100644
--- a/arch/arm/boot/dts/microchip/sama7d65.dtsi
+++ b/arch/arm/boot/dts/microchip/sama7d65.dtsi
@@ -527,7 +527,7 @@
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 37>;
#address-cells = <1>;
- #size-cells = <1>;
+ #size-cells = <0>;
dmas = <&dma0 AT91_XDMAC_DT_PERID(12)>,
<&dma0 AT91_XDMAC_DT_PERID(11)>;
dma-names = "tx", "rx";
@@ -676,7 +676,7 @@
flx9: flexcom@e2820000 {
compatible = "microchip,sama7d65-flexcom", "atmel,sama5d2-flexcom";
reg = <0xe2820000 0x200>;
- ranges = <0x0 0xe281c000 0x800>;
+ ranges = <0x0 0xe2820000 0x800>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 43>;
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm/mach-npcm/Kconfig b/arch/arm/mach-npcm/Kconfig
index 63b42a19d1b8..d933e8abb50f 100644
--- a/arch/arm/mach-npcm/Kconfig
+++ b/arch/arm/mach-npcm/Kconfig
@@ -30,7 +30,6 @@ config ARCH_NPCM7XX
select ARM_ERRATA_764369 if SMP
select ARM_ERRATA_720789
select ARM_ERRATA_754322
- select ARM_ERRATA_794072
select PL310_ERRATA_588369
select PL310_ERRATA_727915
select MFD_SYSCON
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 709da31d5785..137aa8375257 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -202,19 +202,6 @@
nvidia,outputs = <&dsia &dsib &sor0 &sor1>;
nvidia,head = <0>;
-
- interconnects = <&mc TEGRA210_MC_DISPLAY0A &emc>,
- <&mc TEGRA210_MC_DISPLAY0B &emc>,
- <&mc TEGRA210_MC_DISPLAY0C &emc>,
- <&mc TEGRA210_MC_DISPLAYHC &emc>,
- <&mc TEGRA210_MC_DISPLAYD &emc>,
- <&mc TEGRA210_MC_DISPLAYT &emc>;
- interconnect-names = "wina",
- "winb",
- "winc",
- "cursor",
- "wind",
- "wint";
};
dc@54240000 {
@@ -230,15 +217,6 @@
nvidia,outputs = <&dsia &dsib &sor0 &sor1>;
nvidia,head = <1>;
-
- interconnects = <&mc TEGRA210_MC_DISPLAY0AB &emc>,
- <&mc TEGRA210_MC_DISPLAY0BB &emc>,
- <&mc TEGRA210_MC_DISPLAY0CB &emc>,
- <&mc TEGRA210_MC_DISPLAYHCB &emc>;
- interconnect-names = "wina",
- "winb",
- "winc",
- "cursor";
};
dsia: dsi@54300000 {
@@ -1052,7 +1030,6 @@
#iommu-cells = <1>;
#reset-cells = <1>;
- #interconnect-cells = <1>;
};
emc: external-memory-controller@7001b000 {
@@ -1066,7 +1043,6 @@
nvidia,memory-controller = <&mc>;
operating-points-v2 = <&emc_icc_dvfs_opp_table>;
- #interconnect-cells = <0>;
#cooling-cells = <2>;
};
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
index 5334adebf278..b9e0d9c7c065 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
@@ -5788,8 +5788,12 @@
clocks = <&rpmhcc RPMH_CXO_CLK>;
clock-names = "xo";
- power-domains = <&rpmhpd SC8280XP_NSP>;
- power-domain-names = "nsp";
+ power-domains = <&rpmhpd SC8280XP_NSP>,
+ <&rpmhpd SC8280XP_CX>,
+ <&rpmhpd SC8280XP_MXC>;
+ power-domain-names = "nsp",
+ "cx",
+ "mxc";
memory-region = <&pil_nsp0_mem>;
@@ -5919,8 +5923,12 @@
clocks = <&rpmhcc RPMH_CXO_CLK>;
clock-names = "xo";
- power-domains = <&rpmhpd SC8280XP_NSP>;
- power-domain-names = "nsp";
+ power-domains = <&rpmhpd SC8280XP_NSP>,
+ <&rpmhpd SC8280XP_CX>,
+ <&rpmhpd SC8280XP_MXC>;
+ power-domain-names = "nsp",
+ "cx",
+ "mxc";
memory-region = <&pil_nsp1_mem>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-enchilada.dts b/arch/arm64/boot/dts/qcom/sdm845-oneplus-enchilada.dts
index a259eb9d45ae..8aead6dc25e0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-enchilada.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-enchilada.dts
@@ -31,9 +31,9 @@
};
&display_panel {
- status = "okay";
+ compatible = "samsung,sofef00-ams628nw01", "samsung,sofef00";
- compatible = "samsung,sofef00";
+ status = "okay";
};
&bq27441_fg {
diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi
index 2ca9e50ef599..e3f93f4f412d 100644
--- a/arch/arm64/boot/dts/qcom/sm8550.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi
@@ -4133,8 +4133,6 @@
usb_1: usb@a600000 {
compatible = "qcom,sm8550-dwc3", "qcom,snps-dwc3";
reg = <0x0 0x0a600000 0x0 0xfc100>;
- #address-cells = <1>;
- #size-cells = <0>;
clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
<&gcc GCC_USB30_PRIM_MASTER_CLK>,
diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi
index 07ae74851621..f8e1950a74ac 100644
--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi
@@ -5150,9 +5150,6 @@
dma-coherent;
- #address-cells = <1>;
- #size-cells = <0>;
-
status = "disabled";
ports {
diff --git a/arch/arm64/boot/dts/qcom/talos.dtsi b/arch/arm64/boot/dts/qcom/talos.dtsi
index d1dbfa3bd81c..95d26e313622 100644
--- a/arch/arm64/boot/dts/qcom/talos.dtsi
+++ b/arch/arm64/boot/dts/qcom/talos.dtsi
@@ -1399,10 +1399,10 @@
<&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>,
<&gcc GCC_UFS_PHY_AHB_CLK>,
<&gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>,
- <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
<&rpmhcc RPMH_CXO_CLK>,
<&gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>,
- <&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>;
+ <&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>,
+ <&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
clock-names = "core_clk",
"bus_aggr_clk",
"iface_clk",
diff --git a/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts b/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts
index e5e6b800c2d1..3473db08b9b2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3308-sakurapi-rk3308b.dts
@@ -199,7 +199,7 @@
compatible = "brcm,bcm43455-fmac", "brcm,bcm4329-fmac";
reg = <1>;
interrupt-parent = <&gpio0>;
- interrupts = <RK_PA3 GPIO_ACTIVE_HIGH>;
+ interrupts = <RK_PA3 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "host-wake";
pinctrl-names = "default";
pinctrl-0 = <&wifi_host_wake>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3326-odroid-go3.dts b/arch/arm64/boot/dts/rockchip/rk3326-odroid-go3.dts
index 35bbaf559ca3..6b0563cb4d3a 100644
--- a/arch/arm64/boot/dts/rockchip/rk3326-odroid-go3.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3326-odroid-go3.dts
@@ -14,7 +14,8 @@
joystick_mux_controller: mux-controller {
compatible = "gpio-mux";
- pinctrl = <&mux_en_pins>;
+ pinctrl-0 = <&mux_en_pins>;
+ pinctrl-names = "default";
#mux-control-cells = <0>;
mux-gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_LOW>,
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-kobol-helios64.dts b/arch/arm64/boot/dts/rockchip/rk3399-kobol-helios64.dts
index e7d4a2f9a95e..b2de018a7d36 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-kobol-helios64.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-kobol-helios64.dts
@@ -424,9 +424,7 @@
&pcie0 {
ep-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_HIGH>;
- max-link-speed = <2>;
num-lanes = <2>;
- pinctrl-names = "default";
status = "okay";
vpcie12v-supply = <&vcc12v_dcin>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dtsi
index 8d94d9f91a5c..3a9a10f531bd 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-r4s.dtsi
@@ -71,7 +71,6 @@
};
&pcie0 {
- max-link-speed = <1>;
num-lanes = <1>;
vpcie3v3-supply = <&vcc3v3_sys>;
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
index eaaca08a7601..810ab6ff4e67 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
@@ -969,7 +969,6 @@
};
&spi1 {
- max-freq = <10000000>;
status = "okay";
spiflash: flash@0 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
index 2dca1dca20b8..5de964d369b0 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -40,13 +40,13 @@
button-up {
label = "Volume Up";
linux,code = <KEY_VOLUMEUP>;
- press-threshold-microvolt = <100000>;
+ press-threshold-microvolt = <2000>;
};
button-down {
label = "Volume Down";
linux,code = <KEY_VOLUMEDOWN>;
- press-threshold-microvolt = <600000>;
+ press-threshold-microvolt = <300000>;
};
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi
index 587e89d7fc5e..8299e9d10c7c 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-puma.dtsi
@@ -483,7 +483,7 @@
pinctrl-names = "default";
pinctrl-0 = <&q7_thermal_pin &bios_disable_override_hog_pin>;
- gpios {
+ gpio-pins {
bios_disable_override_hog_pin: bios-disable-override-hog-pin {
rockchip,pins =
<3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_down>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts
index 74160cf89188..6d52e3723a4e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-4c-plus.dts
@@ -529,11 +529,11 @@
rockchip,pins = <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
};
- vsel1_gpio: vsel1-gpio {
+ vsel1_gpio: vsel1-gpio-pin {
rockchip,pins = <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
};
- vsel2_gpio: vsel2-gpio {
+ vsel2_gpio: vsel2-gpio-pin {
rockchip,pins = <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
};
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-display-vz.dtso b/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-display-vz.dtso
index 70c23e1bf14b..d1a906031912 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-display-vz.dtso
+++ b/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-display-vz.dtso
@@ -11,7 +11,6 @@
#include "rk3568-wolfvision-pf5-display.dtsi"
&st7789 {
- compatible = "jasonic,jt240mhqs-hwt-ek-e3",
- "sitronix,st7789v";
+ compatible = "jasonic,jt240mhqs-hwt-ek-e3";
rotation = <270>;
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts b/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts
index cce34c541f7c..bb2cc2814b83 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3576-nanopi-m5.dts
@@ -201,6 +201,7 @@
pinctrl-names = "default";
pinctrl-0 = <&hp_det_l>;
+ simple-audio-card,bitclock-master = <&masterdai>;
simple-audio-card,format = "i2s";
simple-audio-card,hp-det-gpios = <&gpio2 RK_PD6 GPIO_ACTIVE_LOW>;
simple-audio-card,mclk-fs = <256>;
@@ -211,15 +212,16 @@
"Headphones", "HPOR",
"IN1P", "Microphone Jack";
simple-audio-card,widgets =
- "Headphone", "Headphone Jack",
+ "Headphone", "Headphones",
"Microphone", "Microphone Jack";
simple-audio-card,codec {
sound-dai = <&rt5616>;
};
- simple-audio-card,cpu {
+ masterdai: simple-audio-card,cpu {
sound-dai = <&sai2>;
+ system-clock-frequency = <12288000>;
};
};
};
@@ -727,10 +729,12 @@
rt5616: audio-codec@1b {
compatible = "realtek,rt5616";
reg = <0x1b>;
- assigned-clocks = <&cru CLK_SAI2_MCLKOUT>;
+ assigned-clocks = <&cru CLK_SAI2_MCLKOUT_TO_IO>;
assigned-clock-rates = <12288000>;
- clocks = <&cru CLK_SAI2_MCLKOUT>;
+ clocks = <&cru CLK_SAI2_MCLKOUT_TO_IO>;
clock-names = "mclk";
+ pinctrl-0 = <&sai2m0_mclk>;
+ pinctrl-names = "default";
#sound-dai-cells = <0>;
};
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
index a86fc6b4e8c4..c72343e7a045 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
@@ -1261,7 +1261,7 @@
gpu: gpu@27800000 {
compatible = "rockchip,rk3576-mali", "arm,mali-bifrost";
- reg = <0x0 0x27800000 0x0 0x200000>;
+ reg = <0x0 0x27800000 0x0 0x20000>;
assigned-clocks = <&scmi_clk SCMI_CLK_GPU>;
assigned-clock-rates = <198000000>;
clocks = <&cru CLK_GPU>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 2a7921793020..7ab12d1054a7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1200,7 +1200,7 @@
status = "disabled";
};
- rknn_mmu_1: iommu@fdac9000 {
+ rknn_mmu_1: iommu@fdaca000 {
compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
reg = <0x0 0xfdaca000 0x0 0x100>;
interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH 0>;
@@ -1230,7 +1230,7 @@
status = "disabled";
};
- rknn_mmu_2: iommu@fdad9000 {
+ rknn_mmu_2: iommu@fdada000 {
compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
reg = <0x0 0xfdada000 0x0 0x100>;
interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH 0>;
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 45288ec9eaf7..35e9eb180c9a 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -670,7 +670,6 @@ CONFIG_PINCTRL_LPASS_LPI=m
CONFIG_PINCTRL_SC7280_LPASS_LPI=m
CONFIG_PINCTRL_SM6115_LPASS_LPI=m
CONFIG_PINCTRL_SM8250_LPASS_LPI=m
-CONFIG_PINCTRL_SM8350_LPASS_LPI=m
CONFIG_PINCTRL_SM8450_LPASS_LPI=m
CONFIG_PINCTRL_SC8280XP_LPASS_LPI=m
CONFIG_PINCTRL_SM8550_LPASS_LPI=m
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index a1ad12c72ebf..ce516d8187b1 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -300,6 +300,8 @@ void kvm_get_kimage_voffset(struct alt_instr *alt,
__le32 *origptr, __le32 *updptr, int nr_inst);
void kvm_compute_final_ctr_el0(struct alt_instr *alt,
__le32 *origptr, __le32 *updptr, int nr_inst);
+void kvm_pan_patch_el2_entry(struct alt_instr *alt,
+ __le32 *origptr, __le32 *updptr, int nr_inst);
void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr, u64 elr_virt,
u64 elr_phys, u64 par, uintptr_t vcpu, u64 far, u64 hpfar);
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index c9eab316398e..55d34192a8de 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -119,22 +119,6 @@ static inline unsigned long *vcpu_hcr(struct kvm_vcpu *vcpu)
return (unsigned long *)&vcpu->arch.hcr_el2;
}
-static inline void vcpu_clear_wfx_traps(struct kvm_vcpu *vcpu)
-{
- vcpu->arch.hcr_el2 &= ~HCR_TWE;
- if (atomic_read(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vlpi_count) ||
- vcpu->kvm->arch.vgic.nassgireq)
- vcpu->arch.hcr_el2 &= ~HCR_TWI;
- else
- vcpu->arch.hcr_el2 |= HCR_TWI;
-}
-
-static inline void vcpu_set_wfx_traps(struct kvm_vcpu *vcpu)
-{
- vcpu->arch.hcr_el2 |= HCR_TWE;
- vcpu->arch.hcr_el2 |= HCR_TWI;
-}
-
static inline unsigned long vcpu_get_vsesr(struct kvm_vcpu *vcpu)
{
return vcpu->arch.vsesr_el2;
diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
index fc02de43c68d..c0ad262a8289 100644
--- a/arch/arm64/include/asm/kvm_pgtable.h
+++ b/arch/arm64/include/asm/kvm_pgtable.h
@@ -87,7 +87,15 @@ typedef u64 kvm_pte_t;
#define KVM_PTE_LEAF_ATTR_HI_SW GENMASK(58, 55)
-#define KVM_PTE_LEAF_ATTR_HI_S1_XN BIT(54)
+#define __KVM_PTE_LEAF_ATTR_HI_S1_XN BIT(54)
+#define __KVM_PTE_LEAF_ATTR_HI_S1_UXN BIT(54)
+#define __KVM_PTE_LEAF_ATTR_HI_S1_PXN BIT(53)
+
+#define KVM_PTE_LEAF_ATTR_HI_S1_XN \
+ ({ cpus_have_final_cap(ARM64_KVM_HVHE) ? \
+ (__KVM_PTE_LEAF_ATTR_HI_S1_UXN | \
+ __KVM_PTE_LEAF_ATTR_HI_S1_PXN) : \
+ __KVM_PTE_LEAF_ATTR_HI_S1_XN; })
#define KVM_PTE_LEAF_ATTR_HI_S2_XN GENMASK(54, 53)
@@ -293,8 +301,8 @@ typedef bool (*kvm_pgtable_force_pte_cb_t)(u64 addr, u64 end,
* children.
* @KVM_PGTABLE_WALK_SHARED: Indicates the page-tables may be shared
* with other software walkers.
- * @KVM_PGTABLE_WALK_HANDLE_FAULT: Indicates the page-table walk was
- * invoked from a fault handler.
+ * @KVM_PGTABLE_WALK_IGNORE_EAGAIN: Don't terminate the walk early if
+ * the walker returns -EAGAIN.
* @KVM_PGTABLE_WALK_SKIP_BBM_TLBI: Visit and update table entries
* without Break-before-make's
* TLB invalidation.
@@ -307,7 +315,7 @@ enum kvm_pgtable_walk_flags {
KVM_PGTABLE_WALK_TABLE_PRE = BIT(1),
KVM_PGTABLE_WALK_TABLE_POST = BIT(2),
KVM_PGTABLE_WALK_SHARED = BIT(3),
- KVM_PGTABLE_WALK_HANDLE_FAULT = BIT(4),
+ KVM_PGTABLE_WALK_IGNORE_EAGAIN = BIT(4),
KVM_PGTABLE_WALK_SKIP_BBM_TLBI = BIT(5),
KVM_PGTABLE_WALK_SKIP_CMO = BIT(6),
};
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 9df51accbb02..106b15eb232a 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -91,7 +91,8 @@
*/
#define pstate_field(op1, op2) ((op1) << Op1_shift | (op2) << Op2_shift)
#define PSTATE_Imm_shift CRm_shift
-#define SET_PSTATE(x, r) __emit_inst(0xd500401f | PSTATE_ ## r | ((!!x) << PSTATE_Imm_shift))
+#define ENCODE_PSTATE(x, r) (0xd500401f | PSTATE_ ## r | ((!!x) << PSTATE_Imm_shift))
+#define SET_PSTATE(x, r) __emit_inst(ENCODE_PSTATE(x, r))
#define PSTATE_PAN pstate_field(0, 4)
#define PSTATE_UAO pstate_field(0, 3)
diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index 18749e9a6c2d..9717568518ba 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -402,7 +402,7 @@ int swsusp_arch_suspend(void)
* Memory allocated by get_safe_page() will be dealt with by the hibernate code,
* we don't need to free it here.
*/
-int swsusp_arch_resume(void)
+int __nocfi swsusp_arch_resume(void)
{
int rc;
void *zero_page;
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index 85bc629270bd..211f0e2e55e2 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -86,6 +86,7 @@ KVM_NVHE_ALIAS(kvm_patch_vector_branch);
KVM_NVHE_ALIAS(kvm_update_va_mask);
KVM_NVHE_ALIAS(kvm_get_kimage_voffset);
KVM_NVHE_ALIAS(kvm_compute_final_ctr_el0);
+KVM_NVHE_ALIAS(kvm_pan_patch_el2_entry);
KVM_NVHE_ALIAS(spectre_bhb_patch_loop_iter);
KVM_NVHE_ALIAS(spectre_bhb_patch_loop_mitigation_enable);
KVM_NVHE_ALIAS(spectre_bhb_patch_wa3);
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index b9bdd83fbbca..6c5ff6807d4c 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -968,20 +968,18 @@ static int sve_set_common(struct task_struct *target,
vq = sve_vq_from_vl(task_get_vl(target, type));
/* Enter/exit streaming mode */
- if (system_supports_sme()) {
- switch (type) {
- case ARM64_VEC_SVE:
- target->thread.svcr &= ~SVCR_SM_MASK;
- set_tsk_thread_flag(target, TIF_SVE);
- break;
- case ARM64_VEC_SME:
- target->thread.svcr |= SVCR_SM_MASK;
- set_tsk_thread_flag(target, TIF_SME);
- break;
- default:
- WARN_ON_ONCE(1);
- return -EINVAL;
- }
+ switch (type) {
+ case ARM64_VEC_SVE:
+ target->thread.svcr &= ~SVCR_SM_MASK;
+ set_tsk_thread_flag(target, TIF_SVE);
+ break;
+ case ARM64_VEC_SME:
+ target->thread.svcr |= SVCR_SM_MASK;
+ set_tsk_thread_flag(target, TIF_SME);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
}
/* Always zero V regs, FPSR, and FPCR */
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 1110eeb21f57..08ffc5a5aea4 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -449,12 +449,28 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
if (user->sve_size < SVE_SIG_CONTEXT_SIZE(vq))
return -EINVAL;
+ if (sm) {
+ sme_alloc(current, false);
+ if (!current->thread.sme_state)
+ return -ENOMEM;
+ }
+
sve_alloc(current, true);
if (!current->thread.sve_state) {
clear_thread_flag(TIF_SVE);
return -ENOMEM;
}
+ if (sm) {
+ current->thread.svcr |= SVCR_SM_MASK;
+ set_thread_flag(TIF_SME);
+ } else {
+ current->thread.svcr &= ~SVCR_SM_MASK;
+ set_thread_flag(TIF_SVE);
+ }
+
+ current->thread.fp_type = FP_STATE_SVE;
+
err = __copy_from_user(current->thread.sve_state,
(char __user const *)user->sve +
SVE_SIG_REGS_OFFSET,
@@ -462,12 +478,6 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
if (err)
return -EFAULT;
- if (flags & SVE_SIG_FLAG_SM)
- current->thread.svcr |= SVCR_SM_MASK;
- else
- set_thread_flag(TIF_SVE);
- current->thread.fp_type = FP_STATE_SVE;
-
err = read_fpsimd_context(&fpsimd, user);
if (err)
return err;
@@ -576,6 +586,10 @@ static int restore_za_context(struct user_ctxs *user)
if (user->za_size < ZA_SIG_CONTEXT_SIZE(vq))
return -EINVAL;
+ sve_alloc(current, false);
+ if (!current->thread.sve_state)
+ return -ENOMEM;
+
sme_alloc(current, true);
if (!current->thread.sme_state) {
current->thread.svcr &= ~SVCR_ZA_MASK;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 4f80da0c0d1d..620a465248d1 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -569,6 +569,7 @@ static bool kvm_vcpu_should_clear_twi(struct kvm_vcpu *vcpu)
return kvm_wfi_trap_policy == KVM_WFX_NOTRAP;
return single_task_running() &&
+ vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 &&
(atomic_read(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vlpi_count) ||
vcpu->kvm->arch.vgic.nassgireq);
}
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 53bf70126f81..808d26bed182 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -403,6 +403,7 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
struct s1_walk_result *wr, u64 va)
{
u64 va_top, va_bottom, baddr, desc, new_desc, ipa;
+ struct kvm_s2_trans s2_trans = {};
int level, stride, ret;
level = wi->sl;
@@ -420,8 +421,6 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
ipa = baddr | index;
if (wi->s2) {
- struct kvm_s2_trans s2_trans = {};
-
ret = kvm_walk_nested_s2(vcpu, ipa, &s2_trans);
if (ret) {
fail_s1_walk(wr,
@@ -515,6 +514,11 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
new_desc |= PTE_AF;
if (new_desc != desc) {
+ if (wi->s2 && !kvm_s2_trans_writable(&s2_trans)) {
+ fail_s1_walk(wr, ESR_ELx_FSC_PERM_L(level), true);
+ return -EPERM;
+ }
+
ret = kvm_swap_s1_desc(vcpu, ipa, desc, new_desc, wi);
if (ret)
return ret;
diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
index 9f4e8d68ab50..d1ccddf9e87d 100644
--- a/arch/arm64/kvm/hyp/entry.S
+++ b/arch/arm64/kvm/hyp/entry.S
@@ -126,7 +126,9 @@ SYM_INNER_LABEL(__guest_exit, SYM_L_GLOBAL)
add x1, x1, #VCPU_CONTEXT
- ALTERNATIVE(nop, SET_PSTATE_PAN(1), ARM64_HAS_PAN, CONFIG_ARM64_PAN)
+ alternative_cb ARM64_ALWAYS_SYSTEM, kvm_pan_patch_el2_entry
+ nop
+ alternative_cb_end
// Store the guest regs x2 and x3
stp x2, x3, [x1, #CPU_XREG_OFFSET(2)]
diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
index c5d5e5b86eaf..afecbdd3c1e9 100644
--- a/arch/arm64/kvm/hyp/include/hyp/switch.h
+++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
@@ -854,7 +854,7 @@ static inline bool kvm_hyp_handle_exit(struct kvm_vcpu *vcpu, u64 *exit_code,
return false;
}
-static inline void synchronize_vcpu_pstate(struct kvm_vcpu *vcpu, u64 *exit_code)
+static inline void synchronize_vcpu_pstate(struct kvm_vcpu *vcpu)
{
/*
* Check for the conditions of Cortex-A510's #2077057. When these occur
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index a7c689152f68..8ffbbce5e2ed 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -180,6 +180,9 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
/* Propagate WFx trapping flags */
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWE | HCR_TWI);
hyp_vcpu->vcpu.arch.hcr_el2 |= hcr_el2 & (HCR_TWE | HCR_TWI);
+ } else {
+ memcpy(&hyp_vcpu->vcpu.arch.fgt, hyp_vcpu->host_vcpu->arch.fgt,
+ sizeof(hyp_vcpu->vcpu.arch.fgt));
}
}
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 8911338961c5..12b2acfbcfd1 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -172,7 +172,6 @@ static int pkvm_vcpu_init_traps(struct pkvm_hyp_vcpu *hyp_vcpu)
/* Trust the host for non-protected vcpu features. */
vcpu->arch.hcrx_el2 = host_vcpu->arch.hcrx_el2;
- memcpy(vcpu->arch.fgt, host_vcpu->arch.fgt, sizeof(vcpu->arch.fgt));
return 0;
}
diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c
index d3b9ec8a7c28..779089e42681 100644
--- a/arch/arm64/kvm/hyp/nvhe/switch.c
+++ b/arch/arm64/kvm/hyp/nvhe/switch.c
@@ -211,7 +211,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
{
const exit_handler_fn *handlers = kvm_get_exit_handler_array(vcpu);
- synchronize_vcpu_pstate(vcpu, exit_code);
+ synchronize_vcpu_pstate(vcpu);
/*
* Some guests (e.g., protected VMs) are not be allowed to run in
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index 947ac1a951a5..9abc0a6cf448 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -144,7 +144,7 @@ static bool kvm_pgtable_walk_continue(const struct kvm_pgtable_walker *walker,
* page table walk.
*/
if (r == -EAGAIN)
- return !(walker->flags & KVM_PGTABLE_WALK_HANDLE_FAULT);
+ return walker->flags & KVM_PGTABLE_WALK_IGNORE_EAGAIN;
return !r;
}
@@ -1262,7 +1262,8 @@ int kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
{
return stage2_update_leaf_attrs(pgt, addr, size, 0,
KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W,
- NULL, NULL, 0);
+ NULL, NULL,
+ KVM_PGTABLE_WALK_IGNORE_EAGAIN);
}
void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr,
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index 9984c492305a..9db3f11a4754 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -536,7 +536,7 @@ static const exit_handler_fn hyp_exit_handlers[] = {
static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
{
- synchronize_vcpu_pstate(vcpu, exit_code);
+ synchronize_vcpu_pstate(vcpu);
/*
* If we were in HYP context on entry, adjust the PSTATE view
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 48d7c372a4cd..2caa97f87890 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -497,7 +497,7 @@ static int share_pfn_hyp(u64 pfn)
this->count = 1;
rb_link_node(&this->node, parent, node);
rb_insert_color(&this->node, &hyp_shared_pfns);
- ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn, 1);
+ ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn);
unlock:
mutex_unlock(&hyp_shared_pfns_lock);
@@ -523,7 +523,7 @@ static int unshare_pfn_hyp(u64 pfn)
rb_erase(&this->node, &hyp_shared_pfns);
kfree(this);
- ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn, 1);
+ ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn);
unlock:
mutex_unlock(&hyp_shared_pfns_lock);
@@ -1563,14 +1563,12 @@ static void adjust_nested_exec_perms(struct kvm *kvm,
*prot &= ~KVM_PGTABLE_PROT_PX;
}
-#define KVM_PGTABLE_WALK_MEMABORT_FLAGS (KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED)
-
static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
struct kvm_s2_trans *nested,
struct kvm_memory_slot *memslot, bool is_perm)
{
bool write_fault, exec_fault, writable;
- enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_MEMABORT_FLAGS;
+ enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt;
unsigned long mmu_seq;
@@ -1665,7 +1663,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
struct kvm_pgtable *pgt;
struct page *page;
vm_flags_t vm_flags;
- enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_MEMABORT_FLAGS;
+ enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
if (fault_is_perm)
fault_granule = kvm_vcpu_trap_get_perm_fault_granule(vcpu);
@@ -1933,7 +1931,7 @@ out_unlock:
/* Resolve the access fault by making the page young again. */
static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
{
- enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED;
+ enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
struct kvm_s2_mmu *mmu;
trace_kvm_access_fault(fault_ipa);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index c8fd7c6a12a1..88a57ca36d96 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -4668,7 +4668,10 @@ static void perform_access(struct kvm_vcpu *vcpu,
* that we don't know how to handle. This certainly qualifies
* as a gross bug that should be fixed right away.
*/
- BUG_ON(!r->access);
+ if (!r->access) {
+ bad_trap(vcpu, params, r, "register access");
+ return;
+ }
/* Skip instruction if instructed so */
if (likely(r->access(vcpu, params, r)))
diff --git a/arch/arm64/kvm/va_layout.c b/arch/arm64/kvm/va_layout.c
index 91b22a014610..bf888d150dc7 100644
--- a/arch/arm64/kvm/va_layout.c
+++ b/arch/arm64/kvm/va_layout.c
@@ -296,3 +296,31 @@ void kvm_compute_final_ctr_el0(struct alt_instr *alt,
generate_mov_q(read_sanitised_ftr_reg(SYS_CTR_EL0),
origptr, updptr, nr_inst);
}
+
+void kvm_pan_patch_el2_entry(struct alt_instr *alt,
+ __le32 *origptr, __le32 *updptr, int nr_inst)
+{
+ /*
+ * If we're running at EL1 without hVHE, then SCTLR_EL2.SPAN means
+ * nothing to us (it is RES1), and we don't need to set PSTATE.PAN
+ * to anything useful.
+ */
+ if (!is_kernel_in_hyp_mode() && !cpus_have_cap(ARM64_KVM_HVHE))
+ return;
+
+ /*
+ * Leap of faith: at this point, we must be running VHE one way or
+ * another, and FEAT_PAN is required to be implemented. If KVM
+ * explodes at runtime because your system does not abide by this
+ * requirement, call your favourite HW vendor, they have screwed up.
+ *
+ * We don't expect hVHE to access any userspace mapping, so always
+ * set PSTATE.PAN on enty. Same thing if we have PAN enabled on an
+ * EL2 kernel. Only force it to 0 if we have not configured PAN in
+ * the kernel (and you know this is really silly).
+ */
+ if (cpus_have_cap(ARM64_KVM_HVHE) || IS_ENABLED(CONFIG_ARM64_PAN))
+ *updptr = cpu_to_le32(ENCODE_PSTATE(1, PAN));
+ else
+ *updptr = cpu_to_le32(ENCODE_PSTATE(0, PAN));
+}
diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c
index e5000bef90f2..7cf9310de0ec 100644
--- a/arch/powerpc/kvm/book3s_hv_uvmem.c
+++ b/arch/powerpc/kvm/book3s_hv_uvmem.c
@@ -723,7 +723,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long gpa, struct kvm *kvm)
dpage = pfn_to_page(uvmem_pfn);
dpage->zone_device_data = pvt;
- zone_device_page_init(dpage, 0);
+ zone_device_page_init(dpage, &kvmppc_uvmem_pgmap, 0);
return dpage;
out_clear:
spin_lock(&kvmppc_uvmem_bitmap_lock);
diff --git a/arch/riscv/Kconfig.errata b/arch/riscv/Kconfig.errata
index aca9b0cfcfec..3c945d086c7d 100644
--- a/arch/riscv/Kconfig.errata
+++ b/arch/riscv/Kconfig.errata
@@ -84,6 +84,7 @@ config ERRATA_STARFIVE_JH7100
select DMA_GLOBAL_POOL
select RISCV_DMA_NONCOHERENT
select RISCV_NONSTANDARD_CACHE_OPS
+ select CACHEMAINT_FOR_DMA
select SIFIVE_CCACHE
default n
help
diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c
index 38aac2c47845..d0c61f86cba3 100644
--- a/arch/riscv/errata/sifive/errata.c
+++ b/arch/riscv/errata/sifive/errata.c
@@ -75,26 +75,12 @@ static u32 __init_or_module sifive_errata_probe(unsigned long archid,
return cpu_req_errata;
}
-static void __init_or_module warn_miss_errata(u32 miss_errata)
-{
- int i;
-
- pr_warn("----------------------------------------------------------------\n");
- pr_warn("WARNING: Missing the following errata may cause potential issues\n");
- for (i = 0; i < ERRATA_SIFIVE_NUMBER; i++)
- if (miss_errata & 0x1 << i)
- pr_warn("\tSiFive Errata[%d]:%s\n", i, errata_list[i].name);
- pr_warn("Please enable the corresponding Kconfig to apply them\n");
- pr_warn("----------------------------------------------------------------\n");
-}
-
void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
unsigned long archid, unsigned long impid,
unsigned int stage)
{
struct alt_entry *alt;
u32 cpu_req_errata;
- u32 cpu_apply_errata = 0;
u32 tmp;
BUILD_BUG_ON(ERRATA_SIFIVE_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
@@ -118,10 +104,6 @@ void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt),
alt->alt_len);
mutex_unlock(&text_mutex);
- cpu_apply_errata |= tmp;
}
}
- if (stage != RISCV_ALTERNATIVES_MODULE &&
- cpu_apply_errata != cpu_req_errata)
- warn_miss_errata(cpu_req_errata - cpu_apply_errata);
}
diff --git a/arch/riscv/include/asm/compat.h b/arch/riscv/include/asm/compat.h
index 6081327e55f5..28e115eed218 100644
--- a/arch/riscv/include/asm/compat.h
+++ b/arch/riscv/include/asm/compat.h
@@ -2,7 +2,7 @@
#ifndef __ASM_COMPAT_H
#define __ASM_COMPAT_H
-#define COMPAT_UTS_MACHINE "riscv\0\0"
+#define COMPAT_UTS_MACHINE "riscv32\0\0"
/*
* Architecture specific compatibility types
diff --git a/arch/riscv/include/asm/syscall.h b/arch/riscv/include/asm/syscall.h
index 34313387f977..8067e666a4ca 100644
--- a/arch/riscv/include/asm/syscall.h
+++ b/arch/riscv/include/asm/syscall.h
@@ -20,7 +20,7 @@ extern void * const sys_call_table[];
extern void * const compat_sys_call_table[];
/*
- * Only the low 32 bits of orig_r0 are meaningful, so we return int.
+ * Only the low 32 bits of orig_a0 are meaningful, so we return int.
* This importantly ignores the high bits on 64-bit, so comparisons
* sign-extend the low 32 bits.
*/
diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h
index 36bba6720c26..11c9886c3b70 100644
--- a/arch/riscv/include/asm/uaccess.h
+++ b/arch/riscv/include/asm/uaccess.h
@@ -97,13 +97,23 @@ static inline unsigned long __untagged_addr_remote(struct mm_struct *mm, unsigne
*/
#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+/*
+ * Use a temporary variable for the output of the asm goto to avoid a
+ * triggering an LLVM assertion due to sign extending the output when
+ * it is used in later function calls:
+ * https://github.com/llvm/llvm-project/issues/143795
+ */
#define __get_user_asm(insn, x, ptr, label) \
+do { \
+ u64 __tmp; \
asm_goto_output( \
"1:\n" \
" " insn " %0, %1\n" \
_ASM_EXTABLE_UACCESS_ERR(1b, %l2, %0) \
- : "=&r" (x) \
- : "m" (*(ptr)) : : label)
+ : "=&r" (__tmp) \
+ : "m" (*(ptr)) : : label); \
+ (x) = (__typeof__(x))(unsigned long)__tmp; \
+} while (0)
#else /* !CONFIG_CC_HAS_ASM_GOTO_OUTPUT */
#define __get_user_asm(insn, x, ptr, label) \
do { \
diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
index 5a956108b1ea..dbb067e345f0 100644
--- a/arch/riscv/kernel/signal.c
+++ b/arch/riscv/kernel/signal.c
@@ -145,14 +145,14 @@ struct arch_ext_priv {
long (*save)(struct pt_regs *regs, void __user *sc_vec);
};
-struct arch_ext_priv arch_ext_list[] = {
+static struct arch_ext_priv arch_ext_list[] = {
{
.magic = RISCV_V_MAGIC,
.save = &save_v_state,
},
};
-const size_t nr_arch_exts = ARRAY_SIZE(arch_ext_list);
+static const size_t nr_arch_exts = ARRAY_SIZE(arch_ext_list);
static long restore_sigcontext(struct pt_regs *regs,
struct sigcontext __user *sc)
@@ -297,7 +297,7 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,
} else {
err |= __put_user(arch_ext->magic, &sc_ext_ptr->magic);
err |= __put_user(ext_size, &sc_ext_ptr->size);
- sc_ext_ptr = (void *)sc_ext_ptr + ext_size;
+ sc_ext_ptr = (void __user *)sc_ext_ptr + ext_size;
}
}
/* Write zero to fp-reserved space and check it on restore_sigcontext */
diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c
index 24b3f57d467f..aff93090c4ef 100644
--- a/arch/riscv/kernel/suspend.c
+++ b/arch/riscv/kernel/suspend.c
@@ -51,10 +51,11 @@ void suspend_restore_csrs(struct suspend_context *context)
#ifdef CONFIG_MMU
if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SSTC)) {
- csr_write(CSR_STIMECMP, context->stimecmp);
#if __riscv_xlen < 64
+ csr_write(CSR_STIMECMP, ULONG_MAX);
csr_write(CSR_STIMECMPH, context->stimecmph);
#endif
+ csr_write(CSR_STIMECMP, context->stimecmp);
}
csr_write(CSR_SATP, context->satp);
diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c
index 85a7262115e1..f36247e4c783 100644
--- a/arch/riscv/kvm/vcpu_timer.c
+++ b/arch/riscv/kvm/vcpu_timer.c
@@ -72,8 +72,9 @@ static int kvm_riscv_vcpu_timer_cancel(struct kvm_vcpu_timer *t)
static int kvm_riscv_vcpu_update_vstimecmp(struct kvm_vcpu *vcpu, u64 ncycles)
{
#if defined(CONFIG_32BIT)
- ncsr_write(CSR_VSTIMECMP, ncycles & 0xFFFFFFFF);
+ ncsr_write(CSR_VSTIMECMP, ULONG_MAX);
ncsr_write(CSR_VSTIMECMPH, ncycles >> 32);
+ ncsr_write(CSR_VSTIMECMP, (u32)ncycles);
#else
ncsr_write(CSR_VSTIMECMP, ncycles);
#endif
@@ -307,8 +308,9 @@ void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu)
return;
#if defined(CONFIG_32BIT)
- ncsr_write(CSR_VSTIMECMP, (u32)t->next_cycles);
+ ncsr_write(CSR_VSTIMECMP, ULONG_MAX);
ncsr_write(CSR_VSTIMECMPH, (u32)(t->next_cycles >> 32));
+ ncsr_write(CSR_VSTIMECMP, (u32)(t->next_cycles));
#else
ncsr_write(CSR_VSTIMECMP, t->next_cycles);
#endif
diff --git a/arch/s390/boot/vmlinux.lds.S b/arch/s390/boot/vmlinux.lds.S
index 50988022f9ea..070bc18babd0 100644
--- a/arch/s390/boot/vmlinux.lds.S
+++ b/arch/s390/boot/vmlinux.lds.S
@@ -137,6 +137,15 @@ SECTIONS
}
_end = .;
+ /* Sections to be discarded */
+ /DISCARD/ : {
+ COMMON_DISCARDS
+ *(.eh_frame)
+ *(*__ksymtab*)
+ *(___kcrctab*)
+ *(.modinfo)
+ }
+
DWARF_DEBUG
ELF_DETAILS
@@ -161,12 +170,4 @@ SECTIONS
*(.rela.*) *(.rela_*)
}
ASSERT(SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations (.rela) detected!")
-
- /* Sections to be discarded */
- /DISCARD/ : {
- COMMON_DISCARDS
- *(.eh_frame)
- *(*__ksymtab*)
- *(___kcrctab*)
- }
}
diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
index 2fa12d4ac106..fece5d975eaf 100644
--- a/arch/s390/kernel/vdso/Makefile
+++ b/arch/s390/kernel/vdso/Makefile
@@ -28,7 +28,7 @@ KBUILD_CFLAGS_VDSO := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAG
KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO))
KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO))
KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables
-KBUILD_CFLAGS_VDSO += -fno-stack-protector
+KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE)
ldflags-y := -shared -soname=linux-vdso.so.1 \
--hash-style=both --build-id=sha1 -T
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 62963022b517..ad35c546243e 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1574,13 +1574,22 @@ static inline bool intel_pmu_has_bts_period(struct perf_event *event, u64 period
struct hw_perf_event *hwc = &event->hw;
unsigned int hw_event, bts_event;
- if (event->attr.freq)
+ /*
+ * Only use BTS for fixed rate period==1 events.
+ */
+ if (event->attr.freq || period != 1)
+ return false;
+
+ /*
+ * BTS doesn't virtualize.
+ */
+ if (event->attr.exclude_host)
return false;
hw_event = hwc->config & INTEL_ARCH_EVENT_MASK;
bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
- return hw_event == bts_event && period == 1;
+ return hw_event == bts_event;
}
static inline bool intel_pmu_has_bts(struct perf_event *event)
diff --git a/arch/x86/include/asm/kfence.h b/arch/x86/include/asm/kfence.h
index ff5c7134a37a..acf9ffa1a171 100644
--- a/arch/x86/include/asm/kfence.h
+++ b/arch/x86/include/asm/kfence.h
@@ -42,10 +42,34 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
+ pteval_t val;
if (WARN_ON(!pte || level != PG_LEVEL_4K))
return false;
+ val = pte_val(*pte);
+
+ /*
+ * protect requires making the page not-present. If the PTE is
+ * already in the right state, there's nothing to do.
+ */
+ if (protect != !!(val & _PAGE_PRESENT))
+ return true;
+
+ /*
+ * Otherwise, invert the entire PTE. This avoids writing out an
+ * L1TF-vulnerable PTE (not present, without the high address bits
+ * set).
+ */
+ set_pte(pte, __pte(~val));
+
+ /*
+ * If the page was protected (non-present) and we're making it
+ * present, there is no need to flush the TLB at all.
+ */
+ if (!protect)
+ return true;
+
/*
* We need to avoid IPIs, as we may get KFENCE allocations or faults
* with interrupts disabled. Therefore, the below is best-effort, and
@@ -53,11 +77,6 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect)
* lazy fault handling takes care of faults after the page is PRESENT.
*/
- if (protect)
- set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT));
- else
- set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT));
-
/*
* Flush this CPU's TLB, assuming whoever did the allocation/free is
* likely to continue running on this CPU.
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 998bd807fc7b..b83a06739b51 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -821,8 +821,6 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
force_sig_pkuerr((void __user *)address, pkey);
else
force_sig_fault(SIGSEGV, si_code, (void __user *)address);
-
- local_irq_disable();
}
static noinline void
@@ -1474,15 +1472,12 @@ handle_page_fault(struct pt_regs *regs, unsigned long error_code,
do_kern_addr_fault(regs, error_code, address);
} else {
do_user_addr_fault(regs, error_code, address);
- /*
- * User address page fault handling might have reenabled
- * interrupts. Fixing up all potential exit points of
- * do_user_addr_fault() and its leaf functions is just not
- * doable w/o creating an unholy mess or turning the code
- * upside down.
- */
- local_irq_disable();
}
+ /*
+ * page fault handling might have reenabled interrupts,
+ * make sure to disable them again.
+ */
+ local_irq_disable();
}
DEFINE_IDTENTRY_RAW_ERRORCODE(exc_page_fault)
diff --git a/block/blk-mq.c b/block/blk-mq.c
index a29d8ac9d3e3..968699277c3d 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1480,7 +1480,7 @@ EXPORT_SYMBOL_GPL(blk_rq_is_poll);
static void blk_rq_poll_completion(struct request *rq, struct completion *wait)
{
do {
- blk_hctx_poll(rq->q, rq->mq_hctx, NULL, 0);
+ blk_hctx_poll(rq->q, rq->mq_hctx, NULL, BLK_POLL_ONESHOT);
cond_resched();
} while (!completion_done(wait));
}
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 1c54678fae6b..8000c94690ee 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1957,6 +1957,7 @@ static int disk_update_zone_resources(struct gendisk *disk,
disk->nr_zones = args->nr_zones;
if (args->nr_conv_zones >= disk->nr_zones) {
+ queue_limits_cancel_update(q);
pr_warn("%s: Invalid number of conventional zones %u / %u\n",
disk->disk_name, args->nr_conv_zones, disk->nr_zones);
ret = -ENODEV;
diff --git a/crypto/authencesn.c b/crypto/authencesn.c
index d1bf0fda3f2e..542a978663b9 100644
--- a/crypto/authencesn.c
+++ b/crypto/authencesn.c
@@ -169,6 +169,9 @@ static int crypto_authenc_esn_encrypt(struct aead_request *req)
struct scatterlist *src, *dst;
int err;
+ if (assoclen < 8)
+ return -EINVAL;
+
sg_init_table(areq_ctx->src, 2);
src = scatterwalk_ffwd(areq_ctx->src, req->src, assoclen);
dst = src;
@@ -256,6 +259,9 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req)
u32 tmp[2];
int err;
+ if (assoclen < 8)
+ return -EINVAL;
+
cryptlen -= authsize;
if (req->src != dst)
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 7a7f88b3fa2b..931d0081169b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -2094,13 +2094,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
- ahci_mark_external_port(ap);
-
- ahci_update_initial_lpm_policy(ap);
-
/* disabled/not-implemented port */
- if (!(hpriv->port_map & (1 << i)))
+ if (!(hpriv->port_map & (1 << i))) {
ap->ops = &ata_dummy_port_ops;
+ } else {
+ ahci_mark_external_port(ap);
+ ahci_update_initial_lpm_policy(ap);
+ }
}
/* apply workaround for ASUS P5W DH Deluxe mainboard */
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 09d8c035fcdf..ddf9a7b28a59 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2872,7 +2872,8 @@ static void ata_dev_config_lpm(struct ata_device *dev)
static void ata_dev_print_features(struct ata_device *dev)
{
- if (!(dev->flags & ATA_DFLAG_FEATURES_MASK))
+ if (!(dev->flags & ATA_DFLAG_FEATURES_MASK) && !dev->cpr_log &&
+ !ata_id_has_hipm(dev->id) && !ata_id_has_dipm(dev->id))
return;
ata_dev_info(dev,
@@ -3116,6 +3117,11 @@ int ata_dev_configure(struct ata_device *dev)
ata_mode_string(xfer_mask),
cdb_intr_string, atapi_an_string,
dma_dir_string);
+
+ ata_dev_config_lpm(dev);
+
+ if (print_info)
+ ata_dev_print_features(dev);
}
/* determine max_sectors */
diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c
index b2817a2995d6..04e1e774645e 100644
--- a/drivers/ata/libata-sata.c
+++ b/drivers/ata/libata-sata.c
@@ -909,7 +909,7 @@ static bool ata_scsi_lpm_supported(struct ata_port *ap)
struct ata_link *link;
struct ata_device *dev;
- if (ap->flags & ATA_FLAG_NO_LPM)
+ if ((ap->flags & ATA_FLAG_NO_LPM) || !ap->ops->set_lpm)
return false;
ata_for_each_link(link, ap, EDGE) {
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 349f31bedfa1..bea8da5f8a3a 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -548,6 +548,8 @@ static DEVICE_ATTR_RW(state_synced);
static void device_unbind_cleanup(struct device *dev)
{
devres_release_all(dev);
+ if (dev->driver->p_cb.post_unbind_rust)
+ dev->driver->p_cb.post_unbind_rust(dev);
arch_teardown_dma_ops(dev);
kfree(dev->dma_range_map);
dev->dma_range_map = NULL;
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 1477329410ec..5bf993165438 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -117,6 +117,9 @@ struct regmap {
void *val_buf, size_t val_size);
int (*write)(void *context, const void *data, size_t count);
+ int (*reg_default_cb)(struct device *dev, unsigned int reg,
+ unsigned int *val);
+
unsigned long read_flag_mask;
unsigned long write_flag_mask;
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index 53cc59c84e2f..c924817e19b1 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -79,6 +79,25 @@ static int regcache_flat_populate(struct regmap *map)
__set_bit(index, cache->valid);
}
+ if (map->reg_default_cb) {
+ dev_dbg(map->dev,
+ "Populating regcache_flat using reg_default_cb callback\n");
+
+ for (i = 0; i <= map->max_register; i += map->reg_stride) {
+ unsigned int index = regcache_flat_get_index(map, i);
+ unsigned int value;
+
+ if (test_bit(index, cache->valid))
+ continue;
+
+ if (map->reg_default_cb(map->dev, i, &value))
+ continue;
+
+ cache->data[index] = value;
+ __set_bit(index, cache->valid);
+ }
+ }
+
return 0;
}
diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c
index ca1c72b68f31..4134a77ae1d6 100644
--- a/drivers/base/regmap/regcache-maple.c
+++ b/drivers/base/regmap/regcache-maple.c
@@ -95,12 +95,13 @@ static int regcache_maple_write(struct regmap *map, unsigned int reg,
mas_unlock(&mas);
- if (ret == 0) {
- kfree(lower);
- kfree(upper);
+ if (ret) {
+ kfree(entry);
+ return ret;
}
-
- return ret;
+ kfree(lower);
+ kfree(upper);
+ return 0;
}
static int regcache_maple_drop(struct regmap *map, unsigned int min,
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 319c342bf5a0..31bdbf37dbed 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -223,7 +223,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
goto err_free;
}
- if (map->num_reg_defaults && map->cache_ops->populate) {
+ if (map->cache_ops->populate &&
+ (map->num_reg_defaults || map->reg_default_cb)) {
dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
map->lock(map->lock_arg);
ret = map->cache_ops->populate(map);
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
index f6fc5ed016da..6d8279de3ff2 100644
--- a/drivers/base/regmap/regmap-kunit.c
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -15,6 +15,8 @@ KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *);
struct regmap_test_priv {
struct device *dev;
+ bool *reg_default_called;
+ unsigned int reg_default_max;
};
struct regmap_test_param {
@@ -118,6 +120,14 @@ static const struct regmap_test_param real_cache_types_only_list[] = {
KUNIT_ARRAY_PARAM(real_cache_types_only, real_cache_types_only_list, param_to_desc);
+static const struct regmap_test_param flat_cache_types_list[] = {
+ { .cache = REGCACHE_FLAT, .from_reg = 0 },
+ { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_FLAT, .from_reg = 0x2001 },
+};
+
+KUNIT_ARRAY_PARAM(flat_cache_types, flat_cache_types_list, param_to_desc);
+
static const struct regmap_test_param real_cache_types_list[] = {
{ .cache = REGCACHE_FLAT, .from_reg = 0 },
{ .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true },
@@ -248,6 +258,37 @@ static bool reg_5_false(struct device *dev, unsigned int reg)
return reg != (param->from_reg + 5);
}
+static unsigned int reg_default_expected(unsigned int reg)
+{
+ return 0x5a5a0000 | (reg & 0xffff);
+}
+
+static int reg_default_test_cb(struct device *dev, unsigned int reg,
+ unsigned int *def)
+{
+ struct kunit *test = dev_get_drvdata(dev);
+ struct regmap_test_priv *priv = test->priv;
+
+ if (priv && priv->reg_default_called && reg <= priv->reg_default_max)
+ priv->reg_default_called[reg] = true;
+
+ *def = reg_default_expected(reg);
+ return 0;
+}
+
+static void expect_reg_default_value(struct kunit *test, struct regmap *map,
+ struct regmap_ram_data *data,
+ struct regmap_test_priv *priv,
+ unsigned int reg)
+{
+ unsigned int val;
+
+ KUNIT_EXPECT_TRUE(test, priv->reg_default_called[reg]);
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, reg, &val));
+ KUNIT_EXPECT_EQ(test, reg_default_expected(reg), val);
+ KUNIT_EXPECT_FALSE(test, data->read[reg]);
+}
+
static void basic_read_write(struct kunit *test)
{
struct regmap *map;
@@ -628,6 +669,54 @@ static void reg_defaults(struct kunit *test)
KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
}
+static void reg_default_callback_populates_flat_cache(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap_test_priv *priv = test->priv;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int reg, val;
+ unsigned int defaults_end;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = 3;
+ config.max_register = param->from_reg + BLOCK_TEST_SIZE - 1;
+ config.reg_default_cb = reg_default_test_cb;
+
+ priv->reg_default_max = config.max_register;
+ priv->reg_default_called = kunit_kcalloc(test, config.max_register + 1,
+ sizeof(*priv->reg_default_called),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->reg_default_called);
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (reg = 0; reg <= config.max_register; reg++)
+ data->read[reg] = false;
+
+ defaults_end = param->from_reg + config.num_reg_defaults - 1;
+
+ for (reg = param->from_reg; reg <= defaults_end; reg++) {
+ KUNIT_EXPECT_FALSE(test, priv->reg_default_called[reg]);
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, reg, &val));
+ KUNIT_EXPECT_EQ(test, data->vals[reg], val);
+ KUNIT_EXPECT_FALSE(test, data->read[reg]);
+ }
+
+ if (param->from_reg > 0)
+ expect_reg_default_value(test, map, data, priv, 0);
+
+ if (defaults_end + 1 <= config.max_register)
+ expect_reg_default_value(test, map, data, priv, defaults_end + 1);
+
+ if (config.max_register > defaults_end + 1)
+ expect_reg_default_value(test, map, data, priv, config.max_register);
+}
+
static void reg_defaults_read_dev(struct kunit *test)
{
struct regmap *map;
@@ -2058,6 +2147,8 @@ static struct kunit_case regmap_test_cases[] = {
KUNIT_CASE_PARAM(write_readonly, regcache_types_gen_params),
KUNIT_CASE_PARAM(read_writeonly, regcache_types_gen_params),
KUNIT_CASE_PARAM(reg_defaults, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(reg_default_callback_populates_flat_cache,
+ flat_cache_types_gen_params),
KUNIT_CASE_PARAM(reg_defaults_read_dev, regcache_types_gen_params),
KUNIT_CASE_PARAM(register_patch, regcache_types_gen_params),
KUNIT_CASE_PARAM(stride, regcache_types_gen_params),
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index ce9be3989a21..4231e9d4b8ff 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -408,9 +408,11 @@ static void regmap_lock_hwlock_irq(void *__map)
static void regmap_lock_hwlock_irqsave(void *__map)
{
struct regmap *map = __map;
+ unsigned long flags = 0;
hwspin_lock_timeout_irqsave(map->hwlock, UINT_MAX,
- &map->spinlock_flags);
+ &flags);
+ map->spinlock_flags = flags;
}
static void regmap_unlock_hwlock(void *__map)
@@ -811,6 +813,7 @@ struct regmap *__regmap_init(struct device *dev,
map->precious_reg = config->precious_reg;
map->writeable_noinc_reg = config->writeable_noinc_reg;
map->readable_noinc_reg = config->readable_noinc_reg;
+ map->reg_default_cb = config->reg_default_cb;
map->cache_type = config->cache_type;
spin_lock_init(&map->async_lock);
@@ -1433,6 +1436,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
map->precious_reg = config->precious_reg;
map->writeable_noinc_reg = config->writeable_noinc_reg;
map->readable_noinc_reg = config->readable_noinc_reg;
+ map->reg_default_cb = config->reg_default_cb;
map->cache_type = config->cache_type;
ret = regmap_set_name(map, config);
diff --git a/drivers/block/rnbd/rnbd-clt.c b/drivers/block/rnbd/rnbd-clt.c
index 8194a970f002..d1c354636315 100644
--- a/drivers/block/rnbd/rnbd-clt.c
+++ b/drivers/block/rnbd/rnbd-clt.c
@@ -1662,6 +1662,7 @@ static void destroy_sysfs(struct rnbd_clt_dev *dev,
/* To avoid deadlock firstly remove itself */
sysfs_remove_file_self(&dev->kobj, sysfs_self);
kobject_del(&dev->kobj);
+ kobject_put(&dev->kobj);
}
}
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index f6e5a0766721..cd1e84653002 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -2885,6 +2885,15 @@ static struct ublk_device *ublk_get_device_from_id(int idx)
return ub;
}
+static bool ublk_validate_user_pid(struct ublk_device *ub, pid_t ublksrv_pid)
+{
+ rcu_read_lock();
+ ublksrv_pid = pid_nr(find_vpid(ublksrv_pid));
+ rcu_read_unlock();
+
+ return ub->ublksrv_tgid == ublksrv_pid;
+}
+
static int ublk_ctrl_start_dev(struct ublk_device *ub,
const struct ublksrv_ctrl_cmd *header)
{
@@ -2953,7 +2962,7 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
if (wait_for_completion_interruptible(&ub->completion) != 0)
return -EINTR;
- if (ub->ublksrv_tgid != ublksrv_pid)
+ if (!ublk_validate_user_pid(ub, ublksrv_pid))
return -EINVAL;
mutex_lock(&ub->mutex);
@@ -2972,7 +2981,7 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
disk->fops = &ub_fops;
disk->private_data = ub;
- ub->dev_info.ublksrv_pid = ublksrv_pid;
+ ub->dev_info.ublksrv_pid = ub->ublksrv_tgid;
ub->ub_disk = disk;
ublk_apply_params(ub);
@@ -3320,12 +3329,32 @@ static int ublk_ctrl_stop_dev(struct ublk_device *ub)
static int ublk_ctrl_get_dev_info(struct ublk_device *ub,
const struct ublksrv_ctrl_cmd *header)
{
+ struct task_struct *p;
+ struct pid *pid;
+ struct ublksrv_ctrl_dev_info dev_info;
+ pid_t init_ublksrv_tgid = ub->dev_info.ublksrv_pid;
void __user *argp = (void __user *)(unsigned long)header->addr;
if (header->len < sizeof(struct ublksrv_ctrl_dev_info) || !header->addr)
return -EINVAL;
- if (copy_to_user(argp, &ub->dev_info, sizeof(ub->dev_info)))
+ memcpy(&dev_info, &ub->dev_info, sizeof(dev_info));
+ dev_info.ublksrv_pid = -1;
+
+ if (init_ublksrv_tgid > 0) {
+ rcu_read_lock();
+ pid = find_pid_ns(init_ublksrv_tgid, &init_pid_ns);
+ p = pid_task(pid, PIDTYPE_TGID);
+ if (p) {
+ int vnr = task_tgid_vnr(p);
+
+ if (vnr)
+ dev_info.ublksrv_pid = vnr;
+ }
+ rcu_read_unlock();
+ }
+
+ if (copy_to_user(argp, &dev_info, sizeof(dev_info)))
return -EFAULT;
return 0;
@@ -3470,7 +3499,7 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub,
pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__,
header->dev_id);
- if (ub->ublksrv_tgid != ublksrv_pid)
+ if (!ublk_validate_user_pid(ub, ublksrv_pid))
return -EINVAL;
mutex_lock(&ub->mutex);
@@ -3481,7 +3510,7 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub,
ret = -EBUSY;
goto out_unlock;
}
- ub->dev_info.ublksrv_pid = ublksrv_pid;
+ ub->dev_info.ublksrv_pid = ub->ublksrv_tgid;
ub->dev_info.state = UBLK_S_DEV_LIVE;
pr_devel("%s: new ublksrv_pid %d, dev id %d\n",
__func__, ublksrv_pid, header->dev_id);
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index d0adae3267b4..2b28515de92c 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -685,6 +685,8 @@ static int hci_uart_register_dev(struct hci_uart *hu)
return err;
}
+ set_bit(HCI_UART_PROTO_INIT, &hu->flags);
+
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return 0;
@@ -712,8 +714,6 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
hu->proto = p;
- set_bit(HCI_UART_PROTO_INIT, &hu->flags);
-
err = hci_uart_register_dev(hu);
if (err) {
return err;
diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c
index d8e029e7e53f..3f00d953fb9a 100644
--- a/drivers/bus/simple-pm-bus.c
+++ b/drivers/bus/simple-pm-bus.c
@@ -142,6 +142,12 @@ static const struct of_device_id simple_pm_bus_of_match[] = {
{ .compatible = "simple-mfd", .data = ONLY_BUS },
{ .compatible = "isa", .data = ONLY_BUS },
{ .compatible = "arm,amba-bus", .data = ONLY_BUS },
+ { .compatible = "fsl,ls1021a-scfg", },
+ { .compatible = "fsl,ls1043a-scfg", },
+ { .compatible = "fsl,ls1046a-scfg", },
+ { .compatible = "fsl,ls1088a-isc", },
+ { .compatible = "fsl,ls2080a-isc", },
+ { .compatible = "fsl,lx2160a-isc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, simple_pm_bus_of_match);
diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c
index 4d7cf338824a..cfc4d83c42c0 100644
--- a/drivers/clocksource/timer-riscv.c
+++ b/drivers/clocksource/timer-riscv.c
@@ -50,8 +50,9 @@ static int riscv_clock_next_event(unsigned long delta,
if (static_branch_likely(&riscv_sstc_available)) {
#if defined(CONFIG_32BIT)
- csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF);
+ csr_write(CSR_STIMECMP, ULONG_MAX);
csr_write(CSR_STIMECMPH, next_tval >> 32);
+ csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF);
#else
csr_write(CSR_STIMECMP, next_tval);
#endif
diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c
index 657c98cd723e..2c3eb9e89571 100644
--- a/drivers/comedi/comedi_fops.c
+++ b/drivers/comedi/comedi_fops.c
@@ -1155,7 +1155,7 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
for (i = 0; i < s->n_chan; i++) {
int x;
- x = (dev->minor << 28) | (it->subdev << 24) | (i << 16) |
+ x = (it->subdev << 24) | (i << 16) |
(s->range_table_list[i]->length);
if (put_user(x, it->rangelist + i))
return -EFAULT;
diff --git a/drivers/comedi/drivers/dmm32at.c b/drivers/comedi/drivers/dmm32at.c
index 644e3b643c79..910cd24b1bed 100644
--- a/drivers/comedi/drivers/dmm32at.c
+++ b/drivers/comedi/drivers/dmm32at.c
@@ -330,6 +330,7 @@ static int dmm32at_ai_cmdtest(struct comedi_device *dev,
static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
{
+ unsigned long irq_flags;
unsigned char lo1, lo2, hi2;
unsigned short both2;
@@ -342,6 +343,9 @@ static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
/* set counter clocks to 10MHz, disable all aux dio */
outb(0, dev->iobase + DMM32AT_CTRDIO_CFG_REG);
+ /* serialize access to control register and paged registers */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
/* get access to the clock regs */
outb(DMM32AT_CTRL_PAGE_8254, dev->iobase + DMM32AT_CTRL_REG);
@@ -354,6 +358,8 @@ static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
outb(lo2, dev->iobase + DMM32AT_CLK2);
outb(hi2, dev->iobase + DMM32AT_CLK2);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
/* enable the ai conversion interrupt and the clock to start scans */
outb(DMM32AT_INTCLK_ADINT |
DMM32AT_INTCLK_CLKEN | DMM32AT_INTCLK_CLKSEL,
@@ -363,13 +369,19 @@ static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long irq_flags;
int ret;
dmm32at_ai_set_chanspec(dev, s, cmd->chanlist[0], cmd->chanlist_len);
+ /* serialize access to control register and paged registers */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
/* reset the interrupt just in case */
outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
/*
* wait for circuit to settle
* we don't have the 'insn' here but it's not needed
@@ -429,8 +441,13 @@ static irqreturn_t dmm32at_isr(int irq, void *d)
comedi_handle_events(dev, s);
}
+ /* serialize access to control register and paged registers */
+ spin_lock(&dev->spinlock);
+
/* reset the interrupt */
outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
+
+ spin_unlock(&dev->spinlock);
return IRQ_HANDLED;
}
@@ -481,14 +498,25 @@ static int dmm32at_ao_insn_write(struct comedi_device *dev,
static int dmm32at_8255_io(struct comedi_device *dev,
int dir, int port, int data, unsigned long regbase)
{
+ unsigned long irq_flags;
+ int ret;
+
+ /* serialize access to control register and paged registers */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
/* get access to the DIO regs */
outb(DMM32AT_CTRL_PAGE_8255, dev->iobase + DMM32AT_CTRL_REG);
if (dir) {
outb(data, dev->iobase + regbase + port);
- return 0;
+ ret = 0;
+ } else {
+ ret = inb(dev->iobase + regbase + port);
}
- return inb(dev->iobase + regbase + port);
+
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return ret;
}
/* Make sure the board is there and put it to a known state */
diff --git a/drivers/comedi/range.c b/drivers/comedi/range.c
index 8f43cf88d784..5b8f662365e3 100644
--- a/drivers/comedi/range.c
+++ b/drivers/comedi/range.c
@@ -52,7 +52,7 @@ int do_rangeinfo_ioctl(struct comedi_device *dev,
const struct comedi_lrange *lr;
struct comedi_subdevice *s;
- subd = (it->range_type >> 24) & 0xf;
+ subd = (it->range_type >> 24) & 0xff;
chan = (it->range_type >> 16) & 0xff;
if (!dev->attached)
diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c
index 81e16b5a0245..b8081acba928 100644
--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
@@ -263,6 +263,7 @@ static const struct of_device_id qcom_cpufreq_ipq806x_match_list[] __maybe_unuse
{ .compatible = "qcom,ipq8066", .data = (const void *)QCOM_ID_IPQ8066 },
{ .compatible = "qcom,ipq8068", .data = (const void *)QCOM_ID_IPQ8068 },
{ .compatible = "qcom,ipq8069", .data = (const void *)QCOM_ID_IPQ8069 },
+ { /* sentinel */ }
};
static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index a461095efd8a..8879a7235156 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -83,10 +83,8 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
if (ref->pin != pin)
continue;
reg = dpll_pin_registration_find(ref, ops, priv, cookie);
- if (reg) {
- refcount_inc(&ref->refcount);
- return 0;
- }
+ if (reg)
+ return -EEXIST;
ref_exists = true;
break;
}
@@ -164,10 +162,8 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
if (ref->dpll != dpll)
continue;
reg = dpll_pin_registration_find(ref, ops, priv, cookie);
- if (reg) {
- refcount_inc(&ref->refcount);
- return 0;
- }
+ if (reg)
+ return -EEXIST;
ref_exists = true;
break;
}
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 7fea11a5e359..22ae387ae03c 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -173,20 +173,14 @@ static void split_transaction_timeout_callback(struct timer_list *timer)
}
}
-static void start_split_transaction_timeout(struct fw_transaction *t,
- struct fw_card *card)
+// card->transactions.lock should be acquired in advance for the linked list.
+static void start_split_transaction_timeout(struct fw_transaction *t, unsigned int delta)
{
- unsigned long delta;
-
if (list_empty(&t->link) || WARN_ON(t->is_split_transaction))
return;
t->is_split_transaction = true;
- // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
- // local destination never runs in any type of IRQ context.
- scoped_guard(spinlock_irqsave, &card->split_timeout.lock)
- delta = card->split_timeout.jiffies;
mod_timer(&t->split_timeout_timer, jiffies + delta);
}
@@ -207,13 +201,20 @@ static void transmit_complete_callback(struct fw_packet *packet,
break;
case ACK_PENDING:
{
+ unsigned int delta;
+
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->split_timeout.lock) {
t->split_timeout_cycle =
compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff;
+ delta = card->split_timeout.jiffies;
}
- start_split_transaction_timeout(t, card);
+
+ // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
+ // local destination never runs in any type of IRQ context.
+ scoped_guard(spinlock_irqsave, &card->transactions.lock)
+ start_split_transaction_timeout(t, delta);
break;
}
case ACK_BUSY_X:
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c
index 73d201e7d992..9fdb50f3fec6 100644
--- a/drivers/firmware/cirrus/cs_dsp.c
+++ b/drivers/firmware/cirrus/cs_dsp.c
@@ -412,18 +412,23 @@ static ssize_t cs_dsp_debugfs_string_read(struct cs_dsp *dsp,
size_t count, loff_t *ppos,
const char **pstr)
{
- const char *str __free(kfree) = NULL;
+ const char *str;
+ ssize_t ret = 0;
scoped_guard(mutex, &dsp->pwr_lock) {
- if (!*pstr)
- return 0;
-
- str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
- if (!str)
- return -ENOMEM;
-
- return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
+ if (*pstr) {
+ str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
+ if (str) {
+ ret = simple_read_from_buffer(user_buf, count,
+ ppos, str, strlen(str));
+ kfree(str);
+ } else {
+ ret = -ENOMEM;
+ }
+ }
}
+
+ return ret;
}
static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file,
@@ -1483,7 +1488,7 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
const struct wmfw_region *region;
const struct cs_dsp_region *mem;
const char *region_name;
- u8 *buf __free(kfree) = NULL;
+ u8 *buf = NULL;
size_t buf_len = 0;
size_t region_len;
unsigned int reg;
@@ -1638,6 +1643,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
ret = 0;
out_fw:
+ kfree(buf);
+
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
@@ -2168,8 +2175,9 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
const struct cs_dsp_region *mem;
struct cs_dsp_alg_region *alg_region;
const char *region_name;
- int ret, pos, blocks, type, offset, reg, version;
- u8 *buf __free(kfree) = NULL;
+ int ret, pos, blocks, type, version;
+ unsigned int offset, reg;
+ u8 *buf = NULL;
size_t buf_len = 0;
size_t region_len;
@@ -2193,6 +2201,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
switch (be32_to_cpu(hdr->rev) & 0xff) {
case 1:
case 2:
+ case 3:
break;
default:
cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
@@ -2201,7 +2210,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
goto out_fw;
}
- cs_dsp_info(dsp, "%s: v%d.%d.%d\n", file,
+ cs_dsp_info(dsp, "%s (v%d): v%d.%d.%d\n", file,
+ be32_to_cpu(hdr->rev) & 0xff,
(le32_to_cpu(hdr->ver) >> 16) & 0xff,
(le32_to_cpu(hdr->ver) >> 8) & 0xff,
le32_to_cpu(hdr->ver) & 0xff);
@@ -2232,8 +2242,9 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
(le32_to_cpu(blk->ver) >> 16) & 0xff,
(le32_to_cpu(blk->ver) >> 8) & 0xff,
le32_to_cpu(blk->ver) & 0xff);
- cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
- file, blocks, le32_to_cpu(blk->len), offset, type);
+ cs_dsp_dbg(dsp, "%s.%d: %d bytes off:%#x off32:%#x in %#x\n",
+ file, blocks, le32_to_cpu(blk->len), offset,
+ le32_to_cpu(blk->offset32), type);
reg = 0;
region_name = "Unknown";
@@ -2266,6 +2277,13 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
}
break;
+ case WMFW_ADSP2_XM_LONG:
+ case WMFW_ADSP2_YM_LONG:
+ case WMFW_HALO_XM_PACKED_LONG:
+ case WMFW_HALO_YM_PACKED_LONG:
+ offset = le32_to_cpu(blk->offset32);
+ type &= 0xff; /* strip extended block type flags */
+ fallthrough;
case WMFW_ADSP1_DM:
case WMFW_ADSP1_ZM:
case WMFW_ADSP2_XM:
@@ -2348,6 +2366,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
ret = 0;
out_fw:
+ kfree(buf);
+
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
index 3f8777ee4dc0..635e917e0516 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
@@ -56,13 +56,14 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS")
* @alg_id: Algorithm ID.
* @alg_ver: Algorithm version.
* @type: Type of the block.
- * @offset: Offset.
+ * @offset: 16-bit offset.
+ * @offset32: 32-bit offset (sample rate on V1 and V2 file formats).
* @payload_data: Pointer to buffer containing the payload data.
* @payload_len_bytes: Length of payload data in bytes.
*/
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
- int type, unsigned int offset,
+ int type, u16 offset, u32 offset32,
const void *payload_data, size_t payload_len_bytes)
{
struct wmfw_coeff_item *item;
@@ -75,6 +76,7 @@ void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
item = builder->write_p;
item->offset = cpu_to_le16(offset);
+ item->offset32 = cpu_to_le32(offset32);
item->type = cpu_to_le16(type);
item->id = cpu_to_le32(alg_id);
item->ver = cpu_to_le32(alg_ver << 8);
@@ -104,7 +106,7 @@ static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *bui
info = tmp;
}
- cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len);
+ cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, 0, info, info_len);
kunit_kfree(builder->test_priv->test, tmp);
}
@@ -156,12 +158,40 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
- mem_region, reg_addr_offset,
+ mem_region, (u16)reg_addr_offset, 0,
payload_data, payload_len_bytes);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
+ * cs_dsp_mock_bin_add_patch_off32() - Add a patch data block with 32-bit offset.
+ *
+ * @builder: Pointer to struct cs_dsp_mock_bin_builder.
+ * @alg_id: Algorithm ID for the patch.
+ * @alg_ver: Algorithm version for the patch.
+ * @mem_region: Memory region for the patch.
+ * @reg_addr_offset: Offset to start of data in register addresses.
+ * @payload_data: Pointer to buffer containing the payload data.
+ * @payload_len_bytes: Length of payload data in bytes.
+ */
+void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder,
+ unsigned int alg_id, unsigned int alg_ver,
+ int mem_region, unsigned int reg_addr_offset,
+ const void *payload_data, size_t payload_len_bytes)
+{
+ /* Payload length must be a multiple of 4 */
+ KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
+
+ /* Mark the block as using the 32-bit offset */
+ mem_region |= 0xf400;
+
+ cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
+ mem_region, 0, reg_addr_offset,
+ payload_data, payload_len_bytes);
+}
+EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch_off32, "FW_CS_DSP_KUNIT_TEST_UTILS");
+
+/**
* cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder.
*
* @priv: Pointer to struct cs_dsp_test.
diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c b/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
index 95946fac5563..7fb404425fd6 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
@@ -23,10 +23,10 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS"
/* List of sizes in bytes, for each entry above */
const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = {
0x5000, /* PM_PACKED */
- 0x6000, /* XM_PACKED */
- 0x47F4, /* YM_PACKED */
- 0x8000, /* XM_UNPACKED_24 */
- 0x5FF8, /* YM_UNPACKED_24 */
+ 0x8fff4, /* XM_PACKED */
+ 0x8fff4, /* YM_PACKED */
+ 0xbfff8, /* XM_UNPACKED_24 */
+ 0xbfff8, /* YM_UNPACKED_24 */
0 /* terminator */
};
diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
index fb8e4a5d189a..5167305521cd 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
@@ -157,22 +157,22 @@ static const struct reg_default halo_register_defaults[] = {
};
static const struct regmap_range halo_readable_registers[] = {
- regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
+ regmap_reg_range(0x2000000, 0x208fff0), /* XM_PACKED */
regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */
regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */
- regmap_reg_range(0x2800000, 0x2807fff), /* XM */
+ regmap_reg_range(0x2800000, 0x28bfff4), /* XM */
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
- regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
- regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
+ regmap_reg_range(0x2c00000, 0x2c8fff0), /* YM_PACKED */
+ regmap_reg_range(0x3400000, 0x34bfff4), /* YM */
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
};
static const struct regmap_range halo_writeable_registers[] = {
- regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
- regmap_reg_range(0x2800000, 0x2807fff), /* XM */
+ regmap_reg_range(0x2000000, 0x208fff0), /* XM_PACKED */
+ regmap_reg_range(0x2800000, 0x28bfff4), /* XM */
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
- regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
- regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
+ regmap_reg_range(0x2c00000, 0x2c8fff0), /* YM_PACKED */
+ regmap_reg_range(0x3400000, 0x34bfff4), /* YM */
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
};
diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c
index 2c6486fa9575..66140caeebb5 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_test_bin.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin.c
@@ -71,6 +71,10 @@ struct bin_test_param {
int mem_type;
unsigned int offset_words;
int alg_idx;
+ void (*add_patch)(struct cs_dsp_mock_bin_builder *builder,
+ unsigned int alg_id, unsigned int alg_ver,
+ int mem_region, unsigned int reg_addr_offset,
+ const void *payload_data, size_t payload_len_bytes);
};
static const struct cs_dsp_mock_alg_def bin_test_mock_algs[] = {
@@ -130,12 +134,12 @@ static void bin_patch_one_word(struct kunit *test)
bin_test_mock_algs[param->alg_idx].id,
param->mem_type);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- &payload_data, sizeof(payload_data));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ &payload_data, sizeof(payload_data));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -179,12 +183,12 @@ static void bin_patch_one_multiword(struct kunit *test)
bin_test_mock_algs[param->alg_idx].id,
param->mem_type);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- payload_data, sizeof(payload_data));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ payload_data, sizeof(payload_data));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -230,12 +234,12 @@ static void bin_patch_multi_oneword(struct kunit *test)
/* Add one payload per word */
for (i = 0; i < ARRAY_SIZE(payload_data); ++i) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (param->offset_words + i) * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (param->offset_words + i) * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -287,13 +291,13 @@ static void bin_patch_multi_oneword_unordered(struct kunit *test)
/* Add one payload per word */
for (i = 0; i < ARRAY_SIZE(word_order); ++i) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (param->offset_words + word_order[i]) *
- reg_inc_per_word,
- &payload_data[word_order[i]], sizeof(payload_data[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (param->offset_words + word_order[i]) *
+ reg_inc_per_word,
+ &payload_data[word_order[i]], sizeof(payload_data[0]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -348,12 +352,12 @@ static void bin_patch_multi_oneword_sparse_unordered(struct kunit *test)
/* Add one payload per word */
for (i = 0; i < ARRAY_SIZE(word_offsets); ++i) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- word_offsets[i] * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ word_offsets[i] * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -415,27 +419,27 @@ static void bin_patch_one_word_multiple_mems(struct kunit *test)
}
/* Add words to XM, YM and ZM */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_ADSP2_XM,
- param->offset_words * reg_inc_per_word,
- &payload_data[0], sizeof(payload_data[0]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_ADSP2_YM,
- param->offset_words * reg_inc_per_word,
- &payload_data[1], sizeof(payload_data[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_ADSP2_XM,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[0], sizeof(payload_data[0]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_ADSP2_YM,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[1], sizeof(payload_data[1]));
if (cs_dsp_mock_has_zm(priv)) {
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_ADSP2_ZM,
- param->offset_words * reg_inc_per_word,
- &payload_data[2], sizeof(payload_data[2]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_ADSP2_ZM,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[2], sizeof(payload_data[2]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -504,12 +508,12 @@ static void bin_patch_one_word_multiple_algs(struct kunit *test)
for (i = 0; i < ARRAY_SIZE(bin_test_mock_algs); ++i) {
reg_inc_per_word = cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[i].id,
- bin_test_mock_algs[i].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[i].id,
+ bin_test_mock_algs[i].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -567,12 +571,12 @@ static void bin_patch_one_word_multiple_algs_unordered(struct kunit *test)
alg_idx = alg_order[i];
reg_inc_per_word = cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[alg_idx].id,
- bin_test_mock_algs[alg_idx].ver,
- param->mem_type,
- param->offset_words * reg_inc_per_word,
- &payload_data[i], sizeof(payload_data[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[alg_idx].id,
+ bin_test_mock_algs[alg_idx].ver,
+ param->mem_type,
+ param->offset_words * reg_inc_per_word,
+ &payload_data[i], sizeof(payload_data[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -630,12 +634,12 @@ static void bin_patch_1_packed(struct kunit *test)
patch_pos_words = round_up(alg_base_words + param->offset_words, 4);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -690,20 +694,20 @@ static void bin_patch_1_packed_1_single_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked word following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -770,27 +774,27 @@ static void bin_patch_1_packed_2_single_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 5) - alg_base_words) * 4,
- &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 5) - alg_base_words) * 4,
+ &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -859,34 +863,34 @@ static void bin_patch_1_packed_3_single_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 5) - alg_base_words) * 4,
- &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
-
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 6) - alg_base_words) * 4,
- &unpacked_payloads[2], sizeof(unpacked_payloads[2]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ &unpacked_payloads[0], sizeof(unpacked_payloads[0]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 5) - alg_base_words) * 4,
+ &unpacked_payloads[1], sizeof(unpacked_payloads[1]));
+
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 6) - alg_base_words) * 4,
+ &unpacked_payloads[2], sizeof(unpacked_payloads[2]));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -955,20 +959,20 @@ static void bin_patch_1_packed_2_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1037,20 +1041,20 @@ static void bin_patch_1_packed_3_trailing(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
/* Patch packed block */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
/* ... and the unpacked words following that */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((patch_pos_words + 4) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((patch_pos_words + 4) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1119,20 +1123,20 @@ static void bin_patch_1_single_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked word */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 1) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 1) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1198,26 +1202,26 @@ static void bin_patch_2_single_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 2) - alg_base_words) * 4,
- &unpacked_payload[0], sizeof(unpacked_payload[0]));
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 1) - alg_base_words) * 4,
- &unpacked_payload[1], sizeof(unpacked_payload[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 2) - alg_base_words) * 4,
+ &unpacked_payload[0], sizeof(unpacked_payload[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 1) - alg_base_words) * 4,
+ &unpacked_payload[1], sizeof(unpacked_payload[1]));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1285,20 +1289,20 @@ static void bin_patch_2_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 2) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 2) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1366,32 +1370,32 @@ static void bin_patch_3_single_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 3) - alg_base_words) * 4,
- &unpacked_payload[0], sizeof(unpacked_payload[0]));
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 2) - alg_base_words) * 4,
- &unpacked_payload[1], sizeof(unpacked_payload[1]));
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 1) - alg_base_words) * 4,
- &unpacked_payload[2], sizeof(unpacked_payload[2]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 3) - alg_base_words) * 4,
+ &unpacked_payload[0], sizeof(unpacked_payload[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 2) - alg_base_words) * 4,
+ &unpacked_payload[1], sizeof(unpacked_payload[1]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 1) - alg_base_words) * 4,
+ &unpacked_payload[2], sizeof(unpacked_payload[2]));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1459,20 +1463,20 @@ static void bin_patch_3_leading_1_packed(struct kunit *test)
packed_patch_pos_words = round_up(alg_base_words + param->offset_words, 4) + 4;
/* Patch the leading unpacked words */
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- unpacked_mem_type,
- ((packed_patch_pos_words - 3) - alg_base_words) * 4,
- unpacked_payload, sizeof(unpacked_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ unpacked_mem_type,
+ ((packed_patch_pos_words - 3) - alg_base_words) * 4,
+ unpacked_payload, sizeof(unpacked_payload));
/* ... then the packed block */
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(packed_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- &packed_payload, sizeof(packed_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ &packed_payload, sizeof(packed_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1539,12 +1543,12 @@ static void bin_patch_multi_onepacked(struct kunit *test)
for (i = 0; i < ARRAY_SIZE(packed_payloads); ++i) {
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words + (i * 4));
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- payload_offset,
- &packed_payloads[i], sizeof(packed_payloads[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ &packed_payloads[i], sizeof(packed_payloads[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1604,13 +1608,13 @@ static void bin_patch_multi_onepacked_unordered(struct kunit *test)
patch_pos_in_packed_regs =
_num_words_to_num_packed_regs(patch_pos_words + (payload_order[i] * 4));
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- payload_offset,
- &packed_payloads[payload_order[i]],
- sizeof(packed_payloads[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ &packed_payloads[payload_order[i]],
+ sizeof(packed_payloads[0]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1667,13 +1671,13 @@ static void bin_patch_multi_onepacked_sparse_unordered(struct kunit *test)
patch_pos_words = round_up(alg_base_words + word_offsets[i], 4);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- param->mem_type,
- payload_offset,
- &packed_payloads[i],
- sizeof(packed_payloads[0]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ &packed_payloads[i],
+ sizeof(packed_payloads[0]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1739,21 +1743,21 @@ static void bin_patch_1_packed_multiple_mems(struct kunit *test)
/* Add XM and YM patches */
alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_xm_base_words);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(xm_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_HALO_XM_PACKED,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- packed_xm_payload, sizeof(packed_xm_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_HALO_XM_PACKED,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ packed_xm_payload, sizeof(packed_xm_payload));
alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_ym_base_words);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(ym_patch_pos_words);
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[param->alg_idx].id,
- bin_test_mock_algs[param->alg_idx].ver,
- WMFW_HALO_YM_PACKED,
- (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
- packed_ym_payload, sizeof(packed_ym_payload));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[param->alg_idx].id,
+ bin_test_mock_algs[param->alg_idx].ver,
+ WMFW_HALO_YM_PACKED,
+ (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4,
+ packed_ym_payload, sizeof(packed_ym_payload));
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
KUNIT_ASSERT_EQ(test,
@@ -1823,12 +1827,12 @@ static void bin_patch_1_packed_multiple_algs(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[i].id,
- bin_test_mock_algs[i].ver,
- param->mem_type,
- payload_offset,
- packed_payload[i], sizeof(packed_payload[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[i].id,
+ bin_test_mock_algs[i].ver,
+ param->mem_type,
+ payload_offset,
+ packed_payload[i], sizeof(packed_payload[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -1909,12 +1913,12 @@ static void bin_patch_1_packed_multiple_algs_unordered(struct kunit *test)
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[alg_idx].id,
- bin_test_mock_algs[alg_idx].ver,
- param->mem_type,
- payload_offset,
- packed_payload[i], sizeof(packed_payload[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[alg_idx].id,
+ bin_test_mock_algs[alg_idx].ver,
+ param->mem_type,
+ payload_offset,
+ packed_payload[i], sizeof(packed_payload[i]));
}
fw = cs_dsp_mock_bin_get_firmware(priv->local->bin_builder);
@@ -2006,22 +2010,22 @@ static void bin_patch_mixed_packed_unpacked_random(struct kunit *test)
alg_base_in_packed_regs = _num_words_to_num_packed_regs(alg_base_words);
patch_pos_in_packed_regs = _num_words_to_num_packed_regs(patch_pos_words);
payload_offset = (patch_pos_in_packed_regs - alg_base_in_packed_regs) * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[0].id,
- bin_test_mock_algs[0].ver,
- param->mem_type,
- payload_offset,
- payload->packed[i],
- sizeof(payload->packed[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[0].id,
+ bin_test_mock_algs[0].ver,
+ param->mem_type,
+ payload_offset,
+ payload->packed[i],
+ sizeof(payload->packed[i]));
} else {
payload_offset = offset_words[i] * 4;
- cs_dsp_mock_bin_add_patch(priv->local->bin_builder,
- bin_test_mock_algs[0].id,
- bin_test_mock_algs[0].ver,
- unpacked_mem_type,
- payload_offset,
- &payload->unpacked[i],
- sizeof(payload->unpacked[i]));
+ param->add_patch(priv->local->bin_builder,
+ bin_test_mock_algs[0].id,
+ bin_test_mock_algs[0].ver,
+ unpacked_mem_type,
+ payload_offset,
+ &payload->unpacked[i],
+ sizeof(payload->unpacked[i]));
}
}
@@ -2151,7 +2155,8 @@ static void bin_patch_name_and_info(struct kunit *test)
KUNIT_EXPECT_EQ(test, reg_val, payload_data);
}
-static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp)
+static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp,
+ int wmdr_ver)
{
struct cs_dsp_test *priv;
struct cs_dsp_mock_xm_header *xm_hdr;
@@ -2199,7 +2204,7 @@ static int cs_dsp_bin_test_common_init(struct kunit *test, struct cs_dsp *dsp)
KUNIT_ASSERT_EQ(test, ret, 0);
priv->local->bin_builder =
- cs_dsp_mock_bin_init(priv, 1,
+ cs_dsp_mock_bin_init(priv, wmdr_ver,
cs_dsp_mock_xm_header_get_fw_version(xm_hdr));
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->local->bin_builder);
@@ -2244,7 +2249,7 @@ static void cs_dsp_bin_test_exit(struct kunit *test)
cs_dsp_suppress_info_messages = false;
}
-static int cs_dsp_bin_test_halo_init(struct kunit *test)
+static int cs_dsp_bin_test_halo_init_common(struct kunit *test, int wmdr_ver)
{
struct cs_dsp *dsp;
@@ -2260,7 +2265,17 @@ static int cs_dsp_bin_test_halo_init(struct kunit *test)
dsp->base = cs_dsp_mock_halo_core_base;
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
- return cs_dsp_bin_test_common_init(test, dsp);
+ return cs_dsp_bin_test_common_init(test, dsp, wmdr_ver);
+}
+
+static int cs_dsp_bin_test_halo_init(struct kunit *test)
+{
+ return cs_dsp_bin_test_halo_init_common(test, 1);
+}
+
+static int cs_dsp_bin_test_halo_wmdr3_init(struct kunit *test)
+{
+ return cs_dsp_bin_test_halo_init_common(test, 3);
}
static int cs_dsp_bin_test_adsp2_32bit_init(struct kunit *test)
@@ -2279,7 +2294,7 @@ static int cs_dsp_bin_test_adsp2_32bit_init(struct kunit *test)
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
- return cs_dsp_bin_test_common_init(test, dsp);
+ return cs_dsp_bin_test_common_init(test, dsp, 1);
}
static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test)
@@ -2298,70 +2313,152 @@ static int cs_dsp_bin_test_adsp2_16bit_init(struct kunit *test)
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
- return cs_dsp_bin_test_common_init(test, dsp);
+ return cs_dsp_bin_test_common_init(test, dsp, 1);
}
+#define WMDR_PATCH_SHORT .add_patch = cs_dsp_mock_bin_add_patch
+#define WMDR_PATCH_LONG .add_patch = cs_dsp_mock_bin_add_patch_off32
+
/* Parameterize on choice of XM or YM with a range of word offsets */
static const struct bin_test_param x_or_y_and_offset_param_cases[] = {
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 0 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 1 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 2 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 3 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 4 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 23 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 22 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 21 },
- { .mem_type = WMFW_ADSP2_XM, .offset_words = 20 },
-
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 0 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 1 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 2 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 3 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 4 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 23 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 22 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 21 },
- { .mem_type = WMFW_ADSP2_YM, .offset_words = 20 },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 3, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 23, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 22, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 3, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 23, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param x_or_y_and_long_offset_param_cases[] = {
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 3, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 23, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 22, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 21, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 20, WMDR_PATCH_LONG },
+
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 3, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 23, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 22, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 21, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 20, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .offset_words = 0x2f000, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f003, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f002, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f001, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .offset_words = 0x2f000, WMDR_PATCH_LONG },
};
/* Parameterize on ZM with a range of word offsets */
static const struct bin_test_param z_and_offset_param_cases[] = {
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 1 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 2 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 3 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 4 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 23 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 22 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 21 },
- { .mem_type = WMFW_ADSP2_ZM, .offset_words = 20 },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 3, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 23, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 22, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 21, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .offset_words = 20, WMDR_PATCH_SHORT },
};
/* Parameterize on choice of packed XM or YM with a range of word offsets */
static const struct bin_test_param packed_x_or_y_and_offset_param_cases[] = {
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0 },
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4 },
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8 },
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12 },
-
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12 },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param packed_x_or_y_and_long_offset_param_cases[] = {
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 8, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 12, WMDR_PATCH_LONG },
+
+ /* Offset < 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 4, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 8, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 12, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x10008, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x2f000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0x2f004, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x10008, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x2f000, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0x2f004, WMDR_PATCH_LONG },
};
static void x_or_y_or_z_and_offset_param_desc(const struct bin_test_param *param,
char *desc)
{
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s@%u",
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s@%u %s",
cs_dsp_mem_region_name(param->mem_type),
- param->offset_words);
+ param->offset_words,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(x_or_y_and_offset,
x_or_y_and_offset_param_cases,
x_or_y_or_z_and_offset_param_desc);
+KUNIT_ARRAY_PARAM(x_or_y_and_long_offset,
+ x_or_y_and_long_offset_param_cases,
+ x_or_y_or_z_and_offset_param_desc);
+
KUNIT_ARRAY_PARAM(z_and_offset,
z_and_offset_param_cases,
x_or_y_or_z_and_offset_param_desc);
@@ -2370,103 +2467,181 @@ KUNIT_ARRAY_PARAM(packed_x_or_y_and_offset,
packed_x_or_y_and_offset_param_cases,
x_or_y_or_z_and_offset_param_desc);
+KUNIT_ARRAY_PARAM(packed_x_or_y_and_long_offset,
+ packed_x_or_y_and_long_offset_param_cases,
+ x_or_y_or_z_and_offset_param_desc);
+
/* Parameterize on choice of packed XM or YM */
static const struct bin_test_param packed_x_or_y_param_cases[] = {
- { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0 },
- { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0 },
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param packed_x_or_y_long_param_cases[] = {
+ { .mem_type = WMFW_HALO_XM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .offset_words = 0, WMDR_PATCH_LONG },
};
static void x_or_y_or_z_param_desc(const struct bin_test_param *param,
char *desc)
{
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", cs_dsp_mem_region_name(param->mem_type));
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s %s",
+ cs_dsp_mem_region_name(param->mem_type),
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(packed_x_or_y, packed_x_or_y_param_cases, x_or_y_or_z_param_desc);
+KUNIT_ARRAY_PARAM(packed_x_or_y_long, packed_x_or_y_long_param_cases, x_or_y_or_z_param_desc);
static const struct bin_test_param offset_param_cases[] = {
- { .offset_words = 0 },
- { .offset_words = 1 },
- { .offset_words = 2 },
- { .offset_words = 3 },
- { .offset_words = 4 },
- { .offset_words = 23 },
- { .offset_words = 22 },
- { .offset_words = 21 },
- { .offset_words = 20 },
+ { .offset_words = 0, WMDR_PATCH_SHORT },
+ { .offset_words = 1, WMDR_PATCH_SHORT },
+ { .offset_words = 2, WMDR_PATCH_SHORT },
+ { .offset_words = 3, WMDR_PATCH_SHORT },
+ { .offset_words = 4, WMDR_PATCH_SHORT },
+ { .offset_words = 23, WMDR_PATCH_SHORT },
+ { .offset_words = 22, WMDR_PATCH_SHORT },
+ { .offset_words = 21, WMDR_PATCH_SHORT },
+ { .offset_words = 20, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param long_offset_param_cases[] = {
+ /* Offset < 0xffff in long-offset block type */
+ { .offset_words = 0, WMDR_PATCH_LONG },
+ { .offset_words = 1, WMDR_PATCH_LONG },
+ { .offset_words = 2, WMDR_PATCH_LONG },
+ { .offset_words = 3, WMDR_PATCH_LONG },
+ { .offset_words = 4, WMDR_PATCH_LONG },
+ { .offset_words = 23, WMDR_PATCH_LONG },
+ { .offset_words = 22, WMDR_PATCH_LONG },
+ { .offset_words = 21, WMDR_PATCH_LONG },
+ { .offset_words = 20, WMDR_PATCH_LONG },
+
+ /* Offset > 0xffff in long-offset block type */
+ { .offset_words = 0x10000, WMDR_PATCH_LONG },
+ { .offset_words = 0x10001, WMDR_PATCH_LONG },
+ { .offset_words = 0x10002, WMDR_PATCH_LONG },
+ { .offset_words = 0x10003, WMDR_PATCH_LONG },
+ { .offset_words = 0x10004, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f000, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f001, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f002, WMDR_PATCH_LONG },
+ { .offset_words = 0x2f003, WMDR_PATCH_LONG },
};
static void offset_param_desc(const struct bin_test_param *param, char *desc)
{
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "@%u", param->offset_words);
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "@%u %s",
+ param->offset_words,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(offset, offset_param_cases, offset_param_desc);
+KUNIT_ARRAY_PARAM(long_offset, long_offset_param_cases, offset_param_desc);
static const struct bin_test_param alg_param_cases[] = {
- { .alg_idx = 0 },
- { .alg_idx = 1 },
- { .alg_idx = 2 },
- { .alg_idx = 3 },
+ { .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .alg_idx = 3, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param alg_long_param_cases[] = {
+ { .alg_idx = 0, WMDR_PATCH_LONG },
+ { .alg_idx = 1, WMDR_PATCH_LONG },
+ { .alg_idx = 2, WMDR_PATCH_LONG },
+ { .alg_idx = 3, WMDR_PATCH_LONG },
};
static void alg_param_desc(const struct bin_test_param *param, char *desc)
{
WARN_ON(param->alg_idx >= ARRAY_SIZE(bin_test_mock_algs));
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "alg[%u] (%#x)",
- param->alg_idx, bin_test_mock_algs[param->alg_idx].id);
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "alg[%u] (%#x) %s",
+ param->alg_idx, bin_test_mock_algs[param->alg_idx].id,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(alg, alg_param_cases, alg_param_desc);
+KUNIT_ARRAY_PARAM(alg_long, alg_long_param_cases, alg_param_desc);
static const struct bin_test_param x_or_y_and_alg_param_cases[] = {
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0 },
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1 },
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2 },
- { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3 },
-
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0 },
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1 },
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2 },
- { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3 },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param x_or_y_and_alg_long_param_cases[] = {
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_XM, .alg_idx = 3, WMDR_PATCH_LONG },
+
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_ADSP2_YM, .alg_idx = 3, WMDR_PATCH_LONG },
};
static void x_or_y_or_z_and_alg_param_desc(const struct bin_test_param *param, char *desc)
{
WARN_ON(param->alg_idx >= ARRAY_SIZE(bin_test_mock_algs));
- snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s alg[%u] (%#x)",
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s alg[%u] (%#x) %s",
cs_dsp_mem_region_name(param->mem_type),
- param->alg_idx, bin_test_mock_algs[param->alg_idx].id);
+ param->alg_idx, bin_test_mock_algs[param->alg_idx].id,
+ (param->add_patch == cs_dsp_mock_bin_add_patch_off32) ? "offs32" : "");
}
KUNIT_ARRAY_PARAM(x_or_y_and_alg, x_or_y_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc);
+KUNIT_ARRAY_PARAM(x_or_y_and_alg_long, x_or_y_and_alg_long_param_cases,
+ x_or_y_or_z_and_alg_param_desc);
static const struct bin_test_param z_and_alg_param_cases[] = {
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0 },
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 1 },
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 2 },
- { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 3 },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_ADSP2_ZM, .alg_idx = 3, WMDR_PATCH_SHORT },
};
KUNIT_ARRAY_PARAM(z_and_alg, z_and_alg_param_cases, x_or_y_or_z_and_alg_param_desc);
static const struct bin_test_param packed_x_or_y_and_alg_param_cases[] = {
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0 },
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1 },
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2 },
- { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3 },
-
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0 },
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1 },
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2 },
- { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3 },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT },
+
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2, WMDR_PATCH_SHORT },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_SHORT },
+};
+
+static const struct bin_test_param packed_x_or_y_and_alg_long_param_cases[] = {
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_XM_PACKED, .alg_idx = 3, WMDR_PATCH_LONG },
+
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 0, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 1, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 2, WMDR_PATCH_LONG },
+ { .mem_type = WMFW_HALO_YM_PACKED, .alg_idx = 3, WMDR_PATCH_LONG },
};
KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg, packed_x_or_y_and_alg_param_cases,
x_or_y_or_z_and_alg_param_desc);
+KUNIT_ARRAY_PARAM(packed_x_or_y_and_alg_long, packed_x_or_y_and_alg_long_param_cases,
+ x_or_y_or_z_and_alg_param_desc);
+
static struct kunit_case cs_dsp_bin_test_cases_halo[] = {
/* Unpacked memory */
KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params),
@@ -2522,6 +2697,111 @@ static struct kunit_case cs_dsp_bin_test_cases_halo[] = {
{ } /* terminator */
};
+static struct kunit_case cs_dsp_bin_test_cases_halo_wmdr3[] = {
+ /* Unpacked memory */
+ KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_multiword, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_unordered, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_sparse_unordered, x_or_y_and_alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs, x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs_unordered, x_or_y_and_offset_gen_params),
+
+ /* Packed memory tests */
+ KUNIT_CASE_PARAM(bin_patch_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_1_single_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_single_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_single_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_trailing,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_single_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_single_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_single_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_leading_1_packed,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_unordered,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_sparse_unordered,
+ packed_x_or_y_and_alg_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs_unordered,
+ packed_x_or_y_and_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_mixed_packed_unpacked_random,
+ packed_x_or_y_gen_params),
+
+ /* Unpacked memory with long-offset blocks */
+ KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_multiword, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_unordered, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_mems, alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_oneword_sparse_unordered, x_or_y_and_alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs, x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_one_word_multiple_algs_unordered,
+ x_or_y_and_long_offset_gen_params),
+
+ /* Packed memory tests with long offset blocks */
+ KUNIT_CASE_PARAM(bin_patch_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_1_single_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_single_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_single_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_2_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_3_trailing,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_single_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_single_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_2_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_single_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_3_leading_1_packed,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_unordered,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_mems, alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_multi_onepacked_sparse_unordered,
+ packed_x_or_y_and_alg_long_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_1_packed_multiple_algs_unordered,
+ packed_x_or_y_and_long_offset_gen_params),
+ KUNIT_CASE_PARAM(bin_patch_mixed_packed_unpacked_random,
+ packed_x_or_y_long_gen_params),
+
+ KUNIT_CASE(bin_patch_name_and_info),
+
+ { } /* terminator */
+};
+
static struct kunit_case cs_dsp_bin_test_cases_adsp2[] = {
/* XM and YM */
KUNIT_CASE_PARAM(bin_patch_one_word, x_or_y_and_offset_gen_params),
@@ -2557,6 +2837,12 @@ static struct kunit_suite cs_dsp_bin_test_halo = {
.test_cases = cs_dsp_bin_test_cases_halo,
};
+static struct kunit_suite cs_dsp_bin_test_halo_wmdr3 = {
+ .name = "cs_dsp_bin_halo_wmdr_v3",
+ .init = cs_dsp_bin_test_halo_wmdr3_init,
+ .test_cases = cs_dsp_bin_test_cases_halo_wmdr3,
+};
+
static struct kunit_suite cs_dsp_bin_test_adsp2_32bit = {
.name = "cs_dsp_bin_adsp2_32bit",
.init = cs_dsp_bin_test_adsp2_32bit_init,
@@ -2572,5 +2858,6 @@ static struct kunit_suite cs_dsp_bin_test_adsp2_16bit = {
};
kunit_test_suites(&cs_dsp_bin_test_halo,
+ &cs_dsp_bin_test_halo_wmdr3,
&cs_dsp_bin_test_adsp2_32bit,
&cs_dsp_bin_test_adsp2_16bit);
diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
index 631b9cb9eb25..9b2763b36970 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
@@ -68,24 +68,24 @@ static void bin_load_with_unknown_blocks(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- 0xf5, 0,
+ 0xf5, 0, 0,
random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- 0xf500, 0,
+ 0xf500, 0, 0,
random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- 0xc300, 0,
+ 0xc300, 0, 0,
random_data, sizeof(random_data));
/* Add a single payload to be written to DSP memory */
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- WMFW_ADSP2_YM, 0,
+ WMFW_ADSP2_YM, 0, 0,
payload_data, payload_size_bytes);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
@@ -279,7 +279,7 @@ static void bin_too_short_for_block_header(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- param->block_type, 0,
+ param->block_type, 0, 0,
NULL, 0);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
@@ -311,7 +311,7 @@ static void bin_too_short_for_block_payload(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- param->block_type, 0,
+ param->block_type, 0, 0,
payload, sizeof(payload));
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
@@ -343,7 +343,7 @@ static void bin_block_payload_len_garbage(struct kunit *test)
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
- param->block_type, 0,
+ param->block_type, 0, 0,
&payload, sizeof(payload));
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index f5ff6e84a9b7..17b5f3415465 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -74,10 +74,10 @@ struct mm_struct efi_mm = {
.page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
.user_ns = &init_user_ns,
- .cpu_bitmap = { [BITS_TO_LONGS(NR_CPUS)] = 0},
#ifdef CONFIG_SCHED_MM_CID
.mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(efi_mm.mm_cid.lock),
#endif
+ .flexible_array = MM_STRUCT_FLEXIBLE_ARRAY_INIT,
};
struct workqueue_struct *efi_rts_wq;
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index af9287ff5dc4..2352d099709c 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -301,12 +301,10 @@ static struct brcmstb_gpio_bank *brcmstb_gpio_hwirq_to_bank(
struct brcmstb_gpio_priv *priv, irq_hw_number_t hwirq)
{
struct brcmstb_gpio_bank *bank;
- int i = 0;
- /* banks are in descending order */
- list_for_each_entry_reverse(bank, &priv->bank_list, node) {
- i += bank->chip.gc.ngpio;
- if (hwirq < i)
+ list_for_each_entry(bank, &priv->bank_list, node) {
+ if (hwirq >= bank->chip.gc.offset &&
+ hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio))
return bank;
}
return NULL;
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index e136e81794df..e39723b5901b 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -799,10 +799,13 @@ static struct platform_device omap_mpuio_device = {
static inline void omap_mpuio_init(struct gpio_bank *bank)
{
- platform_set_drvdata(&omap_mpuio_device, bank);
+ static bool registered;
- if (platform_driver_register(&omap_mpuio_driver) == 0)
- (void) platform_device_register(&omap_mpuio_device);
+ platform_set_drvdata(&omap_mpuio_device, bank);
+ if (!registered) {
+ (void)platform_device_register(&omap_mpuio_device);
+ registered = true;
+ }
}
/*---------------------------------------------------------------------*/
@@ -1575,13 +1578,24 @@ static struct platform_driver omap_gpio_driver = {
*/
static int __init omap_gpio_drv_reg(void)
{
- return platform_driver_register(&omap_gpio_driver);
+ int ret;
+
+ ret = platform_driver_register(&omap_mpuio_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&omap_gpio_driver);
+ if (ret)
+ platform_driver_unregister(&omap_mpuio_driver);
+
+ return ret;
}
postcore_initcall(omap_gpio_drv_reg);
static void __exit omap_gpio_exit(void)
{
platform_driver_unregister(&omap_gpio_driver);
+ platform_driver_unregister(&omap_mpuio_driver);
}
module_exit(omap_gpio_exit);
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 8727ae54bc57..f93a3dbb2daa 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -914,6 +914,8 @@ static void pca953x_irq_shutdown(struct irq_data *d)
clear_bit(hwirq, chip->irq_trig_fall);
clear_bit(hwirq, chip->irq_trig_level_low);
clear_bit(hwirq, chip->irq_trig_level_high);
+
+ pca953x_irq_mask(d);
}
static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p)
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index bae2061f15fc..0fff4a699f12 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -18,7 +18,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -164,12 +163,6 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip,
unsigned long flags;
u32 data = input ? 0 : 1;
-
- if (input)
- pinctrl_gpio_direction_input(chip, offset);
- else
- pinctrl_gpio_direction_output(chip, offset);
-
raw_spin_lock_irqsave(&bank->slock, flags);
rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr);
raw_spin_unlock_irqrestore(&bank->slock, flags);
@@ -593,7 +586,6 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
gc->ngpio = bank->nr_pins;
gc->label = bank->name;
gc->parent = bank->dev;
- gc->can_sleep = true;
ret = gpiochip_add_data(gc, bank);
if (ret) {
diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c
index 413bcd0a4240..2cc8abe705cd 100644
--- a/drivers/gpio/gpio-sprd.c
+++ b/drivers/gpio/gpio-sprd.c
@@ -35,7 +35,7 @@
struct sprd_gpio {
struct gpio_chip chip;
void __iomem *base;
- spinlock_t lock;
+ raw_spinlock_t lock;
int irq;
};
@@ -54,7 +54,7 @@ static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset,
unsigned long flags;
u32 tmp;
- spin_lock_irqsave(&sprd_gpio->lock, flags);
+ raw_spin_lock_irqsave(&sprd_gpio->lock, flags);
tmp = readl_relaxed(base + reg);
if (val)
@@ -63,7 +63,7 @@ static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset,
tmp &= ~BIT(SPRD_GPIO_BIT(offset));
writel_relaxed(tmp, base + reg);
- spin_unlock_irqrestore(&sprd_gpio->lock, flags);
+ raw_spin_unlock_irqrestore(&sprd_gpio->lock, flags);
}
static int sprd_gpio_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
@@ -236,7 +236,7 @@ static int sprd_gpio_probe(struct platform_device *pdev)
if (IS_ERR(sprd_gpio->base))
return PTR_ERR(sprd_gpio->base);
- spin_lock_init(&sprd_gpio->lock);
+ raw_spin_lock_init(&sprd_gpio->lock);
sprd_gpio->chip.label = dev_name(&pdev->dev);
sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index 37f2ce20f1ae..098e67d70ffa 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -1682,10 +1682,10 @@ static void gpio_virtuser_device_config_group_release(struct config_item *item)
{
struct gpio_virtuser_device *dev = to_gpio_virtuser_device(item);
- guard(mutex)(&dev->lock);
-
- if (gpio_virtuser_device_is_live(dev))
- gpio_virtuser_device_deactivate(dev);
+ scoped_guard(mutex, &dev->lock) {
+ if (gpio_virtuser_device_is_live(dev))
+ gpio_virtuser_device_deactivate(dev);
+ }
mutex_destroy(&dev->lock);
ida_free(&gpio_virtuser_ida, dev->id);
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index 83dd227dbbec..9627b3a9c7f3 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -1104,6 +1104,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
unsigned int pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
struct gpio_desc *desc;
+ u16 word, shift;
bool found;
mutex_lock(&achip->conn_lock);
@@ -1158,10 +1159,22 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
mutex_unlock(&achip->conn_lock);
- if (function == ACPI_WRITE)
- gpiod_set_raw_value_cansleep(desc, !!(*value & BIT(i)));
- else
- *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i;
+ /*
+ * For the cases when OperationRegion() consists of more than
+ * 64 bits calculate the word and bit shift to use that one to
+ * access the value.
+ */
+ word = i / 64;
+ shift = i % 64;
+
+ if (function == ACPI_WRITE) {
+ gpiod_set_raw_value_cansleep(desc, value[word] & BIT_ULL(shift));
+ } else {
+ if (gpiod_get_raw_value_cansleep(desc))
+ value[word] |= BIT_ULL(shift);
+ else
+ value[word] &= ~BIT_ULL(shift);
+ }
}
out:
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 3735c9fe1502..2adc3c070908 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2549,6 +2549,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
if (!ctx) {
pr_err("Failed to allocate memory for line info notification\n");
+ fput(fp);
return NOTIFY_DONE;
}
@@ -2696,7 +2697,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
- return -ENODEV;
+ return -ENOMEM;
cdev->watched_lines = bitmap_zalloc(gdev->ngpio, GFP_KERNEL);
if (!cdev->watched_lines)
@@ -2796,13 +2797,18 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
return -ENOMEM;
ret = cdev_device_add(&gdev->chrdev, &gdev->dev);
- if (ret)
+ if (ret) {
+ destroy_workqueue(gdev->line_state_wq);
return ret;
+ }
guard(srcu)(&gdev->srcu);
gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc)
+ if (!gc) {
+ cdev_device_del(&gdev->chrdev, &gdev->dev);
+ destroy_workqueue(gdev->line_state_wq);
return -ENODEV;
+ }
gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 17343fdc9758..9e6544203439 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -515,7 +515,7 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
{
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
- unsigned long *flags;
+ struct gpio_desc *desc;
int ret;
list_for_each_entry(entry, &gpio_shared_list, list) {
@@ -543,15 +543,17 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
if (list_count_nodes(&entry->refs) <= 1)
continue;
- flags = &gdev->descs[entry->offset].flags;
+ desc = &gdev->descs[entry->offset];
- __set_bit(GPIOD_FLAG_SHARED, flags);
+ __set_bit(GPIOD_FLAG_SHARED, &desc->flags);
/*
* Shared GPIOs are not requested via the normal path. Make
* them inaccessible to anyone even before we register the
* chip.
*/
- __set_bit(GPIOD_FLAG_REQUESTED, flags);
+ ret = gpiod_request_commit(desc, "shared");
+ if (ret)
+ return ret;
pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
entry->offset, gpio_device_get_label(gdev));
@@ -562,8 +564,10 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
ref->con_id ?: "(none)");
ret = gpio_shared_make_adev(gdev, entry, ref);
- if (ret)
+ if (ret) {
+ gpiod_free_commit(desc);
return ret;
+ }
}
}
@@ -579,6 +583,8 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
+ gpiod_free_commit(&gdev->descs[entry->offset]);
+
list_for_each_entry(ref, &entry->refs, list) {
guard(mutex)(&ref->lock);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index fe2d107b0a84..1578cf3a8c74 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2453,7 +2453,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
*/
-static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
+int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
unsigned int offset;
int ret;
@@ -2515,7 +2515,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
return ret;
}
-static void gpiod_free_commit(struct gpio_desc *desc)
+void gpiod_free_commit(struct gpio_desc *desc)
{
unsigned long flags;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 77f6f2936dc2..3abb90385829 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -244,7 +244,9 @@ DEFINE_CLASS(gpio_chip_guard,
struct gpio_desc *desc)
int gpiod_request(struct gpio_desc *desc, const char *label);
+int gpiod_request_commit(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
+void gpiod_free_commit(struct gpio_desc *desc);
static inline int gpiod_request_user(struct gpio_desc *desc, const char *label)
{
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 7e6bc0b3a589..ed85d0ceee3b 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -210,7 +210,7 @@ config DRM_GPUVM
config DRM_GPUSVM
tristate
- depends on DRM && DEVICE_PRIVATE
+ depends on DRM
select HMM_MIRROR
select MMU_NOTIFIER
help
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0e1c668b46d2..d26191717428 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -108,8 +108,10 @@ obj-$(CONFIG_DRM_EXEC) += drm_exec.o
obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o
drm_gpusvm_helper-y := \
- drm_gpusvm.o\
+ drm_gpusvm.o
+drm_gpusvm_helper-$(CONFIG_ZONE_DEVICE) += \
drm_pagemap.o
+
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm_helper.o
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
index 06c333b2213b..d78d9e7fb9d1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
@@ -763,7 +763,7 @@ void amdgpu_fence_save_wptr(struct amdgpu_fence *af)
}
static void amdgpu_ring_backup_unprocessed_command(struct amdgpu_ring *ring,
- u64 start_wptr, u32 end_wptr)
+ u64 start_wptr, u64 end_wptr)
{
unsigned int first_idx = start_wptr & ring->buf_mask;
unsigned int last_idx = end_wptr & ring->buf_mask;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
index 8924380086c8..d9c7ad297293 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
@@ -498,8 +498,13 @@ void amdgpu_gmc_filter_faults_remove(struct amdgpu_device *adev, uint64_t addr,
if (adev->irq.retry_cam_enabled)
return;
+ else if (adev->irq.ih1.ring_size)
+ ih = &adev->irq.ih1;
+ else if (adev->irq.ih_soft.enabled)
+ ih = &adev->irq.ih_soft;
+ else
+ return;
- ih = &adev->irq.ih1;
/* Get the WPTR of the last entry in IH ring */
last_wptr = amdgpu_ih_get_wptr(adev, ih);
/* Order wptr with ring data. */
@@ -733,8 +738,10 @@ int amdgpu_gmc_flush_gpu_tlb_pasid(struct amdgpu_device *adev, uint16_t pasid,
if (!adev->gmc.flush_pasid_uses_kiq || !ring->sched.ready) {
- if (!adev->gmc.gmc_funcs->flush_gpu_tlb_pasid)
- return 0;
+ if (!adev->gmc.gmc_funcs->flush_gpu_tlb_pasid) {
+ r = 0;
+ goto error_unlock_reset;
+ }
if (adev->gmc.flush_tlb_needs_extra_type_2)
adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
index 586a58facca1..44f230d67da2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
@@ -235,7 +235,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
amdgpu_ring_ib_begin(ring);
- if (ring->funcs->emit_gfx_shadow)
+ if (ring->funcs->emit_gfx_shadow && adev->gfx.cp_gfx_shadow)
amdgpu_ring_emit_gfx_shadow(ring, shadow_va, csa_va, gds_va,
init_shadow, vmid);
@@ -291,7 +291,8 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
fence_flags | AMDGPU_FENCE_FLAG_64BIT);
}
- if (ring->funcs->emit_gfx_shadow && ring->funcs->init_cond_exec) {
+ if (ring->funcs->emit_gfx_shadow && ring->funcs->init_cond_exec &&
+ adev->gfx.cp_gfx_shadow) {
amdgpu_ring_emit_gfx_shadow(ring, 0, 0, 0, false, 0);
amdgpu_ring_init_cond_exec(ring, ring->cond_exe_gpu_addr);
}
@@ -302,7 +303,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
if (job && job->vmid)
amdgpu_vmid_reset(adev, ring->vm_hub, job->vmid);
amdgpu_ring_undo(ring);
- return r;
+ goto free_fence;
}
*f = &af->base;
/* get a ref for the job */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 0a0dcbf0798d..7ccb724b2488 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -217,8 +217,11 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
if (!entity)
return 0;
- return drm_sched_job_init(&(*job)->base, entity, 1, owner,
- drm_client_id);
+ r = drm_sched_job_init(&(*job)->base, entity, 1, owner, drm_client_id);
+ if (!r)
+ return 0;
+
+ kfree((*job)->hw_vm_fence);
err_fence:
kfree((*job)->hw_fence);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
index d75b9940f248..fc65fb36e115 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
@@ -6879,7 +6879,7 @@ static int gfx_v10_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset)
memcpy_toio(mqd, adev->gfx.me.mqd_backup[mqd_idx], sizeof(*mqd));
/* reset the ring */
ring->wptr = 0;
- *ring->wptr_cpu_addr = 0;
+ atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0);
amdgpu_ring_clear_ring(ring);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index 8a2ee2de390f..e642236ea2c5 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -4201,7 +4201,7 @@ static int gfx_v11_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset)
memcpy_toio(mqd, adev->gfx.me.mqd_backup[mqd_idx], sizeof(*mqd));
/* reset the ring */
ring->wptr = 0;
- *ring->wptr_cpu_addr = 0;
+ atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0);
amdgpu_ring_clear_ring(ring);
}
@@ -6823,11 +6823,12 @@ static int gfx_v11_0_reset_kgq(struct amdgpu_ring *ring,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
+ bool use_mmio = false;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, false);
+ r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio);
if (r) {
dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r);
@@ -6836,16 +6837,18 @@ static int gfx_v11_0_reset_kgq(struct amdgpu_ring *ring,
return r;
}
- r = gfx_v11_0_kgq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kgq\n");
- return r;
- }
+ if (use_mmio) {
+ r = gfx_v11_0_kgq_init_queue(ring, true);
+ if (r) {
+ dev_err(adev->dev, "failed to init kgq\n");
+ return r;
+ }
- r = amdgpu_mes_map_legacy_queue(adev, ring);
- if (r) {
- dev_err(adev->dev, "failed to remap kgq\n");
- return r;
+ r = amdgpu_mes_map_legacy_queue(adev, ring);
+ if (r) {
+ dev_err(adev->dev, "failed to remap kgq\n");
+ return r;
+ }
}
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index d01d2712cf57..4aab89a9ab40 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -278,7 +278,6 @@ static void gfx_v12_0_select_se_sh(struct amdgpu_device *adev, u32 se_num,
u32 sh_num, u32 instance, int xcc_id);
static u32 gfx_v12_0_get_wgp_active_bitmap_per_sh(struct amdgpu_device *adev);
-static void gfx_v12_0_ring_emit_frame_cntl(struct amdgpu_ring *ring, bool start, bool secure);
static void gfx_v12_0_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg,
uint32_t val);
static int gfx_v12_0_wait_for_rlc_autoload_complete(struct amdgpu_device *adev);
@@ -3080,7 +3079,7 @@ static int gfx_v12_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset)
memcpy_toio(mqd, adev->gfx.me.mqd_backup[mqd_idx], sizeof(*mqd));
/* reset the ring */
ring->wptr = 0;
- *ring->wptr_cpu_addr = 0;
+ atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0);
amdgpu_ring_clear_ring(ring);
}
@@ -4634,16 +4633,6 @@ static int gfx_v12_0_ring_preempt_ib(struct amdgpu_ring *ring)
return r;
}
-static void gfx_v12_0_ring_emit_frame_cntl(struct amdgpu_ring *ring,
- bool start,
- bool secure)
-{
- uint32_t v = secure ? FRAME_TMZ : 0;
-
- amdgpu_ring_write(ring, PACKET3(PACKET3_FRAME_CONTROL, 0));
- amdgpu_ring_write(ring, v | FRAME_CMD(start ? 0 : 1));
-}
-
static void gfx_v12_0_ring_emit_rreg(struct amdgpu_ring *ring, uint32_t reg,
uint32_t reg_val_offs)
{
@@ -5308,11 +5297,12 @@ static int gfx_v12_0_reset_kgq(struct amdgpu_ring *ring,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
+ bool use_mmio = false;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, false);
+ r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio);
if (r) {
dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r);
r = gfx_v12_reset_gfx_pipe(ring);
@@ -5320,16 +5310,18 @@ static int gfx_v12_0_reset_kgq(struct amdgpu_ring *ring,
return r;
}
- r = gfx_v12_0_kgq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kgq\n");
- return r;
- }
+ if (use_mmio) {
+ r = gfx_v12_0_kgq_init_queue(ring, true);
+ if (r) {
+ dev_err(adev->dev, "failed to init kgq\n");
+ return r;
+ }
- r = amdgpu_mes_map_legacy_queue(adev, ring);
- if (r) {
- dev_err(adev->dev, "failed to remap kgq\n");
- return r;
+ r = amdgpu_mes_map_legacy_queue(adev, ring);
+ if (r) {
+ dev_err(adev->dev, "failed to remap kgq\n");
+ return r;
+ }
}
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
@@ -5520,7 +5512,6 @@ static const struct amdgpu_ring_funcs gfx_v12_0_ring_funcs_gfx = {
.emit_cntxcntl = gfx_v12_0_ring_emit_cntxcntl,
.init_cond_exec = gfx_v12_0_ring_emit_init_cond_exec,
.preempt_ib = gfx_v12_0_ring_preempt_ib,
- .emit_frame_cntl = gfx_v12_0_ring_emit_frame_cntl,
.emit_wreg = gfx_v12_0_ring_emit_wreg,
.emit_reg_wait = gfx_v12_0_ring_emit_reg_wait,
.emit_reg_write_reg_wait = gfx_v12_0_ring_emit_reg_write_reg_wait,
diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c
index ad36c96478a8..25536d89635d 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc21.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc21.c
@@ -225,7 +225,13 @@ static u32 soc21_get_config_memsize(struct amdgpu_device *adev)
static u32 soc21_get_xclk(struct amdgpu_device *adev)
{
- return adev->clock.spll.reference_freq;
+ u32 reference_clock = adev->clock.spll.reference_freq;
+
+ /* reference clock is actually 99.81 Mhz rather than 100 Mhz */
+ if ((adev->flags & AMD_IS_APU) && reference_clock == 10000)
+ return 9981;
+
+ return reference_clock;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_debug.h b/drivers/gpu/drm/amd/amdkfd/kfd_debug.h
index 27aa1a5b120f..fbb751821c69 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_debug.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_debug.h
@@ -120,8 +120,7 @@ static inline bool kfd_dbg_has_gws_support(struct kfd_node *dev)
&& dev->kfd->mec2_fw_version < 0x1b6) ||
(KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 1)
&& dev->kfd->mec2_fw_version < 0x30) ||
- (KFD_GC_VERSION(dev) >= IP_VERSION(11, 0, 0) &&
- KFD_GC_VERSION(dev) < IP_VERSION(12, 0, 0)))
+ kfd_dbg_has_cwsr_workaround(dev))
return false;
/* Assume debugging and cooperative launch supported otherwise. */
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
index af53e796ea1b..6ada7b4af7c6 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
@@ -217,7 +217,7 @@ svm_migrate_get_vram_page(struct svm_range *prange, unsigned long pfn)
page = pfn_to_page(pfn);
svm_range_bo_ref(prange->svm_bo);
page->zone_device_data = prange->svm_bo;
- zone_device_page_init(page, 0);
+ zone_device_page_init(page, page_pgmap(page), 0);
}
static void
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1ea5a250440f..a8a59126b2d2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7754,10 +7754,12 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
/* Cancel and flush any pending HDMI HPD debounce work */
- cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
+ if (aconnector->hdmi_hpd_debounce_delay_ms) {
+ cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
+ if (aconnector->hdmi_prev_sink) {
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = NULL;
+ }
}
if (aconnector->bl_idx != -1) {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index d585618b8064..a2de3bba8346 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -79,7 +79,6 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr
goto cleanup;
list->type = ops[i]->base.id;
- list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id);
i++;
@@ -197,6 +196,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr
goto cleanup;
drm_colorop_set_next_property(ops[i-1], ops[i]);
+
+ list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[0]->base.id);
+
return 0;
cleanup:
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 697e232acebf..9fcd72d87d25 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -248,8 +248,6 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
struct vblank_control_work *vblank_work =
container_of(work, struct vblank_control_work, work);
struct amdgpu_display_manager *dm = vblank_work->dm;
- struct amdgpu_device *adev = drm_to_adev(dm->ddev);
- int r;
mutex_lock(&dm->dc_lock);
@@ -279,16 +277,7 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
if (dm->active_vblank_irq_count == 0) {
dc_post_update_surfaces_to_stream(dm->dc);
-
- r = amdgpu_dpm_pause_power_profile(adev, true);
- if (r)
- dev_warn(adev->dev, "failed to set default power profile mode\n");
-
dc_allow_idle_optimizations(dm->dc, true);
-
- r = amdgpu_dpm_pause_power_profile(adev, false);
- if (r)
- dev_warn(adev->dev, "failed to restore the power profile mode\n");
}
mutex_unlock(&dm->dc_lock);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
index 0a2a3f233a0e..e7b0928bd3db 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@ -915,13 +915,19 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
struct amdgpu_dm_connector *amdgpu_dm_connector;
const struct dc_link *dc_link;
- use_polling |= connector->polled != DRM_CONNECTOR_POLL_HPD;
-
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
continue;
amdgpu_dm_connector = to_amdgpu_dm_connector(connector);
+ /*
+ * Analog connectors may be hot-plugged unlike other connector
+ * types that don't support HPD. Only poll analog connectors.
+ */
+ use_polling |=
+ amdgpu_dm_connector->dc_link &&
+ dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id);
+
dc_link = amdgpu_dm_connector->dc_link;
/*
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index 2e3ee78999d9..7c4496fb4b9d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -1790,12 +1790,13 @@ dm_atomic_plane_get_property(struct drm_plane *plane,
static int
dm_plane_init_colorops(struct drm_plane *plane)
{
- struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+ struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES] = {};
struct drm_device *dev = plane->dev;
struct amdgpu_device *adev = drm_to_adev(dev);
struct dc *dc = adev->dm.dc;
int len = 0;
- int ret;
+ int ret = 0;
+ int i;
if (plane->type == DRM_PLANE_TYPE_CURSOR)
return 0;
@@ -1806,7 +1807,7 @@ dm_plane_init_colorops(struct drm_plane *plane)
if (ret) {
drm_err(plane->dev, "Failed to create color pipeline for plane %d: %d\n",
plane->base.id, ret);
- return ret;
+ goto out;
}
len++;
@@ -1814,7 +1815,11 @@ dm_plane_init_colorops(struct drm_plane *plane)
drm_plane_create_color_pipeline_property(plane, pipelines, len);
}
- return 0;
+out:
+ for (i = 0; i < len; i++)
+ kfree(pipelines[i].name);
+
+ return ret;
}
#endif
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index 79b174e5326d..302af1fb6901 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -80,15 +80,15 @@ int amdgpu_dpm_set_powergating_by_smu(struct amdgpu_device *adev,
enum ip_power_state pwr_state = gate ? POWER_STATE_OFF : POWER_STATE_ON;
bool is_vcn = block_type == AMD_IP_BLOCK_TYPE_VCN;
+ mutex_lock(&adev->pm.mutex);
+
if (atomic_read(&adev->pm.pwr_state[block_type]) == pwr_state &&
(!is_vcn || adev->vcn.num_vcn_inst == 1)) {
dev_dbg(adev->dev, "IP block%d already in the target %s state!",
block_type, gate ? "gate" : "ungate");
- return 0;
+ goto out_unlock;
}
- mutex_lock(&adev->pm.mutex);
-
switch (block_type) {
case AMD_IP_BLOCK_TYPE_UVD:
case AMD_IP_BLOCK_TYPE_VCE:
@@ -115,6 +115,7 @@ int amdgpu_dpm_set_powergating_by_smu(struct amdgpu_device *adev,
if (!ret)
atomic_set(&adev->pm.pwr_state[block_type], pwr_state);
+out_unlock:
mutex_unlock(&adev->pm.mutex);
return ret;
diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
index 1f539cc65f41..695432d3045f 100644
--- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
+++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
@@ -2273,8 +2273,6 @@ static int si_populate_smc_tdp_limits(struct amdgpu_device *adev,
if (scaling_factor == 0)
return -EINVAL;
- memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
-
ret = si_calculate_adjusted_tdp_limits(adev,
false, /* ??? */
adev->pm.dpm.tdp_adjustment,
@@ -2283,6 +2281,12 @@ static int si_populate_smc_tdp_limits(struct amdgpu_device *adev,
if (ret)
return ret;
+ if (adev->pdev->device == 0x6611 && adev->pdev->revision == 0x87) {
+ /* Workaround buggy powertune on Radeon 430 and 520. */
+ tdp_limit = 32;
+ near_tdp_limit = 28;
+ }
+
smc_table->dpm2Params.TDPLimit =
cpu_to_be32(si_scale_power_for_smc(tdp_limit, scaling_factor) * 1000);
smc_table->dpm2Params.NearTDPLimit =
@@ -2328,16 +2332,8 @@ static int si_populate_smc_tdp_limits_2(struct amdgpu_device *adev,
if (ni_pi->enable_power_containment) {
SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
- u32 scaling_factor = si_get_smc_power_scaling_factor(adev);
int ret;
- memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
-
- smc_table->dpm2Params.NearTDPLimit =
- cpu_to_be32(si_scale_power_for_smc(adev->pm.dpm.near_tdp_limit_adjusted, scaling_factor) * 1000);
- smc_table->dpm2Params.SafePowerLimit =
- cpu_to_be32(si_scale_power_for_smc((adev->pm.dpm.near_tdp_limit_adjusted * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
-
ret = amdgpu_si_copy_bytes_to_smc(adev,
(si_pi->state_table_start +
offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
@@ -3473,10 +3469,15 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev,
(adev->pdev->revision == 0x80) ||
(adev->pdev->revision == 0x81) ||
(adev->pdev->revision == 0x83) ||
- (adev->pdev->revision == 0x87) ||
+ (adev->pdev->revision == 0x87 &&
+ adev->pdev->device != 0x6611) ||
(adev->pdev->device == 0x6604) ||
(adev->pdev->device == 0x6605)) {
max_sclk = 75000;
+ } else if (adev->pdev->revision == 0x87 &&
+ adev->pdev->device == 0x6611) {
+ /* Radeon 430 and 520 */
+ max_sclk = 78000;
}
}
@@ -7600,12 +7601,12 @@ static int si_dpm_set_interrupt_state(struct amdgpu_device *adev,
case AMDGPU_IRQ_STATE_DISABLE:
cg_thermal_int = RREG32_SMC(mmCG_THERMAL_INT);
cg_thermal_int |= CG_THERMAL_INT__THERM_INT_MASK_HIGH_MASK;
- WREG32_SMC(mmCG_THERMAL_INT, cg_thermal_int);
+ WREG32(mmCG_THERMAL_INT, cg_thermal_int);
break;
case AMDGPU_IRQ_STATE_ENABLE:
cg_thermal_int = RREG32_SMC(mmCG_THERMAL_INT);
cg_thermal_int &= ~CG_THERMAL_INT__THERM_INT_MASK_HIGH_MASK;
- WREG32_SMC(mmCG_THERMAL_INT, cg_thermal_int);
+ WREG32(mmCG_THERMAL_INT, cg_thermal_int);
break;
default:
break;
@@ -7617,12 +7618,12 @@ static int si_dpm_set_interrupt_state(struct amdgpu_device *adev,
case AMDGPU_IRQ_STATE_DISABLE:
cg_thermal_int = RREG32_SMC(mmCG_THERMAL_INT);
cg_thermal_int |= CG_THERMAL_INT__THERM_INT_MASK_LOW_MASK;
- WREG32_SMC(mmCG_THERMAL_INT, cg_thermal_int);
+ WREG32(mmCG_THERMAL_INT, cg_thermal_int);
break;
case AMDGPU_IRQ_STATE_ENABLE:
cg_thermal_int = RREG32_SMC(mmCG_THERMAL_INT);
cg_thermal_int &= ~CG_THERMAL_INT__THERM_INT_MASK_LOW_MASK;
- WREG32_SMC(mmCG_THERMAL_INT, cg_thermal_int);
+ WREG32(mmCG_THERMAL_INT, cg_thermal_int);
break;
default:
break;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
index 4263798d716b..8e592a477c33 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
@@ -56,6 +56,7 @@
#define SMUQ10_TO_UINT(x) ((x) >> 10)
#define SMUQ10_FRAC(x) ((x) & 0x3ff)
#define SMUQ10_ROUND(x) ((SMUQ10_TO_UINT(x)) + ((SMUQ10_FRAC(x)) >= 0x200))
+#define SMU_V13_SOFT_FREQ_ROUND(x) ((x) + 1)
extern const int pmfw_decoded_link_speed[5];
extern const int pmfw_decoded_link_width[7];
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h
index 29a4583db873..0b1e6f25e611 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h
@@ -57,6 +57,7 @@ extern const int decoded_link_width[8];
#define DECODE_GEN_SPEED(gen_speed_idx) (decoded_link_speed[gen_speed_idx])
#define DECODE_LANE_WIDTH(lane_width_idx) (decoded_link_width[lane_width_idx])
+#define SMU_V14_SOFT_FREQ_ROUND(x) ((x) + 1)
struct smu_14_0_max_sustainable_clocks {
uint32_t display_clock;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
index a89075e25717..2efd914d81e5 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
@@ -1555,6 +1555,7 @@ int smu_v13_0_set_soft_freq_limited_range(struct smu_context *smu,
return clk_id;
if (max > 0) {
+ max = SMU_V13_SOFT_FREQ_ROUND(max);
if (automatic)
param = (uint32_t)((clk_id << 16) | 0xffff);
else
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
index f2a16dfee599..06a81533759c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
@@ -1178,6 +1178,7 @@ int smu_v14_0_set_soft_freq_limited_range(struct smu_context *smu,
return clk_id;
if (max > 0) {
+ max = SMU_V14_SOFT_FREQ_ROUND(max);
if (automatic)
param = (uint32_t)((clk_id << 16) | 0xffff);
else
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 82aaf74e1bc0..432342452484 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -2062,33 +2062,41 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
}
ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
- if (ret)
+ if (ret) {
dev_err_probe(dev, ret, "Failed to attach bridge\n");
+ goto unregister_aux;
+ }
dw_dp_init_hw(dp);
ret = phy_init(dp->phy);
if (ret) {
dev_err_probe(dev, ret, "phy init failed\n");
- return ERR_PTR(ret);
+ goto unregister_aux;
}
ret = devm_add_action_or_reset(dev, dw_dp_phy_exit, dp);
if (ret)
- return ERR_PTR(ret);
+ goto unregister_aux;
dp->irq = platform_get_irq(pdev, 0);
- if (dp->irq < 0)
- return ERR_PTR(ret);
+ if (dp->irq < 0) {
+ ret = dp->irq;
+ goto unregister_aux;
+ }
ret = devm_request_threaded_irq(dev, dp->irq, NULL, dw_dp_irq,
IRQF_ONESHOT, dev_name(dev), dp);
if (ret) {
dev_err_probe(dev, ret, "failed to request irq\n");
- return ERR_PTR(ret);
+ goto unregister_aux;
}
return dp;
+
+unregister_aux:
+ drm_dp_aux_unregister(&dp->aux);
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dw_dp_bind);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index e4df43427394..25f68fed9b48 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -960,16 +960,21 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
{
struct drm_gem_change_handle *args = data;
struct drm_gem_object *obj;
- int ret;
+ int handle, ret;
if (!drm_core_check_feature(dev, DRIVER_GEM))
return -EOPNOTSUPP;
+ /* idr_alloc() limitation. */
+ if (args->new_handle > INT_MAX)
+ return -EINVAL;
+ handle = args->new_handle;
+
obj = drm_gem_object_lookup(file_priv, args->handle);
if (!obj)
return -ENOENT;
- if (args->handle == args->new_handle) {
+ if (args->handle == handle) {
ret = 0;
goto out;
}
@@ -977,18 +982,19 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
mutex_lock(&file_priv->prime.lock);
spin_lock(&file_priv->table_lock);
- ret = idr_alloc(&file_priv->object_idr, obj,
- args->new_handle, args->new_handle + 1, GFP_NOWAIT);
+ ret = idr_alloc(&file_priv->object_idr, obj, handle, handle + 1,
+ GFP_NOWAIT);
spin_unlock(&file_priv->table_lock);
if (ret < 0)
goto out_unlock;
if (obj->dma_buf) {
- ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf, args->new_handle);
+ ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf,
+ handle);
if (ret < 0) {
spin_lock(&file_priv->table_lock);
- idr_remove(&file_priv->object_idr, args->new_handle);
+ idr_remove(&file_priv->object_idr, handle);
spin_unlock(&file_priv->table_lock);
goto out_unlock;
}
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index 06c1bd8fc4d1..704f2f945019 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -197,7 +197,7 @@ static void drm_pagemap_get_devmem_page(struct page *page,
struct drm_pagemap_zdd *zdd)
{
page->zone_device_data = drm_pagemap_zdd_get(zdd);
- zone_device_page_init(page, 0);
+ zone_device_page_init(page, page_pgmap(page), 0);
}
/**
diff --git a/drivers/gpu/drm/i915/display/intel_color_pipeline.c b/drivers/gpu/drm/i915/display/intel_color_pipeline.c
index 942d9b9c93ce..04af552b3648 100644
--- a/drivers/gpu/drm/i915/display/intel_color_pipeline.c
+++ b/drivers/gpu/drm/i915/display/intel_color_pipeline.c
@@ -34,11 +34,19 @@ int _intel_color_pipeline_plane_init(struct drm_plane *plane, struct drm_prop_en
return ret;
list->type = colorop->base.base.id;
- list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", colorop->base.base.id);
/* TODO: handle failures and clean up */
prev_op = &colorop->base;
+ colorop = intel_colorop_create(INTEL_PLANE_CB_CSC);
+ ret = drm_plane_colorop_ctm_3x4_init(dev, &colorop->base, plane,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ return ret;
+
+ drm_colorop_set_next_property(prev_op, &colorop->base);
+ prev_op = &colorop->base;
+
if (DISPLAY_VER(display) >= 35 &&
intel_color_crtc_has_3dlut(display, pipe) &&
plane->type == DRM_PLANE_TYPE_PRIMARY) {
@@ -55,15 +63,6 @@ int _intel_color_pipeline_plane_init(struct drm_plane *plane, struct drm_prop_en
prev_op = &colorop->base;
}
- colorop = intel_colorop_create(INTEL_PLANE_CB_CSC);
- ret = drm_plane_colorop_ctm_3x4_init(dev, &colorop->base, plane,
- DRM_COLOROP_FLAG_ALLOW_BYPASS);
- if (ret)
- return ret;
-
- drm_colorop_set_next_property(prev_op, &colorop->base);
- prev_op = &colorop->base;
-
colorop = intel_colorop_create(INTEL_PLANE_CB_POST_CSC_LUT);
ret = drm_plane_colorop_curve_1d_lut_init(dev, &colorop->base, plane,
PLANE_GAMMA_SIZE,
@@ -74,6 +73,8 @@ int _intel_color_pipeline_plane_init(struct drm_plane *plane, struct drm_prop_en
drm_colorop_set_next_property(prev_op, &colorop->base);
+ list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", list->type);
+
return 0;
}
@@ -81,9 +82,10 @@ int intel_color_pipeline_plane_init(struct drm_plane *plane, enum pipe pipe)
{
struct drm_device *dev = plane->dev;
struct intel_display *display = to_intel_display(dev);
- struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+ struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES] = {};
int len = 0;
- int ret;
+ int ret = 0;
+ int i;
/* Currently expose pipeline only for HDR planes */
if (!icl_is_hdr_plane(display, to_intel_plane(plane)->id))
@@ -92,8 +94,14 @@ int intel_color_pipeline_plane_init(struct drm_plane *plane, enum pipe pipe)
/* Add pipeline consisting of transfer functions */
ret = _intel_color_pipeline_plane_init(plane, &pipelines[len], pipe);
if (ret)
- return ret;
+ goto out;
len++;
- return drm_plane_create_color_pipeline_property(plane, pipelines, len);
+ ret = drm_plane_create_color_pipeline_property(plane, pipelines, len);
+
+ for (i = 0; i < len; i++)
+ kfree(pipelines[i].name);
+
+out:
+ return ret;
}
diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c
index 8a56952f6730..99d681413eff 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
@@ -137,6 +137,7 @@ update_logtype(struct pvr_device *pvr_dev, u32 group_mask)
struct rogue_fwif_kccb_cmd cmd;
int idx;
int err;
+ int slot;
if (group_mask)
fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_TRACE | group_mask;
@@ -154,8 +155,13 @@ update_logtype(struct pvr_device *pvr_dev, u32 group_mask)
cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_LOGTYPE_UPDATE;
cmd.kccb_flags = 0;
- err = pvr_kccb_send_cmd(pvr_dev, &cmd, NULL);
+ err = pvr_kccb_send_cmd(pvr_dev, &cmd, &slot);
+ if (err)
+ goto err_drm_dev_exit;
+
+ err = pvr_kccb_wait_for_completion(pvr_dev, slot, HZ, NULL);
+err_drm_dev_exit:
drm_dev_exit(idx);
err_up_read:
diff --git a/drivers/gpu/drm/imx/ipuv3/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c
index c5c6e070cc06..e861b8b9d8fa 100644
--- a/drivers/gpu/drm/imx/ipuv3/imx-tve.c
+++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c
@@ -528,6 +528,13 @@ static const struct component_ops imx_tve_ops = {
.bind = imx_tve_bind,
};
+static void imx_tve_put_device(void *_dev)
+{
+ struct device *dev = _dev;
+
+ put_device(dev);
+}
+
static int imx_tve_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -549,6 +556,12 @@ static int imx_tve_probe(struct platform_device *pdev)
if (ddc_node) {
tve->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
+ if (tve->ddc) {
+ ret = devm_add_action_or_reset(dev, imx_tve_put_device,
+ &tve->ddc->dev);
+ if (ret)
+ return ret;
+ }
}
tve->mode = of_get_tve_mode(np);
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 96188bf9274a..ad8c8b823681 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -8,7 +8,7 @@ config DRM_MEDIATEK
depends on OF
depends on MTK_MMSYS
select DRM_CLIENT_SELECTION
- select DRM_GEM_DMA_HELPER if DRM_FBDEV_EMULATION
+ select DRM_GEM_DMA_HELPER
select DRM_KMS_HELPER
select DRM_DISPLAY_HELPER
select DRM_BRIDGE_CONNECTOR
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 61cab32e213a..53360b5d12ba 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -836,20 +836,6 @@ static int mtk_dpi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct mtk_dpi *dpi = bridge_to_dpi(bridge);
- int ret;
-
- dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 1, -1);
- if (IS_ERR(dpi->next_bridge)) {
- ret = PTR_ERR(dpi->next_bridge);
- if (ret == -EPROBE_DEFER)
- return ret;
-
- /* Old devicetree has only one endpoint */
- dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 0, 0);
- if (IS_ERR(dpi->next_bridge))
- return dev_err_probe(dpi->dev, PTR_ERR(dpi->next_bridge),
- "Failed to get bridge\n");
- }
return drm_bridge_attach(encoder, dpi->next_bridge,
&dpi->bridge, flags);
@@ -1319,6 +1305,15 @@ static int mtk_dpi_probe(struct platform_device *pdev)
if (dpi->irq < 0)
return dpi->irq;
+ dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 1, -1);
+ if (IS_ERR(dpi->next_bridge) && PTR_ERR(dpi->next_bridge) == -ENODEV) {
+ /* Old devicetree has only one endpoint */
+ dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 0, 0);
+ }
+ if (IS_ERR(dpi->next_bridge))
+ return dev_err_probe(dpi->dev, PTR_ERR(dpi->next_bridge),
+ "Failed to get bridge\n");
+
platform_set_drvdata(pdev, dpi);
dpi->bridge.of_node = dev->of_node;
diff --git a/drivers/gpu/drm/mediatek/mtk_gem.c b/drivers/gpu/drm/mediatek/mtk_gem.c
index 024cc7e9036c..7525a9f9907a 100644
--- a/drivers/gpu/drm/mediatek/mtk_gem.c
+++ b/drivers/gpu/drm/mediatek/mtk_gem.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 MediaTek Inc.
+ * Copyright (c) 2025 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/dma-buf.h>
@@ -18,24 +20,64 @@
static int mtk_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
-static const struct vm_operations_struct vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
-};
+static void mtk_gem_free_object(struct drm_gem_object *obj)
+{
+ struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj);
+ struct mtk_drm_private *priv = obj->dev->dev_private;
+
+ if (dma_obj->sgt)
+ drm_prime_gem_destroy(obj, dma_obj->sgt);
+ else
+ dma_free_wc(priv->dma_dev, dma_obj->base.size,
+ dma_obj->vaddr, dma_obj->dma_addr);
+
+ /* release file pointer to gem object. */
+ drm_gem_object_release(obj);
+
+ kfree(dma_obj);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ * the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+static struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj);
+ struct mtk_drm_private *priv = obj->dev->dev_private;
+ struct sg_table *sgt;
+ int ret;
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = dma_get_sgtable(priv->dma_dev, sgt, dma_obj->vaddr,
+ dma_obj->dma_addr, obj->size);
+ if (ret) {
+ DRM_ERROR("failed to allocate sgt, %d\n", ret);
+ kfree(sgt);
+ return ERR_PTR(ret);
+ }
+
+ return sgt;
+}
static const struct drm_gem_object_funcs mtk_gem_object_funcs = {
.free = mtk_gem_free_object,
+ .print_info = drm_gem_dma_object_print_info,
.get_sg_table = mtk_gem_prime_get_sg_table,
- .vmap = mtk_gem_prime_vmap,
- .vunmap = mtk_gem_prime_vunmap,
+ .vmap = drm_gem_dma_object_vmap,
.mmap = mtk_gem_object_mmap,
- .vm_ops = &vm_ops,
+ .vm_ops = &drm_gem_dma_vm_ops,
};
-static struct mtk_gem_obj *mtk_gem_init(struct drm_device *dev,
- unsigned long size)
+static struct drm_gem_dma_object *mtk_gem_init(struct drm_device *dev,
+ unsigned long size, bool private)
{
- struct mtk_gem_obj *mtk_gem_obj;
+ struct drm_gem_dma_object *dma_obj;
int ret;
size = round_up(size, PAGE_SIZE);
@@ -43,86 +85,65 @@ static struct mtk_gem_obj *mtk_gem_init(struct drm_device *dev,
if (size == 0)
return ERR_PTR(-EINVAL);
- mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL);
- if (!mtk_gem_obj)
+ dma_obj = kzalloc(sizeof(*dma_obj), GFP_KERNEL);
+ if (!dma_obj)
return ERR_PTR(-ENOMEM);
- mtk_gem_obj->base.funcs = &mtk_gem_object_funcs;
+ dma_obj->base.funcs = &mtk_gem_object_funcs;
- ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size);
- if (ret < 0) {
+ if (private) {
+ ret = 0;
+ drm_gem_private_object_init(dev, &dma_obj->base, size);
+ } else {
+ ret = drm_gem_object_init(dev, &dma_obj->base, size);
+ }
+ if (ret) {
DRM_ERROR("failed to initialize gem object\n");
- kfree(mtk_gem_obj);
+ kfree(dma_obj);
return ERR_PTR(ret);
}
- return mtk_gem_obj;
+ return dma_obj;
}
-struct mtk_gem_obj *mtk_gem_create(struct drm_device *dev,
- size_t size, bool alloc_kmap)
+static struct drm_gem_dma_object *mtk_gem_create(struct drm_device *dev, size_t size)
{
struct mtk_drm_private *priv = dev->dev_private;
- struct mtk_gem_obj *mtk_gem;
+ struct drm_gem_dma_object *dma_obj;
struct drm_gem_object *obj;
int ret;
- mtk_gem = mtk_gem_init(dev, size);
- if (IS_ERR(mtk_gem))
- return ERR_CAST(mtk_gem);
-
- obj = &mtk_gem->base;
+ dma_obj = mtk_gem_init(dev, size, false);
+ if (IS_ERR(dma_obj))
+ return ERR_CAST(dma_obj);
- mtk_gem->dma_attrs = DMA_ATTR_WRITE_COMBINE;
+ obj = &dma_obj->base;
- if (!alloc_kmap)
- mtk_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
-
- mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
- &mtk_gem->dma_addr, GFP_KERNEL,
- mtk_gem->dma_attrs);
- if (!mtk_gem->cookie) {
+ dma_obj->vaddr = dma_alloc_wc(priv->dma_dev, obj->size,
+ &dma_obj->dma_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!dma_obj->vaddr) {
DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
ret = -ENOMEM;
goto err_gem_free;
}
- if (alloc_kmap)
- mtk_gem->kvaddr = mtk_gem->cookie;
-
- DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n",
- mtk_gem->cookie, &mtk_gem->dma_addr,
+ DRM_DEBUG_DRIVER("vaddr = %p dma_addr = %pad size = %zu\n",
+ dma_obj->vaddr, &dma_obj->dma_addr,
size);
- return mtk_gem;
+ return dma_obj;
err_gem_free:
drm_gem_object_release(obj);
- kfree(mtk_gem);
+ kfree(dma_obj);
return ERR_PTR(ret);
}
-void mtk_gem_free_object(struct drm_gem_object *obj)
-{
- struct mtk_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
- struct mtk_drm_private *priv = obj->dev->dev_private;
-
- if (mtk_gem->sg)
- drm_prime_gem_destroy(obj, mtk_gem->sg);
- else
- dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
- mtk_gem->dma_addr, mtk_gem->dma_attrs);
-
- /* release file pointer to gem object. */
- drm_gem_object_release(obj);
-
- kfree(mtk_gem);
-}
-
int mtk_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- struct mtk_gem_obj *mtk_gem;
+ struct drm_gem_dma_object *dma_obj;
int ret;
args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
@@ -135,25 +156,25 @@ int mtk_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
args->size = args->pitch;
args->size *= args->height;
- mtk_gem = mtk_gem_create(dev, args->size, false);
- if (IS_ERR(mtk_gem))
- return PTR_ERR(mtk_gem);
+ dma_obj = mtk_gem_create(dev, args->size);
+ if (IS_ERR(dma_obj))
+ return PTR_ERR(dma_obj);
/*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see.
*/
- ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle);
+ ret = drm_gem_handle_create(file_priv, &dma_obj->base, &args->handle);
if (ret)
goto err_handle_create;
/* drop reference from allocate - handle holds it now. */
- drm_gem_object_put(&mtk_gem->base);
+ drm_gem_object_put(&dma_obj->base);
return 0;
err_handle_create:
- mtk_gem_free_object(&mtk_gem->base);
+ mtk_gem_free_object(&dma_obj->base);
return ret;
}
@@ -161,129 +182,50 @@ static int mtk_gem_object_mmap(struct drm_gem_object *obj,
struct vm_area_struct *vma)
{
- int ret;
- struct mtk_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
+ struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj);
struct mtk_drm_private *priv = obj->dev->dev_private;
+ int ret;
/*
* Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the
* whole buffer from the start.
*/
- vma->vm_pgoff = 0;
+ vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node);
/*
* dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear
* VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
*/
- vm_flags_set(vma, VM_IO | VM_DONTEXPAND | VM_DONTDUMP);
+ vm_flags_mod(vma, VM_IO | VM_DONTEXPAND | VM_DONTDUMP, VM_PFNMAP);
+
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
- ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
- mtk_gem->dma_addr, obj->size, mtk_gem->dma_attrs);
+ ret = dma_mmap_wc(priv->dma_dev, vma, dma_obj->vaddr,
+ dma_obj->dma_addr, obj->size);
+ if (ret)
+ drm_gem_vm_close(vma);
return ret;
}
-/*
- * Allocate a sg_table for this GEM object.
- * Note: Both the table's contents, and the sg_table itself must be freed by
- * the caller.
- * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
- */
-struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
-{
- struct mtk_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
- struct mtk_drm_private *priv = obj->dev->dev_private;
- struct sg_table *sgt;
- int ret;
-
- sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
- if (!sgt)
- return ERR_PTR(-ENOMEM);
-
- ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
- mtk_gem->dma_addr, obj->size,
- mtk_gem->dma_attrs);
- if (ret) {
- DRM_ERROR("failed to allocate sgt, %d\n", ret);
- kfree(sgt);
- return ERR_PTR(ret);
- }
-
- return sgt;
-}
-
struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
- struct dma_buf_attachment *attach, struct sg_table *sg)
+ struct dma_buf_attachment *attach, struct sg_table *sgt)
{
- struct mtk_gem_obj *mtk_gem;
+ struct drm_gem_dma_object *dma_obj;
/* check if the entries in the sg_table are contiguous */
- if (drm_prime_get_contiguous_size(sg) < attach->dmabuf->size) {
+ if (drm_prime_get_contiguous_size(sgt) < attach->dmabuf->size) {
DRM_ERROR("sg_table is not contiguous");
return ERR_PTR(-EINVAL);
}
- mtk_gem = mtk_gem_init(dev, attach->dmabuf->size);
- if (IS_ERR(mtk_gem))
- return ERR_CAST(mtk_gem);
-
- mtk_gem->dma_addr = sg_dma_address(sg->sgl);
- mtk_gem->sg = sg;
-
- return &mtk_gem->base;
-}
-
-int mtk_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
-{
- struct mtk_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
- struct sg_table *sgt = NULL;
- unsigned int npages;
-
- if (mtk_gem->kvaddr)
- goto out;
-
- sgt = mtk_gem_prime_get_sg_table(obj);
- if (IS_ERR(sgt))
- return PTR_ERR(sgt);
-
- npages = obj->size >> PAGE_SHIFT;
- mtk_gem->pages = kcalloc(npages, sizeof(*mtk_gem->pages), GFP_KERNEL);
- if (!mtk_gem->pages) {
- sg_free_table(sgt);
- kfree(sgt);
- return -ENOMEM;
- }
-
- drm_prime_sg_to_page_array(sgt, mtk_gem->pages, npages);
-
- mtk_gem->kvaddr = vmap(mtk_gem->pages, npages, VM_MAP,
- pgprot_writecombine(PAGE_KERNEL));
- if (!mtk_gem->kvaddr) {
- sg_free_table(sgt);
- kfree(sgt);
- kfree(mtk_gem->pages);
- return -ENOMEM;
- }
- sg_free_table(sgt);
- kfree(sgt);
-
-out:
- iosys_map_set_vaddr(map, mtk_gem->kvaddr);
-
- return 0;
-}
-
-void mtk_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
-{
- struct mtk_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
- void *vaddr = map->vaddr;
+ dma_obj = mtk_gem_init(dev, attach->dmabuf->size, true);
+ if (IS_ERR(dma_obj))
+ return ERR_CAST(dma_obj);
- if (!mtk_gem->pages)
- return;
+ dma_obj->dma_addr = sg_dma_address(sgt->sgl);
+ dma_obj->sgt = sgt;
- vunmap(vaddr);
- mtk_gem->kvaddr = NULL;
- kfree(mtk_gem->pages);
+ return &dma_obj->base;
}
diff --git a/drivers/gpu/drm/mediatek/mtk_gem.h b/drivers/gpu/drm/mediatek/mtk_gem.h
index 66e5f154f698..afebc3a970a8 100644
--- a/drivers/gpu/drm/mediatek/mtk_gem.h
+++ b/drivers/gpu/drm/mediatek/mtk_gem.h
@@ -7,42 +7,11 @@
#define _MTK_GEM_H_
#include <drm/drm_gem.h>
+#include <drm/drm_gem_dma_helper.h>
-/*
- * mtk drm buffer structure.
- *
- * @base: a gem object.
- * - a new handle to this gem object would be created
- * by drm_gem_handle_create().
- * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs()
- * @kvaddr: kernel virtual address of gem buffer.
- * @dma_addr: dma address of gem buffer.
- * @dma_attrs: dma attributes of gem buffer.
- *
- * P.S. this object would be transferred to user as kms_bo.handle so
- * user can access the buffer through kms_bo.handle.
- */
-struct mtk_gem_obj {
- struct drm_gem_object base;
- void *cookie;
- void *kvaddr;
- dma_addr_t dma_addr;
- unsigned long dma_attrs;
- struct sg_table *sg;
- struct page **pages;
-};
-
-#define to_mtk_gem_obj(x) container_of(x, struct mtk_gem_obj, base)
-
-void mtk_gem_free_object(struct drm_gem_object *gem);
-struct mtk_gem_obj *mtk_gem_create(struct drm_device *dev, size_t size,
- bool alloc_kmap);
int mtk_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
struct drm_mode_create_dumb *args);
-struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj);
struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct sg_table *sg);
-int mtk_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map);
-void mtk_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
#endif
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
index e78eb0876f16..bd7f8c56ec9c 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
@@ -303,7 +303,7 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device
return dev_err_probe(dev, ret, "Failed to get clocks\n");
hdmi->irq = platform_get_irq(pdev, 0);
- if (!hdmi->irq)
+ if (hdmi->irq < 0)
return hdmi->irq;
hdmi->regs = device_node_to_regmap(dev->of_node);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
index de5e064585f8..7a644bbf5843 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
@@ -168,7 +168,7 @@ struct mtk_hdmi {
bool audio_enable;
bool powered;
bool enabled;
- unsigned int irq;
+ int irq;
enum hdmi_hpd_state hpd;
hdmi_codec_plugged_cb plugged_cb;
struct device *codec_dev;
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
index b844e2c10f28..d937219fdb7e 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
@@ -66,11 +66,19 @@ static int mtk_ddc_check_and_rise_low_bus(struct mtk_hdmi_ddc *ddc)
return 0;
}
-static int mtk_ddc_wr_one(struct mtk_hdmi_ddc *ddc, u16 addr_id,
- u16 offset_id, u8 *wr_data)
+static int mtk_ddcm_write_hdmi(struct mtk_hdmi_ddc *ddc, u16 addr_id,
+ u16 offset_id, u16 data_cnt, u8 *wr_data)
{
u32 val;
- int ret;
+ int ret, i;
+
+ /* Don't allow transfer with a size over than the transfer fifo size
+ * (16 byte)
+ */
+ if (data_cnt > 16) {
+ dev_err(ddc->dev, "Invalid DDCM write request\n");
+ return -EINVAL;
+ }
/* If down, rise bus for write operation */
mtk_ddc_check_and_rise_low_bus(ddc);
@@ -78,16 +86,21 @@ static int mtk_ddc_wr_one(struct mtk_hdmi_ddc *ddc, u16 addr_id,
regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
FIELD_PREP(HPD_DDC_DELAY_CNT, DDC2_DLY_CNT));
+ /* In case there is no payload data, just do a single write for the
+ * address only
+ */
if (wr_data) {
- regmap_write(ddc->regs, SI2C_CTRL,
- FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
- FIELD_PREP(SI2C_WDATA, *wr_data) |
- SI2C_WR);
+ /* Fill transfer fifo with payload data */
+ for (i = 0; i < data_cnt; i++) {
+ regmap_write(ddc->regs, SI2C_CTRL,
+ FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
+ FIELD_PREP(SI2C_WDATA, wr_data[i]) |
+ SI2C_WR);
+ }
}
-
regmap_write(ddc->regs, DDC_CTRL,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_WRITE) |
- FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : 1) |
+ FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : data_cnt) |
FIELD_PREP(DDC_CTRL_OFFSET, offset_id) |
FIELD_PREP(DDC_CTRL_ADDR, addr_id));
usleep_range(1000, 1250);
@@ -96,6 +109,11 @@ static int mtk_ddc_wr_one(struct mtk_hdmi_ddc *ddc, u16 addr_id,
!(val & DDC_I2C_IN_PROG), 500, 1000);
if (ret) {
dev_err(ddc->dev, "DDC I2C write timeout\n");
+
+ /* Abort transfer if it is still in progress */
+ regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ABORT_XFER));
+
return ret;
}
@@ -179,6 +197,11 @@ static int mtk_ddcm_read_hdmi(struct mtk_hdmi_ddc *ddc, u16 uc_dev,
500 * (temp_length + 5));
if (ret) {
dev_err(ddc->dev, "Timeout waiting for DDC I2C\n");
+
+ /* Abort transfer if it is still in progress */
+ regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ABORT_XFER));
+
return ret;
}
@@ -250,24 +273,9 @@ static int mtk_hdmi_fg_ddc_data_read(struct mtk_hdmi_ddc *ddc, u16 b_dev,
static int mtk_hdmi_ddc_fg_data_write(struct mtk_hdmi_ddc *ddc, u16 b_dev,
u8 data_addr, u16 data_cnt, u8 *pr_data)
{
- int i, ret;
-
regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
- /*
- * In case there is no payload data, just do a single write for the
- * address only
- */
- if (data_cnt == 0)
- return mtk_ddc_wr_one(ddc, b_dev, data_addr, NULL);
- i = 0;
- do {
- ret = mtk_ddc_wr_one(ddc, b_dev, data_addr + i, pr_data + i);
- if (ret)
- return ret;
- } while (++i < data_cnt);
-
- return 0;
+ return mtk_ddcm_write_hdmi(ddc, b_dev, data_addr, data_cnt, pr_data);
}
static int mtk_hdmi_ddc_v2_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
index c272e1e74b7d..454b8b93b834 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
@@ -1120,9 +1120,10 @@ static void mtk_hdmi_v2_hpd_disable(struct drm_bridge *bridge)
mtk_hdmi_v2_disable(hdmi);
}
-static int mtk_hdmi_v2_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- unsigned long long tmds_rate)
+static enum drm_mode_status
+mtk_hdmi_v2_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
{
if (mode->clock < MTK_HDMI_V2_CLOCK_MIN)
return MODE_CLOCK_LOW;
diff --git a/drivers/gpu/drm/mediatek/mtk_plane.c b/drivers/gpu/drm/mediatek/mtk_plane.c
index 5043e0377270..fcd10d7e8342 100644
--- a/drivers/gpu/drm/mediatek/mtk_plane.c
+++ b/drivers/gpu/drm/mediatek/mtk_plane.c
@@ -11,13 +11,13 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_print.h>
#include <linux/align.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_drm_drv.h"
-#include "mtk_gem.h"
#include "mtk_plane.h"
static const u64 modifiers[] = {
@@ -114,8 +114,8 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
struct mtk_plane_state *mtk_plane_state)
{
struct drm_framebuffer *fb = new_state->fb;
+ struct drm_gem_dma_object *dma_obj;
struct drm_gem_object *gem;
- struct mtk_gem_obj *mtk_gem;
unsigned int pitch, format;
u64 modifier;
dma_addr_t addr;
@@ -124,8 +124,8 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
int offset;
gem = fb->obj[0];
- mtk_gem = to_mtk_gem_obj(gem);
- addr = mtk_gem->dma_addr;
+ dma_obj = to_drm_gem_dma_obj(gem);
+ addr = dma_obj->dma_addr;
pitch = fb->pitches[0];
format = fb->format->format;
modifier = fb->modifier;
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index ac9a95aab2fb..4c042133261c 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -501,8 +501,6 @@ static const struct adreno_reglist a690_hwcg[] = {
{REG_A6XX_RBBM_CLOCK_CNTL_GMU_GX, 0x00000222},
{REG_A6XX_RBBM_CLOCK_DELAY_GMU_GX, 0x00000111},
{REG_A6XX_RBBM_CLOCK_HYST_GMU_GX, 0x00000555},
- {REG_A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL, 0x10111},
- {REG_A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL, 0x5555},
{}
};
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h
index d1beaad0c82b..834ed6587aa5 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/conn.h
@@ -1,28 +1,81 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVBIOS_CONN_H__
#define __NVBIOS_CONN_H__
+
+/*
+ * An enumerator representing all of the possible VBIOS connector types defined
+ * by Nvidia at
+ * https://nvidia.github.io/open-gpu-doc/DCB/DCB-4.x-Specification.html.
+ *
+ * [1] Nvidia's documentation actually claims DCB_CONNECTOR_HDMI_0 is a "3-Pin
+ * DIN Stereo Connector". This seems very likely to be a documentation typo
+ * or some sort of funny historical baggage, because we've treated this
+ * connector type as HDMI for years without issue.
+ * TODO: Check with Nvidia what's actually happening here.
+ */
enum dcb_connector_type {
- DCB_CONNECTOR_VGA = 0x00,
- DCB_CONNECTOR_TV_0 = 0x10,
- DCB_CONNECTOR_TV_1 = 0x11,
- DCB_CONNECTOR_TV_3 = 0x13,
- DCB_CONNECTOR_DVI_I = 0x30,
- DCB_CONNECTOR_DVI_D = 0x31,
- DCB_CONNECTOR_DMS59_0 = 0x38,
- DCB_CONNECTOR_DMS59_1 = 0x39,
- DCB_CONNECTOR_LVDS = 0x40,
- DCB_CONNECTOR_LVDS_SPWG = 0x41,
- DCB_CONNECTOR_DP = 0x46,
- DCB_CONNECTOR_eDP = 0x47,
- DCB_CONNECTOR_mDP = 0x48,
- DCB_CONNECTOR_HDMI_0 = 0x60,
- DCB_CONNECTOR_HDMI_1 = 0x61,
- DCB_CONNECTOR_HDMI_C = 0x63,
- DCB_CONNECTOR_DMS59_DP0 = 0x64,
- DCB_CONNECTOR_DMS59_DP1 = 0x65,
- DCB_CONNECTOR_WFD = 0x70,
- DCB_CONNECTOR_USB_C = 0x71,
- DCB_CONNECTOR_NONE = 0xff
+ /* Analog outputs */
+ DCB_CONNECTOR_VGA = 0x00, // VGA 15-pin connector
+ DCB_CONNECTOR_DVI_A = 0x01, // DVI-A
+ DCB_CONNECTOR_POD_VGA = 0x02, // Pod - VGA 15-pin connector
+ DCB_CONNECTOR_TV_0 = 0x10, // TV - Composite Out
+ DCB_CONNECTOR_TV_1 = 0x11, // TV - S-Video Out
+ DCB_CONNECTOR_TV_2 = 0x12, // TV - S-Video Breakout - Composite
+ DCB_CONNECTOR_TV_3 = 0x13, // HDTV Component - YPrPb
+ DCB_CONNECTOR_TV_SCART = 0x14, // TV - SCART Connector
+ DCB_CONNECTOR_TV_SCART_D = 0x16, // TV - Composite SCART over D-connector
+ DCB_CONNECTOR_TV_DTERM = 0x17, // HDTV - D-connector (EIAJ4120)
+ DCB_CONNECTOR_POD_TV_3 = 0x18, // Pod - HDTV - YPrPb
+ DCB_CONNECTOR_POD_TV_1 = 0x19, // Pod - S-Video
+ DCB_CONNECTOR_POD_TV_0 = 0x1a, // Pod - Composite
+
+ /* DVI digital outputs */
+ DCB_CONNECTOR_DVI_I_TV_1 = 0x20, // DVI-I-TV-S-Video
+ DCB_CONNECTOR_DVI_I_TV_0 = 0x21, // DVI-I-TV-Composite
+ DCB_CONNECTOR_DVI_I_TV_2 = 0x22, // DVI-I-TV-S-Video Breakout-Composite
+ DCB_CONNECTOR_DVI_I = 0x30, // DVI-I
+ DCB_CONNECTOR_DVI_D = 0x31, // DVI-D
+ DCB_CONNECTOR_DVI_ADC = 0x32, // Apple Display Connector (ADC)
+ DCB_CONNECTOR_DMS59_0 = 0x38, // LFH-DVI-I-1
+ DCB_CONNECTOR_DMS59_1 = 0x39, // LFH-DVI-I-2
+ DCB_CONNECTOR_BNC = 0x3c, // BNC Connector [for SDI?]
+
+ /* LVDS / TMDS digital outputs */
+ DCB_CONNECTOR_LVDS = 0x40, // LVDS-SPWG-Attached [is this name correct?]
+ DCB_CONNECTOR_LVDS_SPWG = 0x41, // LVDS-OEM-Attached (non-removable)
+ DCB_CONNECTOR_LVDS_REM = 0x42, // LVDS-SPWG-Detached [following naming above]
+ DCB_CONNECTOR_LVDS_SPWG_REM = 0x43, // LVDS-OEM-Detached (removable)
+ DCB_CONNECTOR_TMDS = 0x45, // TMDS-OEM-Attached (non-removable)
+
+ /* DP digital outputs */
+ DCB_CONNECTOR_DP = 0x46, // DisplayPort External Connector
+ DCB_CONNECTOR_eDP = 0x47, // DisplayPort Internal Connector
+ DCB_CONNECTOR_mDP = 0x48, // DisplayPort (Mini) External Connector
+
+ /* Dock outputs (not used) */
+ DCB_CONNECTOR_DOCK_VGA_0 = 0x50, // VGA 15-pin if not docked
+ DCB_CONNECTOR_DOCK_VGA_1 = 0x51, // VGA 15-pin if docked
+ DCB_CONNECTOR_DOCK_DVI_I_0 = 0x52, // DVI-I if not docked
+ DCB_CONNECTOR_DOCK_DVI_I_1 = 0x53, // DVI-I if docked
+ DCB_CONNECTOR_DOCK_DVI_D_0 = 0x54, // DVI-D if not docked
+ DCB_CONNECTOR_DOCK_DVI_D_1 = 0x55, // DVI-D if docked
+ DCB_CONNECTOR_DOCK_DP_0 = 0x56, // DisplayPort if not docked
+ DCB_CONNECTOR_DOCK_DP_1 = 0x57, // DisplayPort if docked
+ DCB_CONNECTOR_DOCK_mDP_0 = 0x58, // DisplayPort (Mini) if not docked
+ DCB_CONNECTOR_DOCK_mDP_1 = 0x59, // DisplayPort (Mini) if docked
+
+ /* HDMI? digital outputs */
+ DCB_CONNECTOR_HDMI_0 = 0x60, // HDMI? See [1] in top-level enum comment above
+ DCB_CONNECTOR_HDMI_1 = 0x61, // HDMI-A connector
+ DCB_CONNECTOR_SPDIF = 0x62, // Audio S/PDIF connector
+ DCB_CONNECTOR_HDMI_C = 0x63, // HDMI-C (Mini) connector
+
+ /* Misc. digital outputs */
+ DCB_CONNECTOR_DMS59_DP0 = 0x64, // LFH-DP-1
+ DCB_CONNECTOR_DMS59_DP1 = 0x65, // LFH-DP-2
+ DCB_CONNECTOR_WFD = 0x70, // Virtual connector for Wifi Display (WFD)
+ DCB_CONNECTOR_USB_C = 0x71, // [DP over USB-C; not present in docs]
+ DCB_CONNECTOR_NONE = 0xff // Skip Entry
};
struct nvbios_connT {
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 00515623a2cc..829c2b573971 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -352,6 +352,8 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
.fb_create = nouveau_user_framebuffer_create,
+ .atomic_commit = drm_atomic_helper_commit,
+ .atomic_check = drm_atomic_helper_check,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
index 58071652679d..3d8031296eed 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
@@ -425,7 +425,7 @@ nouveau_dmem_page_alloc_locked(struct nouveau_drm *drm, bool is_large)
order = ilog2(DMEM_CHUNK_NPAGES);
}
- zone_device_folio_init(folio, order);
+ zone_device_folio_init(folio, page_pgmap(folio_page(folio, 0)), order);
return page;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
index 2dab6612c4fc..23d1e5c27bb1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
@@ -191,27 +191,60 @@ nvkm_uconn_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv
spin_lock(&disp->client.lock);
if (!conn->object.func) {
switch (conn->info.type) {
- case DCB_CONNECTOR_VGA : args->v0.type = NVIF_CONN_V0_VGA; break;
- case DCB_CONNECTOR_TV_0 :
- case DCB_CONNECTOR_TV_1 :
- case DCB_CONNECTOR_TV_3 : args->v0.type = NVIF_CONN_V0_TV; break;
- case DCB_CONNECTOR_DMS59_0 :
- case DCB_CONNECTOR_DMS59_1 :
- case DCB_CONNECTOR_DVI_I : args->v0.type = NVIF_CONN_V0_DVI_I; break;
- case DCB_CONNECTOR_DVI_D : args->v0.type = NVIF_CONN_V0_DVI_D; break;
- case DCB_CONNECTOR_LVDS : args->v0.type = NVIF_CONN_V0_LVDS; break;
- case DCB_CONNECTOR_LVDS_SPWG: args->v0.type = NVIF_CONN_V0_LVDS_SPWG; break;
- case DCB_CONNECTOR_DMS59_DP0:
- case DCB_CONNECTOR_DMS59_DP1:
- case DCB_CONNECTOR_DP :
- case DCB_CONNECTOR_mDP :
- case DCB_CONNECTOR_USB_C : args->v0.type = NVIF_CONN_V0_DP; break;
- case DCB_CONNECTOR_eDP : args->v0.type = NVIF_CONN_V0_EDP; break;
- case DCB_CONNECTOR_HDMI_0 :
- case DCB_CONNECTOR_HDMI_1 :
- case DCB_CONNECTOR_HDMI_C : args->v0.type = NVIF_CONN_V0_HDMI; break;
+ /* VGA */
+ case DCB_CONNECTOR_DVI_A :
+ case DCB_CONNECTOR_POD_VGA :
+ case DCB_CONNECTOR_VGA : args->v0.type = NVIF_CONN_V0_VGA; break;
+
+ /* TV */
+ case DCB_CONNECTOR_TV_0 :
+ case DCB_CONNECTOR_TV_1 :
+ case DCB_CONNECTOR_TV_2 :
+ case DCB_CONNECTOR_TV_SCART :
+ case DCB_CONNECTOR_TV_SCART_D :
+ case DCB_CONNECTOR_TV_DTERM :
+ case DCB_CONNECTOR_POD_TV_3 :
+ case DCB_CONNECTOR_POD_TV_1 :
+ case DCB_CONNECTOR_POD_TV_0 :
+ case DCB_CONNECTOR_TV_3 : args->v0.type = NVIF_CONN_V0_TV; break;
+
+ /* DVI */
+ case DCB_CONNECTOR_DVI_I_TV_1 :
+ case DCB_CONNECTOR_DVI_I_TV_0 :
+ case DCB_CONNECTOR_DVI_I_TV_2 :
+ case DCB_CONNECTOR_DVI_ADC :
+ case DCB_CONNECTOR_DMS59_0 :
+ case DCB_CONNECTOR_DMS59_1 :
+ case DCB_CONNECTOR_DVI_I : args->v0.type = NVIF_CONN_V0_DVI_I; break;
+ case DCB_CONNECTOR_TMDS :
+ case DCB_CONNECTOR_DVI_D : args->v0.type = NVIF_CONN_V0_DVI_D; break;
+
+ /* LVDS */
+ case DCB_CONNECTOR_LVDS : args->v0.type = NVIF_CONN_V0_LVDS; break;
+ case DCB_CONNECTOR_LVDS_SPWG : args->v0.type = NVIF_CONN_V0_LVDS_SPWG; break;
+
+ /* DP */
+ case DCB_CONNECTOR_DMS59_DP0 :
+ case DCB_CONNECTOR_DMS59_DP1 :
+ case DCB_CONNECTOR_DP :
+ case DCB_CONNECTOR_mDP :
+ case DCB_CONNECTOR_USB_C : args->v0.type = NVIF_CONN_V0_DP; break;
+ case DCB_CONNECTOR_eDP : args->v0.type = NVIF_CONN_V0_EDP; break;
+
+ /* HDMI */
+ case DCB_CONNECTOR_HDMI_0 :
+ case DCB_CONNECTOR_HDMI_1 :
+ case DCB_CONNECTOR_HDMI_C : args->v0.type = NVIF_CONN_V0_HDMI; break;
+
+ /*
+ * Dock & unused outputs.
+ * BNC, SPDIF, WFD, and detached LVDS go here.
+ */
default:
- WARN_ON(1);
+ nvkm_warn(&disp->engine.subdev,
+ "unimplemented connector type 0x%02x\n",
+ conn->info.type);
+ args->v0.type = NVIF_CONN_V0_VGA;
ret = -EINVAL;
break;
}
diff --git a/drivers/gpu/drm/tyr/Kconfig b/drivers/gpu/drm/tyr/Kconfig
index 4b55308fd2eb..e933e6478027 100644
--- a/drivers/gpu/drm/tyr/Kconfig
+++ b/drivers/gpu/drm/tyr/Kconfig
@@ -6,6 +6,7 @@ config DRM_TYR
depends on RUST
depends on ARM || ARM64 || COMPILE_TEST
depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_LPAE
+ depends on COMMON_CLK
default n
help
Rust DRM driver for ARM Mali CSF-based GPUs.
diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c
index 5c3ffc78aea0..d03a1f2e9c41 100644
--- a/drivers/gpu/drm/vkms/vkms_colorop.c
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -37,7 +37,6 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr
goto cleanup;
list->type = ops[i]->base.id;
- list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id);
i++;
@@ -88,6 +87,8 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr
drm_colorop_set_next_property(ops[i - 1], ops[i]);
+ list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[0]->base.id);
+
return 0;
cleanup:
@@ -103,18 +104,18 @@ cleanup:
int vkms_initialize_colorops(struct drm_plane *plane)
{
- struct drm_prop_enum_list pipeline;
- int ret;
+ struct drm_prop_enum_list pipeline = {};
+ int ret = 0;
/* Add color pipeline */
ret = vkms_initialize_color_pipeline(plane, &pipeline);
if (ret)
- return ret;
+ goto out;
/* Create COLOR_PIPELINE property and attach */
ret = drm_plane_create_color_pipeline_property(plane, &pipeline, 1);
- if (ret)
- return ret;
- return 0;
+ kfree(pipeline.name);
+out:
+ return ret;
}
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index 4b288eb3f5b0..4d7dcaff2b91 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -39,7 +39,7 @@ config DRM_XE
select DRM_TTM
select DRM_TTM_HELPER
select DRM_EXEC
- select DRM_GPUSVM if !UML && DEVICE_PRIVATE
+ select DRM_GPUSVM if !UML
select DRM_GPUVM
select DRM_SCHED
select MMU_NOTIFIER
@@ -80,8 +80,9 @@ config DRM_XE_GPUSVM
bool "Enable CPU to GPU address mirroring"
depends on DRM_XE
depends on !UML
- depends on DEVICE_PRIVATE
+ depends on ZONE_DEVICE
default y
+ select DEVICE_PRIVATE
select DRM_GPUSVM
help
Enable this option if you want support for CPU to GPU address
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index bf4ee976b680..71acd45aa33b 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1055,6 +1055,7 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
unsigned long *scanned)
{
struct xe_device *xe = ttm_to_xe_device(bo->bdev);
+ struct ttm_tt *tt = bo->ttm;
long lret;
/* Fake move to system, without copying data. */
@@ -1079,8 +1080,10 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
.writeback = false,
.allow_move = false});
- if (lret > 0)
+ if (lret > 0) {
xe_ttm_tt_account_subtract(xe, bo->ttm);
+ update_global_total_pages(bo->bdev, -(long)tt->num_pages);
+ }
return lret;
}
@@ -1166,8 +1169,10 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
if (needs_rpm)
xe_pm_runtime_put(xe);
- if (lret > 0)
+ if (lret > 0) {
xe_ttm_tt_account_subtract(xe, tt);
+ update_global_total_pages(bo->bdev, -(long)tt->num_pages);
+ }
out_unref:
xe_bo_put(xe_bo);
diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
index 9f6251b1008b..82edd0466005 100644
--- a/drivers/gpu/drm/xe/xe_configfs.c
+++ b/drivers/gpu/drm/xe/xe_configfs.c
@@ -347,11 +347,10 @@ static bool is_bound(struct xe_config_group_device *dev)
return false;
ret = pci_get_drvdata(pdev);
- pci_dev_put(pdev);
-
if (ret)
pci_dbg(pdev, "Already bound to driver\n");
+ pci_dev_put(pdev);
return ret;
}
diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c
index e91da9589c5f..63fd8bf13c70 100644
--- a/drivers/gpu/drm/xe/xe_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_debugfs.c
@@ -256,14 +256,64 @@ static ssize_t wedged_mode_show(struct file *f, char __user *ubuf,
return simple_read_from_buffer(ubuf, size, pos, buf, len);
}
+static int __wedged_mode_set_reset_policy(struct xe_gt *gt, enum xe_wedged_mode mode)
+{
+ bool enable_engine_reset;
+ int ret;
+
+ enable_engine_reset = (mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET);
+ ret = xe_guc_ads_scheduler_policy_toggle_reset(&gt->uc.guc.ads,
+ enable_engine_reset);
+ if (ret)
+ xe_gt_err(gt, "Failed to update GuC ADS scheduler policy (%pe)\n", ERR_PTR(ret));
+
+ return ret;
+}
+
+static int wedged_mode_set_reset_policy(struct xe_device *xe, enum xe_wedged_mode mode)
+{
+ struct xe_gt *gt;
+ int ret;
+ u8 id;
+
+ guard(xe_pm_runtime)(xe);
+ for_each_gt(gt, xe, id) {
+ ret = __wedged_mode_set_reset_policy(gt, mode);
+ if (ret) {
+ if (id > 0) {
+ xe->wedged.inconsistent_reset = true;
+ drm_err(&xe->drm, "Inconsistent reset policy state between GTs\n");
+ }
+ return ret;
+ }
+ }
+
+ xe->wedged.inconsistent_reset = false;
+
+ return 0;
+}
+
+static bool wedged_mode_needs_policy_update(struct xe_device *xe, enum xe_wedged_mode mode)
+{
+ if (xe->wedged.inconsistent_reset)
+ return true;
+
+ if (xe->wedged.mode == mode)
+ return false;
+
+ if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET ||
+ mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
+ return true;
+
+ return false;
+}
+
static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
size_t size, loff_t *pos)
{
struct xe_device *xe = file_inode(f)->i_private;
- struct xe_gt *gt;
u32 wedged_mode;
ssize_t ret;
- u8 id;
ret = kstrtouint_from_user(ubuf, size, 0, &wedged_mode);
if (ret)
@@ -272,22 +322,14 @@ static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
if (wedged_mode > 2)
return -EINVAL;
- if (xe->wedged.mode == wedged_mode)
- return size;
+ if (wedged_mode_needs_policy_update(xe, wedged_mode)) {
+ ret = wedged_mode_set_reset_policy(xe, wedged_mode);
+ if (ret)
+ return ret;
+ }
xe->wedged.mode = wedged_mode;
- xe_pm_runtime_get(xe);
- for_each_gt(gt, xe, id) {
- ret = xe_guc_ads_scheduler_policy_toggle_reset(&gt->uc.guc.ads);
- if (ret) {
- xe_gt_err(gt, "Failed to update GuC ADS scheduler policy. GuC may still cause engine reset even with wedged_mode=2\n");
- xe_pm_runtime_put(xe);
- return -EIO;
- }
- }
- xe_pm_runtime_put(xe);
-
return size;
}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index cf29e259861f..9a6d49fcd8e4 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -984,8 +984,6 @@ void xe_device_remove(struct xe_device *xe)
{
xe_display_unregister(xe);
- xe_nvm_fini(xe);
-
drm_dev_unplug(&xe->drm);
xe_bo_pci_dev_remove_all(xe);
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 0b2fa7c56d38..047e86e22133 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -44,6 +44,22 @@ struct xe_pat_ops;
struct xe_pxp;
struct xe_vram_region;
+/**
+ * enum xe_wedged_mode - possible wedged modes
+ * @XE_WEDGED_MODE_NEVER: Device will never be declared wedged.
+ * @XE_WEDGED_MODE_UPON_CRITICAL_ERROR: Device will be declared wedged only
+ * when critical error occurs like GT reset failure or firmware failure.
+ * This is the default mode.
+ * @XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET: Device will be declared wedged on
+ * any hang. In this mode, engine resets are disabled to avoid automatic
+ * recovery attempts. This mode is primarily intended for debugging hangs.
+ */
+enum xe_wedged_mode {
+ XE_WEDGED_MODE_NEVER = 0,
+ XE_WEDGED_MODE_UPON_CRITICAL_ERROR = 1,
+ XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET = 2,
+};
+
#define XE_BO_INVALID_OFFSET LONG_MAX
#define GRAPHICS_VER(xe) ((xe)->info.graphics_verx100 / 100)
@@ -587,6 +603,8 @@ struct xe_device {
int mode;
/** @wedged.method: Recovery method to be sent in the drm device wedged uevent */
unsigned long method;
+ /** @wedged.inconsistent_reset: Inconsistent reset policy state between GTs */
+ bool inconsistent_reset;
} wedged;
/** @bo_device: Struct to control async free of BOs */
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index fd9480031750..8e3614b24010 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -190,9 +190,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
goto err_syncs;
}
- if (xe_exec_queue_is_parallel(q)) {
- err = copy_from_user(addresses, addresses_user, sizeof(u64) *
- q->width);
+ if (args->num_batch_buffer && xe_exec_queue_is_parallel(q)) {
+ err = copy_from_user(addresses, addresses_user,
+ sizeof(u64) * q->width);
if (err) {
err = -EFAULT;
goto err_syncs;
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 8724f8de67e2..779d7e7e2d2e 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -328,6 +328,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
* @xe: Xe device.
* @tile: tile which bind exec queue belongs to.
* @flags: exec queue creation flags
+ * @user_vm: The user VM which this exec queue belongs to
* @extensions: exec queue creation extensions
*
* Normalize bind exec queue creation. Bind exec queue is tied to migration VM
@@ -341,6 +342,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
*/
struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
struct xe_tile *tile,
+ struct xe_vm *user_vm,
u32 flags, u64 extensions)
{
struct xe_gt *gt = tile->primary_gt;
@@ -377,6 +379,9 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
xe_exec_queue_put(q);
return ERR_PTR(err);
}
+
+ if (user_vm)
+ q->user_vm = xe_vm_get(user_vm);
}
return q;
@@ -407,6 +412,11 @@ void xe_exec_queue_destroy(struct kref *ref)
xe_exec_queue_put(eq);
}
+ if (q->user_vm) {
+ xe_vm_put(q->user_vm);
+ q->user_vm = NULL;
+ }
+
q->ops->destroy(q);
}
@@ -742,6 +752,22 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
return -EINVAL;
+ vm = xe_vm_lookup(xef, args->vm_id);
+ if (XE_IOCTL_DBG(xe, !vm))
+ return -ENOENT;
+
+ err = down_read_interruptible(&vm->lock);
+ if (err) {
+ xe_vm_put(vm);
+ return err;
+ }
+
+ if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) {
+ up_read(&vm->lock);
+ xe_vm_put(vm);
+ return -ENOENT;
+ }
+
for_each_tile(tile, xe, id) {
struct xe_exec_queue *new;
@@ -749,9 +775,11 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (id)
flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
- new = xe_exec_queue_create_bind(xe, tile, flags,
+ new = xe_exec_queue_create_bind(xe, tile, vm, flags,
args->extensions);
if (IS_ERR(new)) {
+ up_read(&vm->lock);
+ xe_vm_put(vm);
err = PTR_ERR(new);
if (q)
goto put_exec_queue;
@@ -763,6 +791,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
list_add_tail(&new->multi_gt_list,
&q->multi_gt_link);
}
+ up_read(&vm->lock);
+ xe_vm_put(vm);
} else {
logical_mask = calc_validate_logical_mask(xe, eci,
args->width,
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index fda4d4f9bda8..37a9da22f420 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -28,6 +28,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
u32 flags, u64 extensions);
struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
struct xe_tile *tile,
+ struct xe_vm *user_vm,
u32 flags, u64 extensions);
void xe_exec_queue_fini(struct xe_exec_queue *q);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 771ffe35cd0c..3a4263c92b3d 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -54,6 +54,12 @@ struct xe_exec_queue {
struct kref refcount;
/** @vm: VM (address space) for this exec queue */
struct xe_vm *vm;
+ /**
+ * @user_vm: User VM (address space) for this exec queue (bind queues
+ * only)
+ */
+ struct xe_vm *user_vm;
+
/** @class: class of this exec queue */
enum xe_engine_class class;
/**
diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c
index ef481b334af4..793d7324a395 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@ -322,7 +322,7 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
else
ggtt->pt_ops = &xelp_pt_ops;
- ggtt->wq = alloc_workqueue("xe-ggtt-wq", 0, WQ_MEM_RECLAIM);
+ ggtt->wq = alloc_workqueue("xe-ggtt-wq", WQ_MEM_RECLAIM, 0);
if (!ggtt->wq)
return -ENOMEM;
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
index 420b0e6089de..e8897a77ba19 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
@@ -41,10 +41,10 @@ struct xe_gt_sriov_vf_runtime {
};
/**
- * xe_gt_sriov_vf_migration - VF migration data.
+ * struct xe_gt_sriov_vf_migration - VF migration data.
*/
struct xe_gt_sriov_vf_migration {
- /** @migration: VF migration recovery worker */
+ /** @worker: VF migration recovery worker */
struct work_struct worker;
/** @lock: Protects recovery_queued, teardown */
spinlock_t lock;
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index bcb85a1bf26d..3f7f1b5602d5 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -983,16 +983,17 @@ static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_off
/**
* xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy
* @ads: Additional data structures object
+ * @enable_engine_reset: true to enable engine resets, false otherwise
*
- * This function update the GuC's engine reset policy based on wedged.mode.
+ * This function update the GuC's engine reset policy.
*
* Return: 0 on success, and negative error code otherwise.
*/
-int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
+int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads,
+ bool enable_engine_reset)
{
struct guc_policies *policies;
struct xe_guc *guc = ads_to_guc(ads);
- struct xe_device *xe = ads_to_xe(ads);
CLASS(xe_guc_buf, buf)(&guc->buf, sizeof(*policies));
if (!xe_guc_buf_is_valid(buf))
@@ -1004,10 +1005,11 @@ int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
policies->dpc_promote_time = ads_blob_read(ads, policies.dpc_promote_time);
policies->max_num_work_items = ads_blob_read(ads, policies.max_num_work_items);
policies->is_valid = 1;
- if (xe->wedged.mode == 2)
- policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
- else
+
+ if (enable_engine_reset)
policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET;
+ else
+ policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
}
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.h b/drivers/gpu/drm/xe/xe_guc_ads.h
index 2e6674c760ff..7a39f361cb17 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.h
+++ b/drivers/gpu/drm/xe/xe_guc_ads.h
@@ -6,6 +6,8 @@
#ifndef _XE_GUC_ADS_H_
#define _XE_GUC_ADS_H_
+#include <linux/types.h>
+
struct xe_guc_ads;
int xe_guc_ads_init(struct xe_guc_ads *ads);
@@ -13,6 +15,7 @@ int xe_guc_ads_init_post_hwconfig(struct xe_guc_ads *ads);
void xe_guc_ads_populate(struct xe_guc_ads *ads);
void xe_guc_ads_populate_minimal(struct xe_guc_ads *ads);
void xe_guc_ads_populate_post_load(struct xe_guc_ads *ads);
-int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads);
+int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads,
+ bool enable_engine_reset);
#endif
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
index 0f5da89ce98b..2a8a985c37e7 100644
--- a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
@@ -15,10 +15,12 @@
#define XE_LB_MAX_PAYLOAD_SIZE SZ_4K
/**
- * xe_late_bind_fw_id - enum to determine late binding fw index
+ * enum xe_late_bind_fw_id - enum to determine late binding fw index
*/
enum xe_late_bind_fw_id {
+ /** @XE_LB_FW_FAN_CONTROL: Fan control */
XE_LB_FW_FAN_CONTROL = 0,
+ /** @XE_LB_FW_MAX_ID: Number of IDs */
XE_LB_FW_MAX_ID
};
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index b5083c99dd50..b8c1dd953665 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -1050,6 +1050,9 @@ static ssize_t setup_utilization_wa(struct xe_lrc *lrc,
{
u32 *cmd = batch;
+ if (IS_SRIOV_VF(gt_to_xe(lrc->gt)))
+ return 0;
+
if (xe_gt_WARN_ON(lrc->gt, max_len < 12))
return -ENOSPC;
@@ -1182,7 +1185,7 @@ static ssize_t setup_invalidate_state_cache_wa(struct xe_lrc *lrc,
return -ENOSPC;
*cmd++ = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1);
- *cmd++ = CS_DEBUG_MODE1(0).addr;
+ *cmd++ = CS_DEBUG_MODE2(0).addr;
*cmd++ = _MASKED_BIT_ENABLE(INSTRUCTION_STATE_CACHE_INVALIDATE);
return cmd - batch;
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index 5a95b08a4723..d8ee76aab4e4 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -2445,7 +2445,7 @@ void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q)
if (is_migrate)
mutex_lock(&m->job_mutex);
else
- xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
+ xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
}
/**
@@ -2463,7 +2463,7 @@ void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q)
if (is_migrate)
mutex_unlock(&m->job_mutex);
else
- xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
+ xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
}
#if IS_ENABLED(CONFIG_PROVE_LOCKING)
diff --git a/drivers/gpu/drm/xe/xe_nvm.c b/drivers/gpu/drm/xe/xe_nvm.c
index 33f4ac82fc80..6da42b2b5e46 100644
--- a/drivers/gpu/drm/xe/xe_nvm.c
+++ b/drivers/gpu/drm/xe/xe_nvm.c
@@ -83,6 +83,27 @@ static bool xe_nvm_writable_override(struct xe_device *xe)
return writable_override;
}
+static void xe_nvm_fini(void *arg)
+{
+ struct xe_device *xe = arg;
+ struct intel_dg_nvm_dev *nvm = xe->nvm;
+
+ if (!xe->info.has_gsc_nvm)
+ return;
+
+ /* No access to internal NVM from VFs */
+ if (IS_SRIOV_VF(xe))
+ return;
+
+ /* Nvm pointer should not be NULL here */
+ if (WARN_ON(!nvm))
+ return;
+
+ auxiliary_device_delete(&nvm->aux_dev);
+ auxiliary_device_uninit(&nvm->aux_dev);
+ xe->nvm = NULL;
+}
+
int xe_nvm_init(struct xe_device *xe)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
@@ -132,39 +153,17 @@ int xe_nvm_init(struct xe_device *xe)
ret = auxiliary_device_init(aux_dev);
if (ret) {
drm_err(&xe->drm, "xe-nvm aux init failed %d\n", ret);
- goto err;
+ kfree(nvm);
+ xe->nvm = NULL;
+ return ret;
}
ret = auxiliary_device_add(aux_dev);
if (ret) {
drm_err(&xe->drm, "xe-nvm aux add failed %d\n", ret);
auxiliary_device_uninit(aux_dev);
- goto err;
+ xe->nvm = NULL;
+ return ret;
}
- return 0;
-
-err:
- kfree(nvm);
- xe->nvm = NULL;
- return ret;
-}
-
-void xe_nvm_fini(struct xe_device *xe)
-{
- struct intel_dg_nvm_dev *nvm = xe->nvm;
-
- if (!xe->info.has_gsc_nvm)
- return;
-
- /* No access to internal NVM from VFs */
- if (IS_SRIOV_VF(xe))
- return;
-
- /* Nvm pointer should not be NULL here */
- if (WARN_ON(!nvm))
- return;
-
- auxiliary_device_delete(&nvm->aux_dev);
- auxiliary_device_uninit(&nvm->aux_dev);
- xe->nvm = NULL;
+ return devm_add_action_or_reset(xe->drm.dev, xe_nvm_fini, xe);
}
diff --git a/drivers/gpu/drm/xe/xe_nvm.h b/drivers/gpu/drm/xe/xe_nvm.h
index 7f3d5f57bed0..fd3467ad35a4 100644
--- a/drivers/gpu/drm/xe/xe_nvm.h
+++ b/drivers/gpu/drm/xe/xe_nvm.h
@@ -10,6 +10,4 @@ struct xe_device;
int xe_nvm_init(struct xe_device *xe);
-void xe_nvm_fini(struct xe_device *xe);
-
#endif
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 9c9ea10d994c..2aa883f5ef79 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -342,7 +342,6 @@ static const struct xe_device_desc lnl_desc = {
.has_display = true,
.has_flat_ccs = 1,
.has_pxp = true,
- .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
.va_bits = 48,
@@ -363,7 +362,6 @@ static const struct xe_device_desc bmg_desc = {
.has_heci_cscfi = 1,
.has_late_bind = true,
.has_sriov = true,
- .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
.subplatforms = (const struct xe_subplatform_desc[]) {
@@ -380,7 +378,6 @@ static const struct xe_device_desc ptl_desc = {
.has_display = true,
.has_flat_ccs = 1,
.has_sriov = true,
- .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
.needs_shared_vf_gt_wq = true,
@@ -393,7 +390,6 @@ static const struct xe_device_desc nvls_desc = {
.dma_mask_size = 46,
.has_display = true,
.has_flat_ccs = 1,
- .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.require_force_probe = true,
.va_bits = 48,
@@ -675,7 +671,6 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_pxp = desc->has_pxp;
xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) &&
desc->has_sriov;
- xe->info.has_mem_copy_instr = desc->has_mem_copy_instr;
xe->info.skip_guc_pc = desc->skip_guc_pc;
xe->info.skip_mtcfg = desc->skip_mtcfg;
xe->info.skip_pcode = desc->skip_pcode;
@@ -864,6 +859,7 @@ static int xe_info_init(struct xe_device *xe,
xe->info.has_range_tlb_inval = graphics_desc->has_range_tlb_inval;
xe->info.has_usm = graphics_desc->has_usm;
xe->info.has_64bit_timestamp = graphics_desc->has_64bit_timestamp;
+ xe->info.has_mem_copy_instr = GRAPHICS_VER(xe) >= 20;
xe_info_probe_tile_count(xe);
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index 9892c063a9c5..a4451bdc79fb 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -46,7 +46,6 @@ struct xe_device_desc {
u8 has_late_bind:1;
u8 has_llc:1;
u8 has_mbx_power_limits:1;
- u8 has_mem_copy_instr:1;
u8 has_pxp:1;
u8 has_sriov:1;
u8 needs_scratch:1;
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
index 797a4b866226..d963231b5135 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
@@ -346,7 +346,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe)
flags = EXEC_QUEUE_FLAG_KERNEL |
EXEC_QUEUE_FLAG_PERMANENT |
EXEC_QUEUE_FLAG_MIGRATE;
- q = xe_exec_queue_create_bind(xe, tile, flags, 0);
+ q = xe_exec_queue_create_bind(xe, tile, NULL, flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_ret;
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 79ab6c512d3e..095bb197e8b0 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -1617,7 +1617,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
if (!vm->pt_root[id])
continue;
- q = xe_exec_queue_create_bind(xe, tile, create_flags, 0);
+ q = xe_exec_queue_create_bind(xe, tile, vm, create_flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_close;
@@ -3578,6 +3578,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
}
+ if (XE_IOCTL_DBG(xe, q && vm != q->user_vm)) {
+ err = -EINVAL;
+ goto put_exec_queue;
+ }
+
/* Ensure all UNMAPs visible */
xe_svm_flush(vm);
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index ef8a5019574e..016f6786134c 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -379,7 +379,7 @@ static inline void xe_vm_set_validation_exec(struct xe_vm *vm, struct drm_exec *
}
/**
- * xe_vm_set_validation_exec() - Accessor to read the drm_exec object
+ * xe_vm_validation_exec() - Accessor to read the drm_exec object
* @vm: The vm we want to register a drm_exec object with.
*
* Return: The drm_exec object used to lock the vm's resv. The value
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 0a3ab7efed46..f1c17fb60dc1 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -195,13 +195,15 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
/*
* Write dump contents to the page. No need to synchronize; panic should
- * be single-threaded.
+ * be single-threaded. Ignore failures from kmsg_dump_get_buffer() since
+ * panic notification should be done even if there is no message data.
+ * Don't assume bytes_written is set in case of failure, so initialize it.
*/
kmsg_dump_rewind(&iter);
- kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
+ bytes_written = 0;
+ (void)kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
&bytes_written);
- if (!bytes_written)
- return;
+
/*
* P3 to contain the physical address of the panic page & P4 to
* contain the size of the panic data in that page. Rest of the
@@ -210,7 +212,7 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
hv_set_msr(HV_MSR_CRASH_P0, 0);
hv_set_msr(HV_MSR_CRASH_P1, 0);
hv_set_msr(HV_MSR_CRASH_P2, 0);
- hv_set_msr(HV_MSR_CRASH_P3, virt_to_phys(hv_panic_page));
+ hv_set_msr(HV_MSR_CRASH_P3, bytes_written ? virt_to_phys(hv_panic_page) : 0);
hv_set_msr(HV_MSR_CRASH_P4, bytes_written);
/*
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index b2862e0a317a..cdbc5f5c3215 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -375,7 +375,7 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
return;
/*
- * The cmxchg() above does an implicit memory barrier to
+ * The cmpxchg() above does an implicit memory barrier to
* ensure the write to MessageType (ie set to
* HVMSG_NONE) happens before we read the
* MessagePending and EOMing. Otherwise, the EOMing
diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
index d93a18f09c76..0b75ff1edb73 100644
--- a/drivers/hv/mshv_eventfd.c
+++ b/drivers/hv/mshv_eventfd.c
@@ -388,7 +388,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
{
struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;
struct mshv_irqfd *irqfd, *tmp;
- unsigned int events;
+ __poll_t events;
int ret;
int idx;
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 30bacba6aec3..adba3564d9f1 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -20,6 +20,41 @@
#define MSHV_MAP_FAULT_IN_PAGES PTRS_PER_PMD
/**
+ * mshv_chunk_stride - Compute stride for mapping guest memory
+ * @page : The page to check for huge page backing
+ * @gfn : Guest frame number for the mapping
+ * @page_count: Total number of pages in the mapping
+ *
+ * Determines the appropriate stride (in pages) for mapping guest memory.
+ * Uses huge page stride if the backing page is huge and the guest mapping
+ * is properly aligned; otherwise falls back to single page stride.
+ *
+ * Return: Stride in pages, or -EINVAL if page order is unsupported.
+ */
+static int mshv_chunk_stride(struct page *page,
+ u64 gfn, u64 page_count)
+{
+ unsigned int page_order;
+
+ /*
+ * Use single page stride by default. For huge page stride, the
+ * page must be compound and point to the head of the compound
+ * page, and both gfn and page_count must be huge-page aligned.
+ */
+ if (!PageCompound(page) || !PageHead(page) ||
+ !IS_ALIGNED(gfn, PTRS_PER_PMD) ||
+ !IS_ALIGNED(page_count, PTRS_PER_PMD))
+ return 1;
+
+ page_order = folio_order(page_folio(page));
+ /* The hypervisor only supports 2M huge page */
+ if (page_order != PMD_ORDER)
+ return -EINVAL;
+
+ return 1 << page_order;
+}
+
+/**
* mshv_region_process_chunk - Processes a contiguous chunk of memory pages
* in a region.
* @region : Pointer to the memory region structure.
@@ -45,25 +80,23 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
int (*handler)(struct mshv_mem_region *region,
u32 flags,
u64 page_offset,
- u64 page_count))
+ u64 page_count,
+ bool huge_page))
{
- u64 count, stride;
- unsigned int page_order;
+ u64 gfn = region->start_gfn + page_offset;
+ u64 count;
struct page *page;
- int ret;
+ int stride, ret;
page = region->pages[page_offset];
if (!page)
return -EINVAL;
- page_order = folio_order(page_folio(page));
- /* The hypervisor only supports 4K and 2M page sizes */
- if (page_order && page_order != PMD_ORDER)
- return -EINVAL;
+ stride = mshv_chunk_stride(page, gfn, page_count);
+ if (stride < 0)
+ return stride;
- stride = 1 << page_order;
-
- /* Start at stride since the first page is validated */
+ /* Start at stride since the first stride is validated */
for (count = stride; count < page_count; count += stride) {
page = region->pages[page_offset + count];
@@ -71,12 +104,13 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
if (!page)
break;
- /* Break if page size changes */
- if (page_order != folio_order(page_folio(page)))
+ /* Break if stride size changes */
+ if (stride != mshv_chunk_stride(page, gfn + count,
+ page_count - count))
break;
}
- ret = handler(region, flags, page_offset, count);
+ ret = handler(region, flags, page_offset, count, stride > 1);
if (ret)
return ret;
@@ -108,7 +142,8 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
int (*handler)(struct mshv_mem_region *region,
u32 flags,
u64 page_offset,
- u64 page_count))
+ u64 page_count,
+ bool huge_page))
{
long ret;
@@ -162,11 +197,10 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
static int mshv_region_chunk_share(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
return hv_call_modify_spa_host_access(region->partition->pt_id,
@@ -188,11 +222,10 @@ int mshv_region_share(struct mshv_mem_region *region)
static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
return hv_call_modify_spa_host_access(region->partition->pt_id,
@@ -212,11 +245,10 @@ int mshv_region_unshare(struct mshv_mem_region *region)
static int mshv_region_chunk_remap(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_MAP_GPA_LARGE_PAGE;
return hv_call_map_gpa_pages(region->partition->pt_id,
@@ -295,11 +327,10 @@ release_pages:
static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count)
+ u64 page_offset, u64 page_count,
+ bool huge_page)
{
- struct page *page = region->pages[page_offset];
-
- if (PageHuge(page) || PageTransCompound(page))
+ if (huge_page)
flags |= HV_UNMAP_GPA_LARGE_PAGE;
return hv_call_unmap_gpa_pages(region->partition->pt_id,
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 1134a82c7881..681b58154d5e 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -611,7 +611,6 @@ mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
return NULL;
}
-#ifdef CONFIG_X86_64
static struct mshv_mem_region *
mshv_partition_region_by_gfn_get(struct mshv_partition *p, u64 gfn)
{
@@ -643,12 +642,17 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
{
struct mshv_partition *p = vp->vp_partition;
struct mshv_mem_region *region;
- struct hv_x64_memory_intercept_message *msg;
bool ret;
u64 gfn;
-
- msg = (struct hv_x64_memory_intercept_message *)
+#if defined(CONFIG_X86_64)
+ struct hv_x64_memory_intercept_message *msg =
+ (struct hv_x64_memory_intercept_message *)
+ vp->vp_intercept_msg_page->u.payload;
+#elif defined(CONFIG_ARM64)
+ struct hv_arm64_memory_intercept_message *msg =
+ (struct hv_arm64_memory_intercept_message *)
vp->vp_intercept_msg_page->u.payload;
+#endif
gfn = HVPFN_DOWN(msg->guest_physical_address);
@@ -666,9 +670,6 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
return ret;
}
-#else /* CONFIG_X86_64 */
-static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) { return false; }
-#endif /* CONFIG_X86_64 */
static bool mshv_vp_handle_intercept(struct mshv_vp *vp)
{
@@ -1280,7 +1281,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
long ret;
if (mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP) ||
- !access_ok((const void *)mem.userspace_addr, mem.size))
+ !access_ok((const void __user *)mem.userspace_addr, mem.size))
return -EINVAL;
mmap_read_lock(current->mm);
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 591b7c12aae5..2482ecf5776b 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -810,16 +810,19 @@ static int intel_th_output_open(struct inode *inode, struct file *file)
int err;
dev = bus_find_device_by_devt(&intel_th_bus, inode->i_rdev);
- if (!dev || !dev->driver) {
+ if (!dev)
+ return -ENODEV;
+
+ if (!dev->driver) {
err = -ENODEV;
- goto out_no_device;
+ goto err_put_dev;
}
thdrv = to_intel_th_driver(dev->driver);
fops = fops_get(thdrv->fops);
if (!fops) {
err = -ENODEV;
- goto out_put_device;
+ goto err_put_dev;
}
replace_fops(file, fops);
@@ -829,19 +832,29 @@ static int intel_th_output_open(struct inode *inode, struct file *file)
if (file->f_op->open) {
err = file->f_op->open(inode, file);
if (err)
- goto out_put_device;
+ goto err_put_dev;
}
return 0;
-out_put_device:
+err_put_dev:
put_device(dev);
-out_no_device:
+
return err;
}
+static int intel_th_output_release(struct inode *inode, struct file *file)
+{
+ struct intel_th_device *thdev = file->private_data;
+
+ put_device(&thdev->dev);
+
+ return 0;
+}
+
static const struct file_operations intel_th_output_fops = {
.open = intel_th_output_open,
+ .release = intel_th_output_release,
.llseek = noop_llseek,
};
diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c
index d42c03ef5db5..8ef6d5d1927b 100644
--- a/drivers/i2c/busses/i2c-k1.c
+++ b/drivers/i2c/busses/i2c-k1.c
@@ -566,7 +566,7 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
- IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(i2c->dev), i2c);
+ IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq");
diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c
index 6d5f1a0d51e9..aef5109c1ddd 100644
--- a/drivers/iio/accel/adxl380.c
+++ b/drivers/iio/accel/adxl380.c
@@ -1784,9 +1784,9 @@ static int adxl380_config_irq(struct iio_dev *indio_dev)
st->int_map[1] = ADXL380_INT0_MAP1_REG;
} else {
st->irq = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT1");
- if (st->irq > 0)
- return dev_err_probe(st->dev, -ENODEV,
- "no interrupt name specified");
+ if (st->irq < 0)
+ return dev_err_probe(st->dev, st->irq,
+ "no interrupt name specified\n");
st->int_map[0] = ADXL380_INT1_MAP0_REG;
st->int_map[1] = ADXL380_INT1_MAP1_REG;
}
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index a7961c610ed2..1a9447c81b0f 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -517,7 +517,6 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = H3LIS331DL_ACCEL_DEV_NAME,
- [1] = IIS328DQ_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
@@ -585,6 +584,77 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.bootime = 2,
},
{
+ .wai = 0x32,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = IIS328DQ_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+ .odr = {
+ .addr = 0x20,
+ .mask = 0x18,
+ .odr_avl = {
+ { .hz = 50, .value = 0x00, },
+ { .hz = 100, .value = 0x01, },
+ { .hz = 400, .value = 0x02, },
+ { .hz = 1000, .value = 0x03, },
+ },
+ },
+ .pw = {
+ .addr = 0x20,
+ .mask = 0x20,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis = {
+ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+ .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+ },
+ .fs = {
+ .addr = 0x23,
+ .mask = 0x30,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_100G,
+ .value = 0x00,
+ .gain = IIO_G_TO_M_S_2(980),
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_200G,
+ .value = 0x01,
+ .gain = IIO_G_TO_M_S_2(1950),
+ },
+ [2] = {
+ .num = ST_ACCEL_FS_AVL_400G,
+ .value = 0x03,
+ .gain = IIO_G_TO_M_S_2(3910),
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x23,
+ .mask = 0x80,
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x22,
+ .mask = 0x02,
+ },
+ .int2 = {
+ .addr = 0x22,
+ .mask = 0x10,
+ },
+ .addr_ihl = 0x22,
+ .mask_ihl = 0x80,
+ },
+ .sim = {
+ .addr = 0x23,
+ .value = BIT(0),
+ },
+ .multi_read_bit = true,
+ .bootime = 2,
+ },
+ {
/* No WAI register present */
.sensors_supported = {
[0] = LIS3L02DQ_ACCEL_DEV_NAME,
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
index 50a6ff7c8b1c..ba12a3796e2b 100644
--- a/drivers/iio/adc/ad7280a.c
+++ b/drivers/iio/adc/ad7280a.c
@@ -1024,7 +1024,9 @@ static int ad7280_probe(struct spi_device *spi)
st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ;
st->spi->mode = SPI_MODE_1;
- spi_setup(st->spi);
+ ret = spi_setup(st->spi);
+ if (ret < 0)
+ return ret;
st->ctrl_lb = FIELD_PREP(AD7280A_CTRL_LB_ACQ_TIME_MSK, st->acquisition_time) |
FIELD_PREP(AD7280A_CTRL_LB_THERMISTOR_MSK, st->thermistor_term_en);
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
index 634852c4bbd2..b81e707ab40c 100644
--- a/drivers/iio/adc/ad7606_par.c
+++ b/drivers/iio/adc/ad7606_par.c
@@ -43,7 +43,8 @@ static int ad7606_par_bus_setup_iio_backend(struct device *dev,
struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
- unsigned int ret, c;
+ unsigned int c;
+ int ret;
struct iio_backend_data_fmt data = {
.sign_extend = true,
.enable = true,
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index f7a9f46ea0dc..2d8f8da3671d 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -95,7 +95,7 @@
#define CHIPID_AD9434 0x6A
#define AD9434_DEF_OUTPUT_MODE 0x00
-#define AD9434_REG_VREF_MASK 0xC0
+#define AD9434_REG_VREF_MASK GENMASK(4, 0)
/*
* Analog Devices AD9467 16-Bit, 200/250 MSPS ADC
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index b4c36e6a7490..aa4ba3f5a506 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -2481,6 +2481,7 @@ static void at91_adc_remove(struct platform_device *pdev)
struct at91_adc_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
+ cancel_work_sync(&st->touch_st.workq);
at91_adc_dma_disable(st);
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 1484adff00df..f2400897818c 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -540,15 +540,6 @@ static const struct iio_chan_spec exynos_adc_iio_channels[] = {
ADC_CHANNEL(9, "adc9"),
};
-static int exynos_adc_remove_devices(struct device *dev, void *c)
-{
- struct platform_device *pdev = to_platform_device(dev);
-
- platform_device_unregister(pdev);
-
- return 0;
-}
-
static int exynos_adc_probe(struct platform_device *pdev)
{
struct exynos_adc *info = NULL;
@@ -660,8 +651,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
return 0;
err_of_populate:
- device_for_each_child(&indio_dev->dev, NULL,
- exynos_adc_remove_devices);
+ of_platform_depopulate(&indio_dev->dev);
iio_device_unregister(indio_dev);
err_irq:
free_irq(info->irq, info);
@@ -681,8 +671,7 @@ static void exynos_adc_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct exynos_adc *info = iio_priv(indio_dev);
- device_for_each_child(&indio_dev->dev, NULL,
- exynos_adc_remove_devices);
+ of_platform_depopulate(&indio_dev->dev);
iio_device_unregister(indio_dev);
free_irq(info->irq, info);
if (info->data->exit_hw)
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index ec96bb0f2ed6..712b5e9caba6 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -665,9 +665,9 @@ static int pac1934_reg_snapshot(struct pac1934_chip_info *info,
/* add the power_acc field */
curr_energy += inc;
- clamp(curr_energy, PAC_193X_MIN_POWER_ACC, PAC_193X_MAX_POWER_ACC);
-
- reg_data->energy_sec_acc[cnt] = curr_energy;
+ reg_data->energy_sec_acc[cnt] = clamp(curr_energy,
+ PAC_193X_MIN_POWER_ACC,
+ PAC_193X_MAX_POWER_ACC);
}
offset_reg_data_p += PAC1934_VPOWER_ACC_REG_LEN;
diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c
index 8859f89fb2a9..0fd839176e26 100644
--- a/drivers/iio/chemical/scd4x.c
+++ b/drivers/iio/chemical/scd4x.c
@@ -584,7 +584,7 @@ static const struct iio_chan_spec scd4x_channels[] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_BE,
+ .endianness = IIO_CPU,
},
},
{
@@ -599,7 +599,7 @@ static const struct iio_chan_spec scd4x_channels[] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_BE,
+ .endianness = IIO_CPU,
},
},
{
@@ -612,7 +612,7 @@ static const struct iio_chan_spec scd4x_channels[] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_BE,
+ .endianness = IIO_CPU,
},
},
};
diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
index 41b96b48ba98..a9578afa7015 100644
--- a/drivers/iio/dac/ad3552r-hs.c
+++ b/drivers/iio/dac/ad3552r-hs.c
@@ -549,12 +549,15 @@ static ssize_t ad3552r_hs_write_data_source(struct file *f,
guard(mutex)(&st->lock);
+ if (count >= sizeof(buf))
+ return -ENOSPC;
+
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf,
count);
if (ret < 0)
return ret;
- buf[count] = '\0';
+ buf[ret] = '\0';
ret = match_string(dbgfs_attr_source, ARRAY_SIZE(dbgfs_attr_source),
buf);
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index d9cae9555e5d..4b18498aa074 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -434,6 +434,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
.num_channels = 4,
.regmap_type = AD5686_REGMAP,
},
+ [ID_AD5695R] = {
+ .channels = ad5685r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
[ID_AD5696] = {
.channels = ad5686_channels,
.num_channels = 4,
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
index ab1cb7b9dba4..25bd9757a594 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
@@ -960,16 +960,17 @@ int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
/*
* T°C = (temp / 128) + 25
- * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
- * scale: 100000 / 13248 = 7.8125
- * offset: 25000
+ * Tm°C = ((temp + 25 * 128) / 128)) * 1000
+ * Tm°C = (temp + 3200) * (1000 / 128)
+ * scale: 1000 / 128 = 7.8125
+ * offset: 3200
*/
case IIO_CHAN_INFO_SCALE:
*val = 7;
*val2 = 812500;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OFFSET:
- *val = 25000;
+ *val = 3200;
return IIO_VAL_INT;
default:
return -EINVAL;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 49ac17806e72..dc78227952a7 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -101,6 +101,13 @@ static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
+static const struct iio_chan_spec st_lsm6ds0_acc_channels[] = {
+ ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
+ ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
+ ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x22, IIO_MOD_X, 0),
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x24, IIO_MOD_Y, 1),
@@ -142,8 +149,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.channels = {
[ST_LSM6DSX_ID_ACC] = {
- .chan = st_lsm6dsx_acc_channels,
- .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ .chan = st_lsm6ds0_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6ds0_acc_channels),
},
[ST_LSM6DSX_ID_GYRO] = {
.chan = st_lsm6ds0_gyro_channels,
@@ -1449,8 +1456,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.channels = {
[ST_LSM6DSX_ID_ACC] = {
- .chan = st_lsm6dsx_acc_channels,
- .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ .chan = st_lsm6ds0_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6ds0_acc_channels),
},
[ST_LSM6DSX_ID_GYRO] = {
.chan = st_lsm6dsx_gyro_channels,
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index f69deefcfb6f..117ffad4f376 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1657,6 +1657,7 @@ static void iio_dev_release(struct device *device)
mutex_destroy(&iio_dev_opaque->info_exist_lock);
mutex_destroy(&iio_dev_opaque->mlock);
+ lockdep_unregister_key(&iio_dev_opaque->info_exist_key);
lockdep_unregister_key(&iio_dev_opaque->mlock_key);
ida_free(&iio_ida, iio_dev_opaque->id);
@@ -1717,9 +1718,10 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers);
lockdep_register_key(&iio_dev_opaque->mlock_key);
+ lockdep_register_key(&iio_dev_opaque->info_exist_key);
mutex_init_with_key(&iio_dev_opaque->mlock, &iio_dev_opaque->mlock_key);
- mutex_init(&iio_dev_opaque->info_exist_lock);
+ mutex_init_with_key(&iio_dev_opaque->info_exist_lock, &iio_dev_opaque->info_exist_key);
indio_dev->dev.parent = parent;
indio_dev->dev.type = &iio_device_type;
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index 654771275ce8..d2cf940b105a 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -116,6 +116,17 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER)
},
{
+ /*
+ * ASUS Zenbook UX425QA_UM425QA
+ * Some Zenbooks report "Zenbook" with a lowercase b.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook UX425QA_UM425QA"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER)
+ },
+ {
/* ASUS ZenBook UX425UA/QA */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
@@ -1176,6 +1187,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
+ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
/*
* A lot of modern Clevo barebones have touchpad and/or keyboard issues
* after suspend fixable with the forcenorestore quirk.
diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c
index 778deeb4a7e8..24d7b5a57794 100644
--- a/drivers/interconnect/debugfs-client.c
+++ b/drivers/interconnect/debugfs-client.c
@@ -150,6 +150,11 @@ int icc_debugfs_client_init(struct dentry *icc_dir)
return ret;
}
+ src_node = devm_kstrdup(&pdev->dev, "", GFP_KERNEL);
+ dst_node = devm_kstrdup(&pdev->dev, "", GFP_KERNEL);
+ if (!src_node || !dst_node)
+ return -ENOMEM;
+
client_dir = debugfs_create_dir("test_client", icc_dir);
debugfs_create_str("src_node", 0600, client_dir, &src_node);
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 5d45795c367a..7c12be1b247f 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -2450,8 +2450,6 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev)
goto out_err;
}
-out_err:
-
iommu_completion_wait(iommu);
if (FEATURE_NUM_INT_REMAP_SUP_2K(amd_iommu_efr2))
@@ -2462,6 +2460,7 @@ out_err:
if (dev_is_pci(dev))
pci_prepare_ats(to_pci_dev(dev), PAGE_SHIFT);
+out_err:
return iommu_dev;
}
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 378104cd395e..04cc7a9036e4 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -1078,6 +1078,9 @@ static int tegra241_vcmdq_hw_init_user(struct tegra241_vcmdq *vcmdq)
{
char header[64];
+ /* Reset VCMDQ */
+ tegra241_vcmdq_hw_deinit(vcmdq);
+
/* Configure the vcmdq only; User space does the enabling */
writeq_relaxed(vcmdq->cmdq.q.q_base, REG_VCMDQ_PAGE1(vcmdq, BASE));
diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h
index 3327116a441c..d575f3ba9d34 100644
--- a/drivers/iommu/generic_pt/iommu_pt.h
+++ b/drivers/iommu/generic_pt/iommu_pt.h
@@ -645,7 +645,7 @@ static __always_inline int __do_map_single_page(struct pt_range *range,
struct pt_iommu_map_args *map = arg;
pts.type = pt_load_single_entry(&pts);
- if (level == 0) {
+ if (pts.level == 0) {
if (pts.type != PT_ENTRY_EMPTY)
return -EADDRINUSE;
pt_install_leaf_entry(&pts, map->oa, PAGE_SHIFT,
@@ -931,6 +931,8 @@ static __maybe_unused int __unmap_range(struct pt_range *range, void *arg,
struct pt_table_p *table)
{
struct pt_state pts = pt_init(range, level, table);
+ unsigned int flush_start_index = UINT_MAX;
+ unsigned int flush_end_index = UINT_MAX;
struct pt_unmap_args *unmap = arg;
unsigned int num_oas = 0;
unsigned int start_index;
@@ -986,6 +988,9 @@ static __maybe_unused int __unmap_range(struct pt_range *range, void *arg,
iommu_pages_list_add(&unmap->free_list,
pts.table_lower);
pt_clear_entries(&pts, ilog2(1));
+ if (pts.index < flush_start_index)
+ flush_start_index = pts.index;
+ flush_end_index = pts.index + 1;
}
pts.index++;
} else {
@@ -999,7 +1004,10 @@ start_oa:
num_contig_lg2 = pt_entry_num_contig_lg2(&pts);
pt_clear_entries(&pts, num_contig_lg2);
num_oas += log2_to_int(num_contig_lg2);
+ if (pts.index < flush_start_index)
+ flush_start_index = pts.index;
pts.index += log2_to_int(num_contig_lg2);
+ flush_end_index = pts.index;
}
if (pts.index >= pts.end_index)
break;
@@ -1007,7 +1015,8 @@ start_oa:
} while (true);
unmap->unmapped += log2_mul(num_oas, pt_table_item_lg2sz(&pts));
- flush_writes_range(&pts, start_index, pts.index);
+ if (flush_start_index != flush_end_index)
+ flush_writes_range(&pts, flush_start_index, flush_end_index);
return ret;
}
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index e6626004b323..05d63fe92e43 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -637,7 +637,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
pte = READ_ONCE(*ptep);
if (!pte) {
WARN_ON(!(data->iop.cfg.quirks & IO_PGTABLE_QUIRK_NO_WARN));
- return -ENOENT;
+ return 0;
}
/* If the size matches this level, we're in the right place */
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index dbe51ecb9a20..f606148920fa 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -289,6 +289,7 @@ static void batch_clear(struct pfn_batch *batch)
batch->end = 0;
batch->pfns[0] = 0;
batch->npfns[0] = 0;
+ batch->kind = 0;
}
/*
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index ada585bfa451..2988def30972 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -709,7 +709,7 @@ static struct its_collection *its_build_mapd_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
- unsigned long itt_addr;
+ phys_addr_t itt_addr;
u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites);
itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
@@ -879,7 +879,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
struct its_cmd_desc *desc)
{
struct its_vpe *vpe = valid_vpe(its, desc->its_vmapp_cmd.vpe);
- unsigned long vpt_addr, vconf_addr;
+ phys_addr_t vpt_addr, vconf_addr;
u64 target;
bool alloc;
@@ -2477,10 +2477,10 @@ retry_baser:
baser->psz = psz;
tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz;
- pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n",
+ pr_info("ITS@%pa: allocated %d %s @%llx (%s, esz %d, psz %dK, shr %d)\n",
&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp),
its_base_type_string[type],
- (unsigned long)virt_to_phys(base),
+ (u64)virt_to_phys(base),
indirect ? "indirect" : "flat", (int)esz,
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
diff --git a/drivers/irqchip/irq-ls-extirq.c b/drivers/irqchip/irq-ls-extirq.c
index 50a7b38381b9..96f9c20621cf 100644
--- a/drivers/irqchip/irq-ls-extirq.c
+++ b/drivers/irqchip/irq-ls-extirq.c
@@ -168,40 +168,34 @@ ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node)
return 0;
}
-static int __init
-ls_extirq_of_init(struct device_node *node, struct device_node *parent)
+static int ls_extirq_probe(struct platform_device *pdev)
{
struct irq_domain *domain, *parent_domain;
+ struct device_node *node, *parent;
+ struct device *dev = &pdev->dev;
struct ls_extirq_data *priv;
int ret;
+ node = dev->of_node;
+ parent = of_irq_find_parent(node);
+ if (!parent)
+ return dev_err_probe(dev, -ENODEV, "Failed to get IRQ parent node\n");
+
parent_domain = irq_find_host(parent);
- if (!parent_domain) {
- pr_err("Cannot find parent domain\n");
- ret = -ENODEV;
- goto err_irq_find_host;
- }
+ if (!parent_domain)
+ return dev_err_probe(dev, -EPROBE_DEFER, "Cannot find parent domain\n");
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- ret = -ENOMEM;
- goto err_alloc_priv;
- }
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return dev_err_probe(dev, -ENOMEM, "Failed to allocate memory\n");
- /*
- * All extirq OF nodes are under a scfg/syscon node with
- * the 'ranges' property
- */
- priv->intpcr = of_iomap(node, 0);
- if (!priv->intpcr) {
- pr_err("Cannot ioremap OF node %pOF\n", node);
- ret = -ENOMEM;
- goto err_iomap;
- }
+ priv->intpcr = devm_of_iomap(dev, node, 0, NULL);
+ if (!priv->intpcr)
+ return dev_err_probe(dev, -ENOMEM, "Cannot ioremap OF node %pOF\n", node);
ret = ls_extirq_parse_map(priv, node);
if (ret)
- goto err_parse_map;
+ return dev_err_probe(dev, ret, "Failed to parse IRQ map\n");
priv->big_endian = of_device_is_big_endian(node->parent);
priv->is_ls1021a_or_ls1043a = of_device_is_compatible(node, "fsl,ls1021a-extirq") ||
@@ -210,23 +204,26 @@ ls_extirq_of_init(struct device_node *node, struct device_node *parent)
domain = irq_domain_create_hierarchy(parent_domain, 0, priv->nirq, of_fwnode_handle(node),
&extirq_domain_ops, priv);
- if (!domain) {
- ret = -ENOMEM;
- goto err_add_hierarchy;
- }
+ if (!domain)
+ return dev_err_probe(dev, -ENOMEM, "Failed to add IRQ domain\n");
return 0;
-
-err_add_hierarchy:
-err_parse_map:
- iounmap(priv->intpcr);
-err_iomap:
- kfree(priv);
-err_alloc_priv:
-err_irq_find_host:
- return ret;
}
-IRQCHIP_DECLARE(ls1021a_extirq, "fsl,ls1021a-extirq", ls_extirq_of_init);
-IRQCHIP_DECLARE(ls1043a_extirq, "fsl,ls1043a-extirq", ls_extirq_of_init);
-IRQCHIP_DECLARE(ls1088a_extirq, "fsl,ls1088a-extirq", ls_extirq_of_init);
+static const struct of_device_id ls_extirq_dt_ids[] = {
+ { .compatible = "fsl,ls1021a-extirq" },
+ { .compatible = "fsl,ls1043a-extirq" },
+ { .compatible = "fsl,ls1088a-extirq" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ls_extirq_dt_ids);
+
+static struct platform_driver ls_extirq_driver = {
+ .probe = ls_extirq_probe,
+ .driver = {
+ .name = "ls-extirq",
+ .of_match_table = ls_extirq_dt_ids,
+ }
+};
+
+builtin_platform_driver(ls_extirq_driver);
diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c
index 899a423b5da8..9b487120f011 100644
--- a/drivers/irqchip/irq-renesas-rzv2h.c
+++ b/drivers/irqchip/irq-renesas-rzv2h.c
@@ -328,6 +328,7 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type)
u32 titsr, titsr_k, titsel_n, tien;
struct rzv2h_icu_priv *priv;
u32 tssr, tssr_k, tssel_n;
+ u32 titsr_cur, tssr_cur;
unsigned int hwirq;
u32 tint, sense;
int tint_nr;
@@ -376,12 +377,18 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type)
guard(raw_spinlock)(&priv->lock);
tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
+ titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k));
+
+ tssr_cur = field_get(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width), tssr);
+ titsr_cur = field_get(ICU_TITSR_TITSEL_MASK(titsel_n), titsr);
+ if (tssr_cur == tint && titsr_cur == sense)
+ return 0;
+
tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien);
tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width);
writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
- titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k));
titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n);
titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n);
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index df98144a9539..33521c328a82 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -109,14 +109,14 @@ mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
spin_unlock_irq(&dev->lock);
if (filep->f_flags & O_NONBLOCK)
return -EAGAIN;
- wait_event_interruptible(dev->wait, (dev->work ||
+ wait_event_interruptible(dev->wait, (READ_ONCE(dev->work) ||
!list_empty(list)));
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&dev->lock);
}
if (dev->work)
- dev->work = 0;
+ WRITE_ONCE(dev->work, 0);
if (!list_empty(list)) {
timer = list_first_entry(list, struct mISDNtimer, list);
list_del(&timer->list);
@@ -141,13 +141,16 @@ mISDN_poll(struct file *filep, poll_table *wait)
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
if (dev) {
+ u32 work;
+
poll_wait(filep, &dev->wait, wait);
mask = 0;
- if (dev->work || !list_empty(&dev->expired))
+ work = READ_ONCE(dev->work);
+ if (work || !list_empty(&dev->expired))
mask |= (EPOLLIN | EPOLLRDNORM);
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
- dev->work, list_empty(&dev->expired));
+ work, list_empty(&dev->expired));
}
return mask;
}
@@ -172,7 +175,7 @@ misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
struct mISDNtimer *timer;
if (!timeout) {
- dev->work = 1;
+ WRITE_ONCE(dev->work, 1);
wake_up_interruptible(&dev->wait);
id = 0;
} else {
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 885399ed0776..d34a19453560 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -560,11 +560,6 @@ int led_classdev_register_ext(struct device *parent,
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
led_cdev->brightness_hw_changed = -1;
#endif
- /* add to the list of leds */
- down_write(&leds_list_lock);
- list_add_tail(&led_cdev->node, &leds_list);
- up_write(&leds_list_lock);
-
if (!led_cdev->max_brightness)
led_cdev->max_brightness = LED_FULL;
@@ -574,6 +569,11 @@ int led_classdev_register_ext(struct device *parent,
led_init_core(led_cdev);
+ /* add to the list of leds */
+ down_write(&leds_list_lock);
+ list_add_tail(&led_cdev->node, &leds_list);
+ up_write(&leds_list_lock);
+
#ifdef CONFIG_LEDS_TRIGGERS
led_trigger_set_default(led_cdev);
#endif
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 8ccacba85547..ec9ff9715081 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -273,6 +273,8 @@ struct bcache_device {
struct bio_set bio_split;
+ struct bio_set bio_detached;
+
unsigned int data_csum:1;
int (*cache_miss)(struct btree *b, struct search *s,
@@ -753,6 +755,13 @@ struct bbio {
struct bio bio;
};
+struct detached_dev_io_private {
+ struct bcache_device *d;
+ unsigned long start_time;
+ struct bio *orig_bio;
+ struct bio bio;
+};
+
#define BTREE_PRIO USHRT_MAX
#define INITIAL_PRIO 32768U
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 82fdea7dea7a..3fa3b13a410f 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -1077,68 +1077,54 @@ static CLOSURE_CALLBACK(cached_dev_nodata)
continue_at(cl, cached_dev_bio_complete, NULL);
}
-struct detached_dev_io_private {
- struct bcache_device *d;
- unsigned long start_time;
- bio_end_io_t *bi_end_io;
- void *bi_private;
- struct block_device *orig_bdev;
-};
-
static void detached_dev_end_io(struct bio *bio)
{
- struct detached_dev_io_private *ddip;
-
- ddip = bio->bi_private;
- bio->bi_end_io = ddip->bi_end_io;
- bio->bi_private = ddip->bi_private;
+ struct detached_dev_io_private *ddip =
+ container_of(bio, struct detached_dev_io_private, bio);
+ struct bio *orig_bio = ddip->orig_bio;
/* Count on the bcache device */
- bio_end_io_acct_remapped(bio, ddip->start_time, ddip->orig_bdev);
+ bio_end_io_acct(orig_bio, ddip->start_time);
if (bio->bi_status) {
- struct cached_dev *dc = container_of(ddip->d,
- struct cached_dev, disk);
+ struct cached_dev *dc = bio->bi_private;
+
/* should count I/O error for backing device here */
bch_count_backing_io_errors(dc, bio);
+ orig_bio->bi_status = bio->bi_status;
}
- kfree(ddip);
- bio_endio(bio);
+ bio_put(bio);
+ bio_endio(orig_bio);
}
-static void detached_dev_do_request(struct bcache_device *d, struct bio *bio,
- struct block_device *orig_bdev, unsigned long start_time)
+static void detached_dev_do_request(struct bcache_device *d,
+ struct bio *orig_bio, unsigned long start_time)
{
struct detached_dev_io_private *ddip;
struct cached_dev *dc = container_of(d, struct cached_dev, disk);
+ struct bio *clone_bio;
- /*
- * no need to call closure_get(&dc->disk.cl),
- * because upper layer had already opened bcache device,
- * which would call closure_get(&dc->disk.cl)
- */
- ddip = kzalloc(sizeof(struct detached_dev_io_private), GFP_NOIO);
- if (!ddip) {
- bio->bi_status = BLK_STS_RESOURCE;
- bio_endio(bio);
+ if (bio_op(orig_bio) == REQ_OP_DISCARD &&
+ !bdev_max_discard_sectors(dc->bdev)) {
+ bio_end_io_acct(orig_bio, start_time);
+ bio_endio(orig_bio);
return;
}
- ddip->d = d;
+ clone_bio = bio_alloc_clone(dc->bdev, orig_bio, GFP_NOIO,
+ &d->bio_detached);
+
+ ddip = container_of(clone_bio, struct detached_dev_io_private, bio);
/* Count on the bcache device */
- ddip->orig_bdev = orig_bdev;
+ ddip->d = d;
ddip->start_time = start_time;
- ddip->bi_end_io = bio->bi_end_io;
- ddip->bi_private = bio->bi_private;
- bio->bi_end_io = detached_dev_end_io;
- bio->bi_private = ddip;
-
- if ((bio_op(bio) == REQ_OP_DISCARD) &&
- !bdev_max_discard_sectors(dc->bdev))
- detached_dev_end_io(bio);
- else
- submit_bio_noacct(bio);
+ ddip->orig_bio = orig_bio;
+
+ clone_bio->bi_end_io = detached_dev_end_io;
+ clone_bio->bi_private = dc;
+
+ submit_bio_noacct(clone_bio);
}
static void quit_max_writeback_rate(struct cache_set *c,
@@ -1214,10 +1200,10 @@ void cached_dev_submit_bio(struct bio *bio)
start_time = bio_start_io_acct(bio);
- bio_set_dev(bio, dc->bdev);
bio->bi_iter.bi_sector += dc->sb.data_offset;
if (cached_dev_get(dc)) {
+ bio_set_dev(bio, dc->bdev);
s = search_alloc(bio, d, orig_bdev, start_time);
trace_bcache_request_start(s->d, bio);
@@ -1237,9 +1223,10 @@ void cached_dev_submit_bio(struct bio *bio)
else
cached_dev_read(dc, s);
}
- } else
+ } else {
/* I/O request sent to backing device */
- detached_dev_do_request(d, bio, orig_bdev, start_time);
+ detached_dev_do_request(d, bio, start_time);
+ }
}
static int cached_dev_ioctl(struct bcache_device *d, blk_mode_t mode,
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index c17d4517af22..238d12ffdae8 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -887,6 +887,7 @@ static void bcache_device_free(struct bcache_device *d)
}
bioset_exit(&d->bio_split);
+ bioset_exit(&d->bio_detached);
kvfree(d->full_dirty_stripes);
kvfree(d->stripe_sectors_dirty);
@@ -949,6 +950,11 @@ static int bcache_device_init(struct bcache_device *d, unsigned int block_size,
BIOSET_NEED_BVECS|BIOSET_NEED_RESCUER))
goto out_ida_remove;
+ if (bioset_init(&d->bio_detached, 4,
+ offsetof(struct detached_dev_io_private, bio),
+ BIOSET_NEED_BVECS|BIOSET_NEED_RESCUER))
+ goto out_bioset_split_exit;
+
if (lim.logical_block_size > PAGE_SIZE && cached_bdev) {
/*
* This should only happen with BCACHE_SB_VERSION_BDEV.
@@ -964,7 +970,7 @@ static int bcache_device_init(struct bcache_device *d, unsigned int block_size,
d->disk = blk_alloc_disk(&lim, NUMA_NO_NODE);
if (IS_ERR(d->disk))
- goto out_bioset_exit;
+ goto out_bioset_detach_exit;
set_capacity(d->disk, sectors);
snprintf(d->disk->disk_name, DISK_NAME_LEN, "bcache%i", idx);
@@ -976,7 +982,9 @@ static int bcache_device_init(struct bcache_device *d, unsigned int block_size,
d->disk->private_data = d;
return 0;
-out_bioset_exit:
+out_bioset_detach_exit:
+ bioset_exit(&d->bio_detached);
+out_bioset_split_exit:
bioset_exit(&d->bio_split);
out_ida_remove:
ida_free(&bcache_device_idx, idx);
diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h
index 5312edbf5190..24fa321d88bd 100644
--- a/drivers/misc/mei/mei-trace.h
+++ b/drivers/misc/mei/mei-trace.h
@@ -21,18 +21,18 @@ TRACE_EVENT(mei_reg_read,
TP_ARGS(dev, reg, offs, val),
TP_STRUCT__entry(
__string(dev, dev_name(dev))
- __field(const char *, reg)
+ __string(reg, reg)
__field(u32, offs)
__field(u32, val)
),
TP_fast_assign(
__assign_str(dev);
- __entry->reg = reg;
+ __assign_str(reg);
__entry->offs = offs;
__entry->val = val;
),
TP_printk("[%s] read %s:[%#x] = %#x",
- __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+ __get_str(dev), __get_str(reg), __entry->offs, __entry->val)
);
TRACE_EVENT(mei_reg_write,
@@ -40,18 +40,18 @@ TRACE_EVENT(mei_reg_write,
TP_ARGS(dev, reg, offs, val),
TP_STRUCT__entry(
__string(dev, dev_name(dev))
- __field(const char *, reg)
+ __string(reg, reg)
__field(u32, offs)
__field(u32, val)
),
TP_fast_assign(
__assign_str(dev);
- __entry->reg = reg;
+ __assign_str(reg);
__entry->offs = offs;
__entry->val = val;
),
TP_printk("[%s] write %s[%#x] = %#x",
- __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+ __get_str(dev), __get_str(reg), __entry->offs, __entry->val)
);
TRACE_EVENT(mei_pci_cfg_read,
@@ -59,18 +59,18 @@ TRACE_EVENT(mei_pci_cfg_read,
TP_ARGS(dev, reg, offs, val),
TP_STRUCT__entry(
__string(dev, dev_name(dev))
- __field(const char *, reg)
+ __string(reg, reg)
__field(u32, offs)
__field(u32, val)
),
TP_fast_assign(
__assign_str(dev);
- __entry->reg = reg;
+ __assign_str(reg);
__entry->offs = offs;
__entry->val = val;
),
TP_printk("[%s] pci cfg read %s:[%#x] = %#x",
- __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+ __get_str(dev), __get_str(reg), __entry->offs, __entry->val)
);
#endif /* _MEI_TRACE_H_ */
diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c
index 42e7d2a2a90c..6d71355528d3 100644
--- a/drivers/misc/uacce/uacce.c
+++ b/drivers/misc/uacce/uacce.c
@@ -40,20 +40,34 @@ static int uacce_start_queue(struct uacce_queue *q)
return 0;
}
-static int uacce_put_queue(struct uacce_queue *q)
+static int uacce_stop_queue(struct uacce_queue *q)
{
struct uacce_device *uacce = q->uacce;
- if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
+ if (q->state != UACCE_Q_STARTED)
+ return 0;
+
+ if (uacce->ops->stop_queue)
uacce->ops->stop_queue(q);
- if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
- uacce->ops->put_queue)
+ q->state = UACCE_Q_INIT;
+
+ return 0;
+}
+
+static void uacce_put_queue(struct uacce_queue *q)
+{
+ struct uacce_device *uacce = q->uacce;
+
+ uacce_stop_queue(q);
+
+ if (q->state != UACCE_Q_INIT)
+ return;
+
+ if (uacce->ops->put_queue)
uacce->ops->put_queue(q);
q->state = UACCE_Q_ZOMBIE;
-
- return 0;
}
static long uacce_fops_unl_ioctl(struct file *filep,
@@ -80,7 +94,7 @@ static long uacce_fops_unl_ioctl(struct file *filep,
ret = uacce_start_queue(q);
break;
case UACCE_CMD_PUT_Q:
- ret = uacce_put_queue(q);
+ ret = uacce_stop_queue(q);
break;
default:
if (uacce->ops->ioctl)
@@ -214,8 +228,14 @@ static void uacce_vma_close(struct vm_area_struct *vma)
}
}
+static int uacce_vma_mremap(struct vm_area_struct *area)
+{
+ return -EPERM;
+}
+
static const struct vm_operations_struct uacce_vm_ops = {
.close = uacce_vma_close,
+ .mremap = uacce_vma_mremap,
};
static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
@@ -382,6 +402,9 @@ static ssize_t isolate_strategy_show(struct device *dev, struct device_attribute
struct uacce_device *uacce = to_uacce_device(dev);
u32 val;
+ if (!uacce->ops->isolate_err_threshold_read)
+ return -ENOENT;
+
val = uacce->ops->isolate_err_threshold_read(uacce);
return sysfs_emit(buf, "%u\n", val);
@@ -394,6 +417,9 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut
unsigned long val;
int ret;
+ if (!uacce->ops->isolate_err_threshold_write)
+ return -ENOENT;
+
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;
@@ -519,6 +545,8 @@ EXPORT_SYMBOL_GPL(uacce_alloc);
*/
int uacce_register(struct uacce_device *uacce)
{
+ int ret;
+
if (!uacce)
return -ENODEV;
@@ -529,7 +557,11 @@ int uacce_register(struct uacce_device *uacce)
uacce->cdev->ops = &uacce_fops;
uacce->cdev->owner = THIS_MODULE;
- return cdev_device_add(uacce->cdev, &uacce->dev);
+ ret = cdev_device_add(uacce->cdev, &uacce->dev);
+ if (ret)
+ uacce->cdev = NULL;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(uacce_register);
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index dc2587ff8519..4db3328f46df 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1306,6 +1306,46 @@ out:
return err;
}
+static int sdmmc_card_busy(struct mmc_host *mmc)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+ u8 stat;
+ u8 mask = SD_DAT3_STATUS | SD_DAT2_STATUS | SD_DAT1_STATUS
+ | SD_DAT0_STATUS;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+ SD_CLK_TOGGLE_EN);
+ if (err)
+ goto out;
+
+ mdelay(1);
+
+ err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
+ if (err)
+ goto out;
+
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+out:
+ mutex_unlock(&pcr->pcr_mutex);
+
+ if (err)
+ return err;
+
+ /* check if any pin between dat[0:3] is low */
+ if ((stat & mask) != mask)
+ return 1;
+ else
+ return 0;
+}
+
static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
@@ -1418,6 +1458,7 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
.get_ro = sdmmc_get_ro,
.get_cd = sdmmc_get_cd,
.start_signal_voltage_switch = sdmmc_switch_voltage,
+ .card_busy = sdmmc_card_busy,
.execute_tuning = sdmmc_execute_tuning,
.init_sd_express = sdmmc_init_sd_express,
};
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 51949cde0958..2b75a36c096b 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -739,6 +739,13 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
+ if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
+ host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
+ dev_err(mmc_dev(host->mmc),
+ "Can't reduce the clock below 52MHz in HS200/HS400 mode");
+ return;
+ }
+
/*
* Disable DLL and reset both of sample and drive clock.
* The bypass bit and start bit need to be set if DLL is not locked.
@@ -1588,6 +1595,7 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm
{
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
unsigned int val, hsp_int_status, hsp_pwr_ctrl;
+ static const char * const clk_ids[] = {"axi"};
struct of_phandle_args args;
struct eic7700_priv *priv;
struct regmap *hsp_regmap;
@@ -1605,6 +1613,11 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm
return ret;
}
+ ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
+ ARRAY_SIZE(clk_ids), clk_ids);
+ if (ret)
+ return ret;
+
ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
if (ret) {
dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
@@ -1726,6 +1739,7 @@ static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
.set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
.set_power = sdhci_set_power_and_bus_voltage,
.irq = dwcmshc_cqe_irq_handler,
+ .adma_write_desc = dwcmshc_adma_write_desc,
.platform_execute_tuning = sdhci_eic7700_executing_tuning,
};
diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c
index e60e4ac1fd6f..3e86f346f751 100644
--- a/drivers/mtd/nand/spi/esmt.c
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -215,7 +215,7 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
SPINAND_INFO("F50D1G41LB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
- 0x7f),
+ 0x7f, 0x7f),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c
index e4ddb1e61923..3409af1ffb80 100644
--- a/drivers/mux/mmio.c
+++ b/drivers/mux/mmio.c
@@ -101,13 +101,13 @@ static int mux_mmio_probe(struct platform_device *pdev)
mux_mmio = mux_chip_priv(mux_chip);
mux_mmio->fields = devm_kmalloc(dev, num_fields * sizeof(*mux_mmio->fields), GFP_KERNEL);
- if (IS_ERR(mux_mmio->fields))
- return PTR_ERR(mux_mmio->fields);
+ if (!mux_mmio->fields)
+ return -ENOMEM;
mux_mmio->hardware_states = devm_kmalloc(dev, num_fields *
sizeof(*mux_mmio->hardware_states), GFP_KERNEL);
- if (IS_ERR(mux_mmio->hardware_states))
- return PTR_ERR(mux_mmio->hardware_states);
+ if (!mux_mmio->hardware_states)
+ return -ENOMEM;
for (i = 0; i < num_fields; i++) {
struct mux_control *mux = &mux_chip->mux[i];
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 3d56339a8a10..45bd2bb102ff 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1862,6 +1862,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
*/
if (!bond_has_slaves(bond)) {
if (bond_dev->type != slave_dev->type) {
+ if (slave_dev->type != ARPHRD_ETHER &&
+ BOND_MODE(bond) == BOND_MODE_8023AD) {
+ SLAVE_NL_ERR(bond_dev, slave_dev, extack,
+ "8023AD mode requires Ethernet devices");
+ return -EINVAL;
+ }
slave_dbg(bond_dev, slave_dev, "change device type from %d to %d\n",
bond_dev->type, slave_dev->type);
@@ -2196,11 +2202,6 @@ skip_mac_set:
unblock_netpoll_tx();
}
- /* broadcast mode uses the all_slaves to loop through slaves. */
- if (bond_mode_can_use_xmit_hash(bond) ||
- BOND_MODE(bond) == BOND_MODE_BROADCAST)
- bond_update_slave_arr(bond, NULL);
-
if (!slave_dev->netdev_ops->ndo_bpf ||
!slave_dev->netdev_ops->ndo_xdp_xmit) {
if (bond->xdp_prog) {
@@ -2234,6 +2235,11 @@ skip_mac_set:
bpf_prog_inc(bond->xdp_prog);
}
+ /* broadcast mode uses the all_slaves to loop through slaves. */
+ if (bond_mode_can_use_xmit_hash(bond) ||
+ BOND_MODE(bond) == BOND_MODE_BROADCAST)
+ bond_update_slave_arr(bond, NULL);
+
bond_xdp_set_features(bond_dev);
slave_info(bond_dev, slave_dev, "Enslaving as %s interface with %s link\n",
@@ -3041,8 +3047,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
__func__, &sip);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}
static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3261,8 +3267,8 @@ static void bond_validate_na(struct bonding *bond, struct slave *slave,
__func__, saddr);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}
static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3332,7 +3338,7 @@ int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
(slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
!slave_do_arp_validate_only(bond))
- slave->last_rx = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
return RX_HANDLER_ANOTHER;
} else if (is_arp) {
return bond_arp_rcv(skb, bond, slave);
@@ -3400,7 +3406,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_tx, 1) &&
- bond_time_in_interval(bond, slave->last_rx, 1)) {
+ bond_time_in_interval(bond, READ_ONCE(slave->last_rx), 1)) {
bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
@@ -3424,8 +3430,10 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) ||
- !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
+ if (!bond_time_in_interval(bond, last_tx,
+ bond->params.missed_max) ||
+ !bond_time_in_interval(bond, READ_ONCE(slave->last_rx),
+ bond->params.missed_max)) {
bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
@@ -4090,8 +4098,9 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb, const v
case BOND_XMIT_POLICY_ENCAP23:
case BOND_XMIT_POLICY_ENCAP34:
memset(fk, 0, sizeof(*fk));
- return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
- fk, data, l2_proto, nhoff, hlen, 0);
+ return __skb_flow_dissect(dev_net(bond->dev), skb,
+ &flow_keys_bonding, fk, data,
+ l2_proto, nhoff, hlen, 0);
default:
break;
}
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 384499c869b8..f1c6e9d8f616 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1152,7 +1152,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
bond_for_each_slave(bond, slave, iter)
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
targets[slot] = target;
}
}
@@ -1221,8 +1221,8 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
bond_for_each_slave(bond, slave, iter) {
targets_rx = slave->target_last_arp_rx;
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
- targets_rx[i] = targets_rx[i+1];
- targets_rx[i] = 0;
+ WRITE_ONCE(targets_rx[i], READ_ONCE(targets_rx[i+1]));
+ WRITE_ONCE(targets_rx[i], 0);
}
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
targets[i] = targets[i+1];
@@ -1377,7 +1377,7 @@ static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot,
if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter) {
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
slave_set_ns_maddr(bond, slave, target, &targets[slot]);
}
targets[slot] = *target;
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index c2a3a4eef5b2..58da323f14d7 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -1099,7 +1099,7 @@ static int at91_can_probe(struct platform_device *pdev)
if (IS_ERR(transceiver)) {
err = PTR_ERR(transceiver);
dev_err_probe(&pdev->dev, err, "failed to get phy\n");
- goto exit_iounmap;
+ goto exit_free;
}
dev->netdev_ops = &at91_netdev_ops;
diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c
index 7ab9578f5b89..769745e22a3c 100644
--- a/drivers/net/can/dev/dev.c
+++ b/drivers/net/can/dev/dev.c
@@ -332,6 +332,7 @@ struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
can_ml = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);
can_set_ml_priv(dev, can_ml);
+ can_set_cap(dev, CAN_CAP_CC);
if (echo_skb_max) {
priv->echo_skb_max = echo_skb_max;
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index de8e212a1366..4c219a5b139b 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -486,11 +486,17 @@ resubmit_urb:
urb->transfer_buffer, RX_BUFFER_SIZE,
ems_usb_read_bulk_callback, dev);
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
if (retval == -ENODEV)
netif_device_detach(netdev);
- else if (retval)
+ else
netdev_err(netdev,
"failed resubmitting read bulk urb: %d\n", retval);
}
diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
index 08da507faef4..8cc924c47042 100644
--- a/drivers/net/can/usb/esd_usb.c
+++ b/drivers/net/can/usb/esd_usb.c
@@ -541,13 +541,20 @@ resubmit_urb:
urb->transfer_buffer, ESD_USB_RX_BUFFER_SIZE,
esd_usb_read_bulk_callback, dev);
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!err)
+ return;
+
+ usb_unanchor_urb(urb);
+
if (err == -ENODEV) {
for (i = 0; i < dev->net_count; i++) {
if (dev->nets[i])
netif_device_detach(dev->nets[i]->netdev);
}
- } else if (err) {
+ } else {
dev_err(dev->udev->dev.parent,
"failed resubmitting read bulk urb: %pe\n", ERR_PTR(err));
}
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index d093babbc320..d8b2dd74b3a1 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -610,7 +610,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
{
struct gs_usb *parent = urb->context;
struct gs_can *dev;
- struct net_device *netdev;
+ struct net_device *netdev = NULL;
int rc;
struct net_device_stats *stats;
struct gs_host_frame *hf = urb->transfer_buffer;
@@ -754,6 +754,10 @@ resubmit_urb:
usb_anchor_urb(urb, &parent->rx_submitted);
rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!rc)
+ return;
+
+ usb_unanchor_urb(urb);
/* USB failure take down all interfaces */
if (rc == -ENODEV) {
@@ -762,6 +766,9 @@ device_detach:
if (parent->canch[rc])
netif_device_detach(parent->canch[rc]->netdev);
}
+ } else if (rc != -ESHUTDOWN && net_ratelimit()) {
+ netdev_info(netdev, "failed to re-submit IN URB: %pe\n",
+ ERR_PTR(rc));
}
}
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
index 62701ec34272..d0a2a2a33c1c 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
@@ -361,7 +361,14 @@ resubmit_urb:
urb->transfer_buffer, KVASER_USB_RX_BUFFER_SIZE,
kvaser_usb_read_bulk_callback, dev);
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!err)
+ return;
+
+ usb_unanchor_urb(urb);
+
if (err == -ENODEV) {
for (i = 0; i < dev->nchannels; i++) {
struct kvaser_usb_net_priv *priv;
@@ -372,7 +379,7 @@ resubmit_urb:
netif_device_detach(priv->netdev);
}
- } else if (err) {
+ } else {
dev_err(&dev->intf->dev,
"Failed resubmitting read bulk urb: %d\n", err);
}
diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c
index 41c0a1c399bf..04170326dc7e 100644
--- a/drivers/net/can/usb/mcba_usb.c
+++ b/drivers/net/can/usb/mcba_usb.c
@@ -608,11 +608,17 @@ resubmit_urb:
urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
mcba_usb_read_bulk_callback, priv);
+ usb_anchor_urb(urb, &priv->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
if (retval == -ENODEV)
netif_device_detach(netdev);
- else if (retval)
+ else
netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
retval);
}
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index 7449328f7cd7..3125cf59d002 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -541,11 +541,17 @@ resubmit_urb:
urb->transfer_buffer, RX_BUFFER_SIZE,
usb_8dev_read_bulk_callback, priv);
+ usb_anchor_urb(urb, &priv->rx_submitted);
+
retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!retval)
+ return;
+
+ usb_unanchor_urb(urb);
if (retval == -ENODEV)
netif_device_detach(netdev);
- else if (retval)
+ else
netdev_err(netdev,
"failed resubmitting read bulk urb: %d\n", retval);
}
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
index 1c511f5dc6ab..7b8c1549a0fb 100644
--- a/drivers/net/dsa/yt921x.c
+++ b/drivers/net/dsa/yt921x.c
@@ -682,21 +682,22 @@ static int yt921x_read_mib(struct yt921x_priv *priv, int port)
const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
u64 *valp = &((u64 *)mib)[i];
- u64 val = *valp;
u32 val0;
- u32 val1;
+ u64 val;
res = yt921x_reg_read(priv, reg, &val0);
if (res)
break;
if (desc->size <= 1) {
- if (val < (u32)val)
- /* overflow */
- val += (u64)U32_MAX + 1;
- val &= ~U32_MAX;
- val |= val0;
+ u64 old_val = *valp;
+
+ val = (old_val & ~(u64)U32_MAX) | val0;
+ if (val < old_val)
+ val += 1ull << 32;
} else {
+ u32 val1;
+
res = yt921x_reg_read(priv, reg + 4, &val1);
if (res)
break;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 3ddd896d6987..b5a60a048896 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -1837,7 +1837,7 @@ static void xgbe_get_stats64(struct net_device *netdev,
s->multicast = pstats->rxmulticastframes_g;
s->rx_length_errors = pstats->rxlengtherror;
s->rx_crc_errors = pstats->rxcrcerror;
- s->rx_fifo_errors = pstats->rxfifooverflow;
+ s->rx_over_errors = pstats->rxfifooverflow;
s->tx_packets = pstats->txframecount_gb;
s->tx_bytes = pstats->txoctetcount_gb;
@@ -2292,9 +2292,6 @@ read_again:
goto read_again;
if (error || packet->errors) {
- if (packet->errors)
- netif_err(pdata, rx_err, netdev,
- "error in received packet\n");
dev_kfree_skb(skb);
goto next_packet;
}
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.c b/drivers/net/ethernet/broadcom/asp2/bcmasp.c
index fd35f4b4dc50..014340f33345 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp.c
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.c
@@ -156,7 +156,7 @@ static void bcmasp_netfilt_hw_en_wake(struct bcmasp_priv *priv,
ASP_RX_FILTER_NET_OFFSET_L4(32),
ASP_RX_FILTER_NET_OFFSET(nfilt->hw_index + 1));
- rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->port + 8) |
+ rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->ch) |
ASP_RX_FILTER_NET_CFG_EN |
ASP_RX_FILTER_NET_CFG_L2_EN |
ASP_RX_FILTER_NET_CFG_L3_EN |
@@ -166,7 +166,7 @@ static void bcmasp_netfilt_hw_en_wake(struct bcmasp_priv *priv,
ASP_RX_FILTER_NET_CFG_UMC(nfilt->port),
ASP_RX_FILTER_NET_CFG(nfilt->hw_index));
- rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->port + 8) |
+ rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->ch) |
ASP_RX_FILTER_NET_CFG_EN |
ASP_RX_FILTER_NET_CFG_L2_EN |
ASP_RX_FILTER_NET_CFG_L3_EN |
@@ -714,6 +714,7 @@ struct bcmasp_net_filter *bcmasp_netfilt_get_init(struct bcmasp_intf *intf,
nfilter = &priv->net_filters[open_index];
nfilter->claimed = true;
nfilter->port = intf->port;
+ nfilter->ch = intf->channel + priv->tx_chan_offset;
nfilter->hw_index = open_index;
}
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.h b/drivers/net/ethernet/broadcom/asp2/bcmasp.h
index 74adfdb50e11..e238507be40a 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp.h
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.h
@@ -348,6 +348,7 @@ struct bcmasp_net_filter {
bool wake_filter;
int port;
+ int ch;
unsigned int hw_index;
};
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
index b9973956c480..ceb6c11431dd 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
@@ -1261,7 +1261,7 @@ struct bcmasp_intf *bcmasp_interface_create(struct bcmasp_priv *priv,
netdev_err(intf->ndev, "invalid PHY mode: %s for port %d\n",
phy_modes(intf->phy_interface), intf->port);
ret = -EINVAL;
- goto err_free_netdev;
+ goto err_deregister_fixed_link;
}
ret = of_get_ethdev_address(ndev_dn, ndev);
@@ -1286,6 +1286,9 @@ struct bcmasp_intf *bcmasp_interface_create(struct bcmasp_priv *priv,
return intf;
+err_deregister_fixed_link:
+ if (of_phy_is_fixed_link(ndev_dn))
+ of_phy_deregister_fixed_link(ndev_dn);
err_free_netdev:
free_netdev(ndev);
err:
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index bb5d2fa15736..8ed45bceb537 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -3801,6 +3801,7 @@ int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac)
{
int status;
bool pmac_valid = false;
+ u32 pmac_id;
eth_zero_addr(mac);
@@ -3813,7 +3814,7 @@ int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac)
adapter->if_handle, 0);
} else {
status = be_cmd_get_mac_from_list(adapter, mac, &pmac_valid,
- NULL, adapter->if_handle, 0);
+ &pmac_id, adapter->if_handle, 0);
}
return status;
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 5bb31c8fab39..995c159003d7 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -2141,7 +2141,7 @@ static int be_get_new_eqd(struct be_eq_obj *eqo)
struct be_aic_obj *aic;
struct be_rx_obj *rxo;
struct be_tx_obj *txo;
- u64 rx_pkts = 0, tx_pkts = 0;
+ u64 rx_pkts = 0, tx_pkts = 0, pkts;
ulong now;
u32 pps, delta;
int i;
@@ -2157,15 +2157,17 @@ static int be_get_new_eqd(struct be_eq_obj *eqo)
for_all_rx_queues_on_eq(adapter, eqo, rxo, i) {
do {
start = u64_stats_fetch_begin(&rxo->stats.sync);
- rx_pkts += rxo->stats.rx_pkts;
+ pkts = rxo->stats.rx_pkts;
} while (u64_stats_fetch_retry(&rxo->stats.sync, start));
+ rx_pkts += pkts;
}
for_all_tx_queues_on_eq(adapter, eqo, txo, i) {
do {
start = u64_stats_fetch_begin(&txo->stats.sync);
- tx_pkts += txo->stats.tx_reqs;
+ pkts = txo->stats.tx_reqs;
} while (u64_stats_fetch_retry(&txo->stats.sync, start));
+ tx_pkts += pkts;
}
/* Skip, if wrapped around or first calculation */
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index a753265961af..797ef6899657 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1150,7 +1150,7 @@ fec_restart(struct net_device *ndev)
u32 rcntl = FEC_RCR_MII;
if (OPT_ARCH_HAS_MAX_FL)
- rcntl |= (fep->netdev->mtu + ETH_HLEN + ETH_FCS_LEN) << 16;
+ rcntl |= (fep->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN) << 16;
if (fep->bufdesc_ex)
fec_ptp_save_state(fep);
@@ -1285,12 +1285,13 @@ fec_restart(struct net_device *ndev)
/* When Jumbo Frame is enabled, the FIFO may not be large enough
* to hold an entire frame. In such cases, if the MTU exceeds
- * (PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN), configure the interface
- * to operate in cut-through mode, triggered by the FIFO threshold.
+ * (PKT_MAXBUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN), configure
+ * the interface to operate in cut-through mode, triggered by
+ * the FIFO threshold.
* Otherwise, enable the ENET store-and-forward mode.
*/
if ((fep->quirks & FEC_QUIRK_JUMBO_FRAME) &&
- (ndev->mtu > (PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN)))
+ (ndev->mtu > (PKT_MAXBUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN)))
writel(0xF, fep->hwp + FEC_X_WMRK);
else
writel(FEC_TXWMRK_STRFWD, fep->hwp + FEC_X_WMRK);
@@ -4037,7 +4038,7 @@ static int fec_change_mtu(struct net_device *ndev, int new_mtu)
if (netif_running(ndev))
return -EBUSY;
- order = get_order(new_mtu + ETH_HLEN + ETH_FCS_LEN
+ order = get_order(new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN
+ FEC_DRV_RESERVE_SPACE);
fep->rx_frame_size = (PAGE_SIZE << order) - FEC_DRV_RESERVE_SPACE;
fep->pagepool_order = order;
@@ -4588,7 +4589,7 @@ fec_probe(struct platform_device *pdev)
else
fep->max_buf_size = PKT_MAXBUF_SIZE;
- ndev->max_mtu = fep->max_buf_size - ETH_HLEN - ETH_FCS_LEN;
+ ndev->max_mtu = fep->max_buf_size - VLAN_ETH_HLEN - ETH_FCS_LEN;
ret = register_netdev(ndev);
if (ret)
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index affd5a6c44e7..131d1210dc4a 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -1602,8 +1602,10 @@ static void ugeth_mac_config(struct phylink_config *config, unsigned int mode,
pr_warn("TBI mode requires that the device tree specify a tbi-handle\n");
tbiphy = of_phy_find_device(ug_info->tbi_node);
- if (!tbiphy)
+ if (!tbiphy) {
pr_warn("Could not get TBI device\n");
+ return;
+ }
value = phy_read(tbiphy, ENET_TBI_MII_CR);
value &= ~0x1000; /* Turn off autonegotiation */
diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
index 970d5ca8cdde..cbdf3a842cfe 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -1206,6 +1206,11 @@ static inline bool gve_supports_xdp_xmit(struct gve_priv *priv)
}
}
+static inline bool gve_is_clock_enabled(struct gve_priv *priv)
+{
+ return priv->nic_ts_report;
+}
+
/* gqi napi handler defined in gve_main.c */
int gve_napi_poll(struct napi_struct *napi, int budget);
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
index 52500ae8348e..311b106160b2 100644
--- a/drivers/net/ethernet/google/gve/gve_ethtool.c
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -938,7 +938,7 @@ static int gve_get_ts_info(struct net_device *netdev,
ethtool_op_get_ts_info(netdev, info);
- if (priv->nic_timestamp_supported) {
+ if (gve_is_clock_enabled(priv)) {
info->so_timestamping |= SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index 7eb64e1e4d85..52c5e4942cd4 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -680,10 +680,12 @@ static int gve_setup_device_resources(struct gve_priv *priv)
}
}
- err = gve_init_clock(priv);
- if (err) {
- dev_err(&priv->pdev->dev, "Failed to init clock");
- goto abort_with_ptype_lut;
+ if (priv->nic_timestamp_supported) {
+ err = gve_init_clock(priv);
+ if (err) {
+ dev_warn(&priv->pdev->dev, "Failed to init clock, continuing without PTP support");
+ err = 0;
+ }
}
err = gve_init_rss_config(priv, priv->rx_cfg.num_queues);
@@ -2183,7 +2185,7 @@ static int gve_set_ts_config(struct net_device *dev,
}
if (kernel_config->rx_filter != HWTSTAMP_FILTER_NONE) {
- if (!priv->nic_ts_report) {
+ if (!gve_is_clock_enabled(priv)) {
NL_SET_ERR_MSG_MOD(extack,
"RX timestamping is not supported");
kernel_config->rx_filter = HWTSTAMP_FILTER_NONE;
diff --git a/drivers/net/ethernet/google/gve/gve_ptp.c b/drivers/net/ethernet/google/gve/gve_ptp.c
index 073677d82ee8..de42fc2c19a1 100644
--- a/drivers/net/ethernet/google/gve/gve_ptp.c
+++ b/drivers/net/ethernet/google/gve/gve_ptp.c
@@ -70,11 +70,6 @@ static int gve_ptp_init(struct gve_priv *priv)
struct gve_ptp *ptp;
int err;
- if (!priv->nic_timestamp_supported) {
- dev_dbg(&priv->pdev->dev, "Device does not support PTP\n");
- return -EOPNOTSUPP;
- }
-
priv->ptp = kzalloc(sizeof(*priv->ptp), GFP_KERNEL);
if (!priv->ptp)
return -ENOMEM;
@@ -116,9 +111,6 @@ int gve_init_clock(struct gve_priv *priv)
{
int err;
- if (!priv->nic_timestamp_supported)
- return 0;
-
err = gve_ptp_init(priv);
if (err)
return err;
diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
index f1bd8f5d5732..63a96106a693 100644
--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
@@ -484,7 +484,7 @@ int gve_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp)
{
const struct gve_xdp_buff *ctx = (void *)_ctx;
- if (!ctx->gve->nic_ts_report)
+ if (!gve_is_clock_enabled(ctx->gve))
return -ENODATA;
if (!(ctx->compl_desc->ts_sub_nsecs_low & GVE_DQO_RX_HWTSTAMP_VALID))
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 7a0654e2d3dd..7a9573dcab74 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -2529,44 +2529,47 @@ static netdev_features_t hns3_features_check(struct sk_buff *skb,
static void hns3_fetch_stats(struct rtnl_link_stats64 *stats,
struct hns3_enet_ring *ring, bool is_tx)
{
+ struct ring_stats ring_stats;
unsigned int start;
do {
start = u64_stats_fetch_begin(&ring->syncp);
- if (is_tx) {
- stats->tx_bytes += ring->stats.tx_bytes;
- stats->tx_packets += ring->stats.tx_pkts;
- stats->tx_dropped += ring->stats.sw_err_cnt;
- stats->tx_dropped += ring->stats.tx_vlan_err;
- stats->tx_dropped += ring->stats.tx_l4_proto_err;
- stats->tx_dropped += ring->stats.tx_l2l3l4_err;
- stats->tx_dropped += ring->stats.tx_tso_err;
- stats->tx_dropped += ring->stats.over_max_recursion;
- stats->tx_dropped += ring->stats.hw_limitation;
- stats->tx_dropped += ring->stats.copy_bits_err;
- stats->tx_dropped += ring->stats.skb2sgl_err;
- stats->tx_dropped += ring->stats.map_sg_err;
- stats->tx_errors += ring->stats.sw_err_cnt;
- stats->tx_errors += ring->stats.tx_vlan_err;
- stats->tx_errors += ring->stats.tx_l4_proto_err;
- stats->tx_errors += ring->stats.tx_l2l3l4_err;
- stats->tx_errors += ring->stats.tx_tso_err;
- stats->tx_errors += ring->stats.over_max_recursion;
- stats->tx_errors += ring->stats.hw_limitation;
- stats->tx_errors += ring->stats.copy_bits_err;
- stats->tx_errors += ring->stats.skb2sgl_err;
- stats->tx_errors += ring->stats.map_sg_err;
- } else {
- stats->rx_bytes += ring->stats.rx_bytes;
- stats->rx_packets += ring->stats.rx_pkts;
- stats->rx_dropped += ring->stats.l2_err;
- stats->rx_errors += ring->stats.l2_err;
- stats->rx_errors += ring->stats.l3l4_csum_err;
- stats->rx_crc_errors += ring->stats.l2_err;
- stats->multicast += ring->stats.rx_multicast;
- stats->rx_length_errors += ring->stats.err_pkt_len;
- }
+ ring_stats = ring->stats;
} while (u64_stats_fetch_retry(&ring->syncp, start));
+
+ if (is_tx) {
+ stats->tx_bytes += ring_stats.tx_bytes;
+ stats->tx_packets += ring_stats.tx_pkts;
+ stats->tx_dropped += ring_stats.sw_err_cnt;
+ stats->tx_dropped += ring_stats.tx_vlan_err;
+ stats->tx_dropped += ring_stats.tx_l4_proto_err;
+ stats->tx_dropped += ring_stats.tx_l2l3l4_err;
+ stats->tx_dropped += ring_stats.tx_tso_err;
+ stats->tx_dropped += ring_stats.over_max_recursion;
+ stats->tx_dropped += ring_stats.hw_limitation;
+ stats->tx_dropped += ring_stats.copy_bits_err;
+ stats->tx_dropped += ring_stats.skb2sgl_err;
+ stats->tx_dropped += ring_stats.map_sg_err;
+ stats->tx_errors += ring_stats.sw_err_cnt;
+ stats->tx_errors += ring_stats.tx_vlan_err;
+ stats->tx_errors += ring_stats.tx_l4_proto_err;
+ stats->tx_errors += ring_stats.tx_l2l3l4_err;
+ stats->tx_errors += ring_stats.tx_tso_err;
+ stats->tx_errors += ring_stats.over_max_recursion;
+ stats->tx_errors += ring_stats.hw_limitation;
+ stats->tx_errors += ring_stats.copy_bits_err;
+ stats->tx_errors += ring_stats.skb2sgl_err;
+ stats->tx_errors += ring_stats.map_sg_err;
+ } else {
+ stats->rx_bytes += ring_stats.rx_bytes;
+ stats->rx_packets += ring_stats.rx_pkts;
+ stats->rx_dropped += ring_stats.l2_err;
+ stats->rx_errors += ring_stats.l2_err;
+ stats->rx_errors += ring_stats.l3l4_csum_err;
+ stats->rx_crc_errors += ring_stats.l2_err;
+ stats->multicast += ring_stats.rx_multicast;
+ stats->rx_length_errors += ring_stats.err_pkt_len;
+ }
}
static void hns3_nic_get_stats64(struct net_device *netdev,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 9bb708fa42f2..416e02e7b995 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -731,7 +731,7 @@ struct hclge_fd_tcam_config_3_cmd {
#define HCLGE_FD_AD_QID_M GENMASK(11, 2)
#define HCLGE_FD_AD_USE_COUNTER_B 12
#define HCLGE_FD_AD_COUNTER_NUM_S 13
-#define HCLGE_FD_AD_COUNTER_NUM_M GENMASK(20, 13)
+#define HCLGE_FD_AD_COUNTER_NUM_M GENMASK(19, 13)
#define HCLGE_FD_AD_NXT_STEP_B 20
#define HCLGE_FD_AD_NXT_KEY_S 21
#define HCLGE_FD_AD_NXT_KEY_M GENMASK(25, 21)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index c589baea7c77..b8e2aa19f9e6 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -5690,7 +5690,7 @@ static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc,
HCLGE_FD_AD_COUNTER_NUM_S, action->counter_id);
hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage);
hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S,
- action->counter_id);
+ action->next_input_key);
req->ad_data = cpu_to_le64(ad_data);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
index a69b361225e9..84bee5d6e638 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
@@ -43,21 +43,12 @@ static void qp_add_napi(struct hinic3_irq_cfg *irq_cfg)
struct hinic3_nic_dev *nic_dev = netdev_priv(irq_cfg->netdev);
netif_napi_add(nic_dev->netdev, &irq_cfg->napi, hinic3_poll);
- netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
- NETDEV_QUEUE_TYPE_RX, &irq_cfg->napi);
- netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
- NETDEV_QUEUE_TYPE_TX, &irq_cfg->napi);
napi_enable(&irq_cfg->napi);
}
static void qp_del_napi(struct hinic3_irq_cfg *irq_cfg)
{
napi_disable(&irq_cfg->napi);
- netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
- NETDEV_QUEUE_TYPE_RX, NULL);
- netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
- NETDEV_QUEUE_TYPE_TX, NULL);
- netif_stop_subqueue(irq_cfg->netdev, irq_cfg->irq_id);
netif_napi_del(&irq_cfg->napi);
}
@@ -150,6 +141,11 @@ int hinic3_qps_irq_init(struct net_device *netdev)
goto err_release_irqs;
}
+ netif_queue_set_napi(irq_cfg->netdev, q_id,
+ NETDEV_QUEUE_TYPE_RX, &irq_cfg->napi);
+ netif_queue_set_napi(irq_cfg->netdev, q_id,
+ NETDEV_QUEUE_TYPE_TX, &irq_cfg->napi);
+
hinic3_set_msix_auto_mask_state(nic_dev->hwdev,
irq_cfg->msix_entry_idx,
HINIC3_SET_MSIX_AUTO_MASK);
@@ -164,6 +160,10 @@ err_release_irqs:
q_id--;
irq_cfg = &nic_dev->q_params.irq_cfg[q_id];
qp_del_napi(irq_cfg);
+ netif_queue_set_napi(irq_cfg->netdev, q_id,
+ NETDEV_QUEUE_TYPE_RX, NULL);
+ netif_queue_set_napi(irq_cfg->netdev, q_id,
+ NETDEV_QUEUE_TYPE_TX, NULL);
hinic3_set_msix_state(nic_dev->hwdev, irq_cfg->msix_entry_idx,
HINIC3_MSIX_DISABLE);
hinic3_set_msix_auto_mask_state(nic_dev->hwdev,
@@ -184,6 +184,10 @@ void hinic3_qps_irq_uninit(struct net_device *netdev)
for (q_id = 0; q_id < nic_dev->q_params.num_qps; q_id++) {
irq_cfg = &nic_dev->q_params.irq_cfg[q_id];
qp_del_napi(irq_cfg);
+ netif_queue_set_napi(irq_cfg->netdev, q_id,
+ NETDEV_QUEUE_TYPE_RX, NULL);
+ netif_queue_set_napi(irq_cfg->netdev, q_id,
+ NETDEV_QUEUE_TYPE_TX, NULL);
hinic3_set_msix_state(nic_dev->hwdev, irq_cfg->msix_entry_idx,
HINIC3_MSIX_DISABLE);
hinic3_set_msix_auto_mask_state(nic_dev->hwdev,
diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c
index d88b7f3fd1f9..2ef39cc70c21 100644
--- a/drivers/net/ethernet/intel/ice/devlink/devlink.c
+++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c
@@ -460,6 +460,7 @@ static void ice_devlink_reinit_down(struct ice_pf *pf)
ice_vsi_decfg(ice_get_main_vsi(pf));
rtnl_unlock();
ice_deinit_pf(pf);
+ ice_deinit_hw(&pf->hw);
ice_deinit_dev(pf);
}
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 147aaee192a7..00f75d87c73f 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -979,6 +979,7 @@ void ice_map_xdp_rings(struct ice_vsi *vsi);
int
ice_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
u32 flags);
+int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
int ice_set_rss_lut(struct ice_vsi *vsi, u8 *lut, u16 lut_size);
int ice_get_rss_lut(struct ice_vsi *vsi, u8 *lut, u16 lut_size);
int ice_set_rss_key(struct ice_vsi *vsi, u8 *seed);
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index 046bc9c65c51..785bf5cc1b25 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -2251,7 +2251,7 @@ void ice_release_res(struct ice_hw *hw, enum ice_aq_res_ids res)
/* there are some rare cases when trying to release the resource
* results in an admin queue timeout, so handle them correctly
*/
- timeout = jiffies + 10 * ICE_CTL_Q_SQ_CMD_TIMEOUT;
+ timeout = jiffies + 10 * usecs_to_jiffies(ICE_CTL_Q_SQ_CMD_TIMEOUT);
do {
status = ice_aq_release_res(hw, res, 0, NULL);
if (status != -EIO)
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index 969d4f8f9c02..3565a5d96c6d 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3626,11 +3626,7 @@ ice_get_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh)
if (!lut)
return -ENOMEM;
- err = ice_get_rss_key(vsi, rxfh->key);
- if (err)
- goto out;
-
- err = ice_get_rss_lut(vsi, lut, vsi->rss_table_size);
+ err = ice_get_rss(vsi, rxfh->key, lut, vsi->rss_table_size);
if (err)
goto out;
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 15621707fbf8..d47af94f31a9 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -398,6 +398,8 @@ static int ice_vsi_alloc_ring_stats(struct ice_vsi *vsi)
if (!ring_stats)
goto err_out;
+ u64_stats_init(&ring_stats->syncp);
+
WRITE_ONCE(tx_ring_stats[i], ring_stats);
}
@@ -417,6 +419,8 @@ static int ice_vsi_alloc_ring_stats(struct ice_vsi *vsi)
if (!ring_stats)
goto err_out;
+ u64_stats_init(&ring_stats->syncp);
+
WRITE_ONCE(rx_ring_stats[i], ring_stats);
}
@@ -2779,12 +2783,14 @@ void ice_vsi_set_napi_queues(struct ice_vsi *vsi)
ASSERT_RTNL();
ice_for_each_rxq(vsi, q_idx)
- netif_queue_set_napi(netdev, q_idx, NETDEV_QUEUE_TYPE_RX,
- &vsi->rx_rings[q_idx]->q_vector->napi);
+ if (vsi->rx_rings[q_idx] && vsi->rx_rings[q_idx]->q_vector)
+ netif_queue_set_napi(netdev, q_idx, NETDEV_QUEUE_TYPE_RX,
+ &vsi->rx_rings[q_idx]->q_vector->napi);
ice_for_each_txq(vsi, q_idx)
- netif_queue_set_napi(netdev, q_idx, NETDEV_QUEUE_TYPE_TX,
- &vsi->tx_rings[q_idx]->q_vector->napi);
+ if (vsi->tx_rings[q_idx] && vsi->tx_rings[q_idx]->q_vector)
+ netif_queue_set_napi(netdev, q_idx, NETDEV_QUEUE_TYPE_TX,
+ &vsi->tx_rings[q_idx]->q_vector->napi);
/* Also set the interrupt number for the NAPI */
ice_for_each_q_vector(vsi, v_idx) {
struct ice_q_vector *q_vector = vsi->q_vectors[v_idx];
@@ -3805,22 +3811,31 @@ int ice_vsi_add_vlan_zero(struct ice_vsi *vsi)
int ice_vsi_del_vlan_zero(struct ice_vsi *vsi)
{
struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi);
+ struct ice_pf *pf = vsi->back;
struct ice_vlan vlan;
int err;
- vlan = ICE_VLAN(0, 0, 0);
- err = vlan_ops->del_vlan(vsi, &vlan);
- if (err && err != -EEXIST)
- return err;
+ if (pf->lag && pf->lag->primary) {
+ dev_dbg(ice_pf_to_dev(pf), "Interface is primary in aggregate - not deleting prune list\n");
+ } else {
+ vlan = ICE_VLAN(0, 0, 0);
+ err = vlan_ops->del_vlan(vsi, &vlan);
+ if (err && err != -EEXIST)
+ return err;
+ }
/* in SVM both VLAN 0 filters are identical */
if (!ice_is_dvm_ena(&vsi->back->hw))
return 0;
- vlan = ICE_VLAN(ETH_P_8021Q, 0, 0);
- err = vlan_ops->del_vlan(vsi, &vlan);
- if (err && err != -EEXIST)
- return err;
+ if (pf->lag && pf->lag->primary) {
+ dev_dbg(ice_pf_to_dev(pf), "Interface is primary in aggregate - not deleting QinQ prune list\n");
+ } else {
+ vlan = ICE_VLAN(ETH_P_8021Q, 0, 0);
+ err = vlan_ops->del_vlan(vsi, &vlan);
+ if (err && err != -EEXIST)
+ return err;
+ }
/* when deleting the last VLAN filter, make sure to disable the VLAN
* promisc mode so the filter isn't left by accident
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 4bb68e7a00f5..71c6d53b461e 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4836,6 +4836,7 @@ static void ice_deinit_features(struct ice_pf *pf)
ice_dpll_deinit(pf);
if (pf->eswitch_mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
xa_destroy(&pf->eswitch.reprs);
+ ice_hwmon_exit(pf);
}
static void ice_init_wakeup(struct ice_pf *pf)
@@ -5437,8 +5438,6 @@ static void ice_remove(struct pci_dev *pdev)
ice_free_vfs(pf);
}
- ice_hwmon_exit(pf);
-
if (!ice_is_safe_mode(pf))
ice_remove_arfs(pf);
@@ -6983,7 +6982,6 @@ void ice_update_vsi_stats(struct ice_vsi *vsi)
cur_ns->rx_errors = pf->stats.crc_errors +
pf->stats.illegal_bytes +
pf->stats.rx_undersize +
- pf->hw_csum_rx_error +
pf->stats.rx_jabber +
pf->stats.rx_fragments +
pf->stats.rx_oversize;
@@ -7989,6 +7987,34 @@ int ice_get_rss_key(struct ice_vsi *vsi, u8 *seed)
}
/**
+ * ice_get_rss - Get RSS LUT and/or key
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the key in
+ * @lut: Buffer to store the lookup table entries
+ * @lut_size: Size of buffer to store the lookup table entries
+ *
+ * Return: 0 on success, negative on failure
+ */
+int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ int err;
+
+ if (seed) {
+ err = ice_get_rss_key(vsi, seed);
+ if (err)
+ return err;
+ }
+
+ if (lut) {
+ err = ice_get_rss_lut(vsi, lut, lut_size);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
* ice_set_rss_hfunc - Set RSS HASH function
* @vsi: Pointer to VSI structure
* @hfunc: hash function (ICE_AQ_VSI_Q_OPT_RSS_*)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index 3e1052d070cf..0a8b50350b86 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -108,11 +108,11 @@ static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
ptp_read_system_prets(sts);
idpf_ptp_enable_shtime(adapter);
+ lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
/* Read the system timestamp post PHC read */
ptp_read_system_postts(sts);
- lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
spin_unlock(&ptp->read_dev_clk_lock);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index 7f3933ca9edc..f58f616d87fc 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -3941,7 +3941,7 @@ static void idpf_update_dim_sample(struct idpf_q_vector *q_vector,
static void idpf_net_dim(struct idpf_q_vector *q_vector)
{
struct dim_sample dim_sample = { };
- u64 packets, bytes;
+ u64 packets, bytes, pkts, bts;
u32 i;
if (!IDPF_ITR_IS_DYNAMIC(q_vector->tx_intr_mode))
@@ -3953,9 +3953,12 @@ static void idpf_net_dim(struct idpf_q_vector *q_vector)
do {
start = u64_stats_fetch_begin(&txq->stats_sync);
- packets += u64_stats_read(&txq->q_stats.packets);
- bytes += u64_stats_read(&txq->q_stats.bytes);
+ pkts = u64_stats_read(&txq->q_stats.packets);
+ bts = u64_stats_read(&txq->q_stats.bytes);
} while (u64_stats_fetch_retry(&txq->stats_sync, start));
+
+ packets += pkts;
+ bytes += bts;
}
idpf_update_dim_sample(q_vector, &dim_sample, &q_vector->tx_dim,
@@ -3972,9 +3975,12 @@ check_rx_itr:
do {
start = u64_stats_fetch_begin(&rxq->stats_sync);
- packets += u64_stats_read(&rxq->q_stats.packets);
- bytes += u64_stats_read(&rxq->q_stats.bytes);
+ pkts = u64_stats_read(&rxq->q_stats.packets);
+ bts = u64_stats_read(&rxq->q_stats.bytes);
} while (u64_stats_fetch_retry(&rxq->stats_sync, start));
+
+ packets += pkts;
+ bytes += bts;
}
idpf_update_dim_sample(q_vector, &dim_sample, &q_vector->rx_dim,
diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
index 498ba1522ca4..9482ab11f050 100644
--- a/drivers/net/ethernet/intel/igc/igc_defines.h
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h
@@ -443,9 +443,10 @@
#define IGC_TXPBSIZE_DEFAULT ( \
IGC_TXPB0SIZE(20) | IGC_TXPB1SIZE(0) | IGC_TXPB2SIZE(0) | \
IGC_TXPB3SIZE(0) | IGC_OS2BMCPBSIZE(4))
+/* TSN value following I225/I226 SW User Manual Section 7.5.4 */
#define IGC_TXPBSIZE_TSN ( \
- IGC_TXPB0SIZE(7) | IGC_TXPB1SIZE(7) | IGC_TXPB2SIZE(7) | \
- IGC_TXPB3SIZE(7) | IGC_OS2BMCPBSIZE(4))
+ IGC_TXPB0SIZE(5) | IGC_TXPB1SIZE(5) | IGC_TXPB2SIZE(5) | \
+ IGC_TXPB3SIZE(5) | IGC_OS2BMCPBSIZE(4))
#define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */
#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index e94c1922b97a..3172cdbca9cc 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -1565,8 +1565,8 @@ static int igc_ethtool_set_channels(struct net_device *netdev,
if (ch->other_count != NON_Q_VECTORS)
return -EINVAL;
- /* Do not allow channel reconfiguration when mqprio is enabled */
- if (adapter->strict_priority_enable)
+ /* Do not allow channel reconfiguration when any TSN qdisc is enabled */
+ if (adapter->flags & IGC_FLAG_TSN_ANY_ENABLED)
return -EINVAL;
/* Verify the number of channels doesn't exceed hw limits */
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 7aafa60ba0c8..89a321a344d2 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -7759,6 +7759,11 @@ int igc_reinit_queues(struct igc_adapter *adapter)
if (netif_running(netdev))
err = igc_open(netdev);
+ if (!err) {
+ /* Restore default IEEE 802.1Qbv schedule after queue reinit */
+ igc_tsn_clear_schedule(adapter);
+ }
+
return err;
}
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c
index b7b46d863bee..7aae83c108fd 100644
--- a/drivers/net/ethernet/intel/igc/igc_ptp.c
+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c
@@ -774,36 +774,43 @@ static void igc_ptp_tx_reg_to_stamp(struct igc_adapter *adapter,
static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
{
struct igc_hw *hw = &adapter->hw;
+ u32 txstmpl_old;
u64 regval;
u32 mask;
int i;
+ /* Establish baseline of TXSTMPL_0 before checking TXTT_0.
+ * This baseline is used to detect if a new timestamp arrives in
+ * register 0 during the hardware bug workaround below.
+ */
+ txstmpl_old = rd32(IGC_TXSTMPL);
+
mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY;
if (mask & IGC_TSYNCTXCTL_TXTT_0) {
regval = rd32(IGC_TXSTMPL);
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
} else {
- /* There's a bug in the hardware that could cause
- * missing interrupts for TX timestamping. The issue
- * is that for new interrupts to be triggered, the
- * IGC_TXSTMPH_0 register must be read.
+ /* TXTT_0 not set - register 0 has no new timestamp initially.
+ *
+ * Hardware bug: Future timestamp interrupts won't fire unless
+ * TXSTMPH_0 is read, even if the timestamp was captured in
+ * registers 1-3.
*
- * To avoid discarding a valid timestamp that just
- * happened at the "wrong" time, we need to confirm
- * that there was no timestamp captured, we do that by
- * assuming that no two timestamps in sequence have
- * the same nanosecond value.
+ * Workaround: Read TXSTMPH_0 here to enable future interrupts.
+ * However, this read clears TXTT_0. If a timestamp arrives in
+ * register 0 after checking TXTT_0 but before this read, it
+ * would be lost.
*
- * So, we read the "low" register, read the "high"
- * register (to latch a new timestamp) and read the
- * "low" register again, if "old" and "new" versions
- * of the "low" register are different, a valid
- * timestamp was captured, we can read the "high"
- * register again.
+ * To detect this race: We saved a baseline read of TXSTMPL_0
+ * before TXTT_0 check. After performing the workaround read of
+ * TXSTMPH_0, we read TXSTMPL_0 again. Since consecutive
+ * timestamps never share the same nanosecond value, a change
+ * between the baseline and new TXSTMPL_0 indicates a timestamp
+ * arrived during the race window. If so, read the complete
+ * timestamp.
*/
- u32 txstmpl_old, txstmpl_new;
+ u32 txstmpl_new;
- txstmpl_old = rd32(IGC_TXSTMPL);
rd32(IGC_TXSTMPH);
txstmpl_new = rd32(IGC_TXSTMPL);
@@ -818,7 +825,7 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
done:
/* Now that the problematic first register was handled, we can
- * use retrieve the timestamps from the other registers
+ * retrieve the timestamps from the other registers
* (starting from '1') with less complications.
*/
for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 034618e79169..c58051e4350b 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -11468,20 +11468,17 @@ static void ixgbe_set_fw_version(struct ixgbe_adapter *adapter)
*/
static int ixgbe_recovery_probe(struct ixgbe_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
struct ixgbe_hw *hw = &adapter->hw;
- bool disable_dev;
int err = -EIO;
if (hw->mac.type != ixgbe_mac_e610)
- goto clean_up_probe;
+ return err;
ixgbe_get_hw_control(adapter);
- mutex_init(&hw->aci.lock);
err = ixgbe_get_flash_data(&adapter->hw);
if (err)
- goto shutdown_aci;
+ goto err_release_hw_control;
timer_setup(&adapter->service_timer, ixgbe_service_timer, 0);
INIT_WORK(&adapter->service_task, ixgbe_recovery_service_task);
@@ -11504,16 +11501,8 @@ static int ixgbe_recovery_probe(struct ixgbe_adapter *adapter)
devl_unlock(adapter->devlink);
return 0;
-shutdown_aci:
- mutex_destroy(&adapter->hw.aci.lock);
+err_release_hw_control:
ixgbe_release_hw_control(adapter);
-clean_up_probe:
- disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state);
- free_netdev(netdev);
- devlink_free(adapter->devlink);
- pci_release_mem_regions(pdev);
- if (disable_dev)
- pci_disable_device(pdev);
return err;
}
@@ -11655,8 +11644,13 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_sw_init;
- if (ixgbe_check_fw_error(adapter))
- return ixgbe_recovery_probe(adapter);
+ if (ixgbe_check_fw_error(adapter)) {
+ err = ixgbe_recovery_probe(adapter);
+ if (err)
+ goto err_sw_init;
+
+ return 0;
+ }
if (adapter->hw.mac.type == ixgbe_mac_e610) {
err = ixgbe_get_caps(&adapter->hw);
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
index 44b201817d94..c116da7d7f18 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
@@ -1389,7 +1389,7 @@ int mvpp2_ethtool_cls_rule_ins(struct mvpp2_port *port,
efs->rule.flow_type = mvpp2_cls_ethtool_flow_to_type(info->fs.flow_type);
if (efs->rule.flow_type < 0) {
ret = efs->rule.flow_type;
- goto clean_rule;
+ goto clean_eth_rule;
}
ret = mvpp2_cls_rfs_parse_rule(&efs->rule);
diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c
index bcea3fc26a8c..57db7ea2f5be 100644
--- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c
+++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c
@@ -1338,7 +1338,7 @@ int octep_device_setup(struct octep_device *oct)
ret = octep_ctrl_net_init(oct);
if (ret)
- return ret;
+ goto unsupported_dev;
INIT_WORK(&oct->tx_timeout_task, octep_tx_timeout_task);
INIT_WORK(&oct->ctrl_mbox_task, octep_ctrl_mbox_task);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index 2d78e08f985f..747fbdf2a908 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -1551,8 +1551,8 @@ static int rvu_get_attach_blkaddr(struct rvu *rvu, int blktype,
return -ENODEV;
}
-static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
- int num_lfs, struct rsrc_attach *attach)
+static int rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
+ int num_lfs, struct rsrc_attach *attach)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
struct rvu_hwinfo *hw = rvu->hw;
@@ -1562,21 +1562,21 @@ static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
u64 cfg;
if (!num_lfs)
- return;
+ return -EINVAL;
blkaddr = rvu_get_attach_blkaddr(rvu, blktype, pcifunc, attach);
if (blkaddr < 0)
- return;
+ return -EFAULT;
block = &hw->block[blkaddr];
if (!block->lf.bmap)
- return;
+ return -ESRCH;
for (slot = 0; slot < num_lfs; slot++) {
/* Allocate the resource */
lf = rvu_alloc_rsrc(&block->lf);
if (lf < 0)
- return;
+ return -EFAULT;
cfg = (1ULL << 63) | (pcifunc << 8) | slot;
rvu_write64(rvu, blkaddr, block->lfcfg_reg |
@@ -1587,6 +1587,8 @@ static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
/* Set start MSIX vector for this LF within this PF/VF */
rvu_set_msix_offset(rvu, pfvf, block, lf);
}
+
+ return 0;
}
static int rvu_check_rsrc_availability(struct rvu *rvu,
@@ -1724,22 +1726,31 @@ int rvu_mbox_handler_attach_resources(struct rvu *rvu,
int err;
/* If first request, detach all existing attached resources */
- if (!attach->modify)
- rvu_detach_rsrcs(rvu, NULL, pcifunc);
+ if (!attach->modify) {
+ err = rvu_detach_rsrcs(rvu, NULL, pcifunc);
+ if (err)
+ return err;
+ }
mutex_lock(&rvu->rsrc_lock);
/* Check if the request can be accommodated */
err = rvu_check_rsrc_availability(rvu, attach, pcifunc);
if (err)
- goto exit;
+ goto fail1;
/* Now attach the requested resources */
- if (attach->npalf)
- rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1, attach);
+ if (attach->npalf) {
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1, attach);
+ if (err)
+ goto fail1;
+ }
- if (attach->nixlf)
- rvu_attach_block(rvu, pcifunc, BLKTYPE_NIX, 1, attach);
+ if (attach->nixlf) {
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_NIX, 1, attach);
+ if (err)
+ goto fail2;
+ }
if (attach->sso) {
/* RVU func doesn't know which exact LF or slot is attached
@@ -1749,33 +1760,64 @@ int rvu_mbox_handler_attach_resources(struct rvu *rvu,
*/
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_SSO);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_SSO,
- attach->sso, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_SSO,
+ attach->sso, attach);
+ if (err)
+ goto fail3;
}
if (attach->ssow) {
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_SSOW);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_SSOW,
- attach->ssow, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_SSOW,
+ attach->ssow, attach);
+ if (err)
+ goto fail4;
}
if (attach->timlfs) {
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_TIM);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_TIM,
- attach->timlfs, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_TIM,
+ attach->timlfs, attach);
+ if (err)
+ goto fail5;
}
if (attach->cptlfs) {
if (attach->modify &&
rvu_attach_from_same_block(rvu, BLKTYPE_CPT, attach))
rvu_detach_block(rvu, pcifunc, BLKTYPE_CPT);
- rvu_attach_block(rvu, pcifunc, BLKTYPE_CPT,
- attach->cptlfs, attach);
+ err = rvu_attach_block(rvu, pcifunc, BLKTYPE_CPT,
+ attach->cptlfs, attach);
+ if (err)
+ goto fail6;
}
-exit:
+ mutex_unlock(&rvu->rsrc_lock);
+ return 0;
+
+fail6:
+ if (attach->timlfs)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_TIM);
+
+fail5:
+ if (attach->ssow)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_SSOW);
+
+fail4:
+ if (attach->sso)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_SSO);
+
+fail3:
+ if (attach->nixlf)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_NIX);
+
+fail2:
+ if (attach->npalf)
+ rvu_detach_block(rvu, pcifunc, BLKTYPE_NPA);
+
+fail1:
mutex_unlock(&rvu->rsrc_lock);
return err;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
index 3abd750a4bd7..3d91a34f8b57 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
@@ -1222,6 +1222,9 @@ int rvu_mbox_handler_cgx_set_link_mode(struct rvu *rvu,
u8 cgx_idx, lmac;
void *cgxd;
+ if (!rvu->fwdata)
+ return LMAC_AF_ERR_FIRMWARE_DATA_NOT_MAPPED;
+
if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc))
return -EPERM;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c
index e4a5f9fa6fd4..bbfd8231aed5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c
@@ -56,7 +56,7 @@ int rvu_sdp_init(struct rvu *rvu)
struct rvu_pfvf *pfvf;
u32 i = 0;
- if (rvu->fwdata->channel_data.valid) {
+ if (rvu->fwdata && rvu->fwdata->channel_data.valid) {
sdp_pf_num[0] = 0;
pfvf = &rvu->pf[sdp_pf_num[0]];
pfvf->sdp_info = &rvu->fwdata->channel_data.info;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c
index 4c7e0f345cb5..060c715ebad0 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c
@@ -328,7 +328,7 @@ static int cn10k_mcs_write_rx_flowid(struct otx2_nic *pfvf,
req->data[0] = FIELD_PREP(MCS_TCAM0_MAC_DA_MASK, mac_da);
req->mask[0] = ~0ULL;
- req->mask[0] = ~MCS_TCAM0_MAC_DA_MASK;
+ req->mask[0] &= ~MCS_TCAM0_MAC_DA_MASK;
req->data[1] = FIELD_PREP(MCS_TCAM1_ETYPE_MASK, ETH_P_MACSEC);
req->mask[1] = ~0ULL;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index e616a727a3a9..8cdfc36d79d2 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -940,13 +940,8 @@ static inline dma_addr_t otx2_dma_map_page(struct otx2_nic *pfvf,
size_t offset, size_t size,
enum dma_data_direction dir)
{
- dma_addr_t iova;
-
- iova = dma_map_page_attrs(pfvf->dev, page,
+ return dma_map_page_attrs(pfvf->dev, page,
offset, size, dir, DMA_ATTR_SKIP_CPU_SYNC);
- if (unlikely(dma_mapping_error(pfvf->dev, iova)))
- return (dma_addr_t)NULL;
- return iova;
}
static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index a7feb4c392b3..6b2d8559f0eb 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -3249,7 +3249,9 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
netdev->watchdog_timeo = OTX2_TX_TIMEOUT;
netdev->netdev_ops = &otx2_netdev_ops;
- netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT;
+ netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
+ NETDEV_XDP_ACT_NDO_XMIT |
+ NETDEV_XDP_ACT_XSK_ZEROCOPY;
netdev->min_mtu = OTX2_MIN_MTU;
netdev->max_mtu = otx2_get_max_mtu(pf);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
index 36806e813c33..1301c56e20d6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
@@ -613,3 +613,19 @@ void mlx5_debug_cq_remove(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
cq->dbg = NULL;
}
}
+
+static int vhca_id_show(struct seq_file *file, void *priv)
+{
+ struct mlx5_core_dev *dev = file->private;
+
+ seq_printf(file, "0x%x\n", MLX5_CAP_GEN(dev, vhca_id));
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(vhca_id);
+
+void mlx5_vhca_debugfs_init(struct mlx5_core_dev *dev)
+{
+ debugfs_create_file("vhca_id", 0400, dev->priv.dbg.dbg_root, dev,
+ &vhca_id_fops);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
index 64c04f52990f..781e39b5aa1d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -575,3 +575,17 @@ bool mlx5_same_hw_devs(struct mlx5_core_dev *dev, struct mlx5_core_dev *peer_dev
return plen && flen && flen == plen &&
!memcmp(fsystem_guid, psystem_guid, flen);
}
+
+void mlx5_core_reps_aux_devs_remove(struct mlx5_core_dev *dev)
+{
+ struct mlx5_priv *priv = &dev->priv;
+
+ if (priv->adev[MLX5_INTERFACE_PROTOCOL_ETH])
+ device_lock_assert(&priv->adev[MLX5_INTERFACE_PROTOCOL_ETH]->adev.dev);
+ else
+ mlx5_core_err(dev, "ETH driver already removed\n");
+ if (priv->adev[MLX5_INTERFACE_PROTOCOL_IB_REP])
+ del_adev(&priv->adev[MLX5_INTERFACE_PROTOCOL_IB_REP]->adev);
+ if (priv->adev[MLX5_INTERFACE_PROTOCOL_ETH_REP])
+ del_adev(&priv->adev[MLX5_INTERFACE_PROTOCOL_ETH_REP]->adev);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
index a8fb4bec369c..9c7064187ed0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -430,7 +430,8 @@ void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
attrs->replay_esn.esn = sa_entry->esn_state.esn;
attrs->replay_esn.esn_msb = sa_entry->esn_state.esn_msb;
attrs->replay_esn.overlap = sa_entry->esn_state.overlap;
- if (attrs->dir == XFRM_DEV_OFFLOAD_OUT)
+ if (attrs->dir == XFRM_DEV_OFFLOAD_OUT ||
+ x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
goto skip_replay_window;
switch (x->replay_esn->replay_window) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
index c17ea0fcd8ef..ef7f5338540f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
@@ -177,8 +177,6 @@ bool mlx5e_psp_handle_tx_skb(struct net_device *netdev,
{
struct mlx5e_priv *priv = netdev_priv(netdev);
struct net *net = sock_net(skb->sk);
- const struct ipv6hdr *ip6;
- struct tcphdr *th;
if (!mlx5e_psp_set_state(priv, skb, psp_st))
return true;
@@ -190,11 +188,18 @@ bool mlx5e_psp_handle_tx_skb(struct net_device *netdev,
return false;
}
if (skb_is_gso(skb)) {
- ip6 = ipv6_hdr(skb);
- th = inner_tcp_hdr(skb);
+ int len = skb_shinfo(skb)->gso_size + inner_tcp_hdrlen(skb);
+ struct tcphdr *th = inner_tcp_hdr(skb);
- th->check = ~tcp_v6_check(skb_shinfo(skb)->gso_size + inner_tcp_hdrlen(skb), &ip6->saddr,
- &ip6->daddr, 0);
+ if (skb->protocol == htons(ETH_P_IP)) {
+ const struct iphdr *ip = ip_hdr(skb);
+
+ th->check = ~tcp_v4_check(len, ip->saddr, ip->daddr, 0);
+ } else {
+ const struct ipv6hdr *ip6 = ipv6_hdr(skb);
+
+ th->check = ~tcp_v6_check(len, &ip6->saddr, &ip6->daddr, 0);
+ }
}
return true;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 9042c8a388e4..4b2963bbe7ff 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -4052,6 +4052,8 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
mlx5e_queue_update_stats(priv);
}
+ netdev_stats_to_stats64(stats, &dev->stats);
+
if (mlx5e_is_uplink_rep(priv)) {
struct mlx5e_vport_stats *vstats = &priv->stats.vport;
@@ -4068,21 +4070,21 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
mlx5e_fold_sw_stats64(priv, stats);
}
- stats->rx_missed_errors = priv->stats.qcnt.rx_out_of_buffer;
- stats->rx_dropped = PPORT_2863_GET(pstats, if_in_discards);
+ stats->rx_missed_errors += priv->stats.qcnt.rx_out_of_buffer;
+ stats->rx_dropped += PPORT_2863_GET(pstats, if_in_discards);
- stats->rx_length_errors =
+ stats->rx_length_errors +=
PPORT_802_3_GET(pstats, a_in_range_length_errors) +
PPORT_802_3_GET(pstats, a_out_of_range_length_field) +
PPORT_802_3_GET(pstats, a_frame_too_long_errors) +
VNIC_ENV_GET(&priv->stats.vnic, eth_wqe_too_small);
- stats->rx_crc_errors =
+ stats->rx_crc_errors +=
PPORT_802_3_GET(pstats, a_frame_check_sequence_errors);
- stats->rx_frame_errors = PPORT_802_3_GET(pstats, a_alignment_errors);
- stats->tx_aborted_errors = PPORT_2863_GET(pstats, if_out_discards);
- stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors +
- stats->rx_frame_errors;
- stats->tx_errors = stats->tx_aborted_errors + stats->tx_carrier_errors;
+ stats->rx_frame_errors += PPORT_802_3_GET(pstats, a_alignment_errors);
+ stats->tx_aborted_errors += PPORT_2863_GET(pstats, if_out_discards);
+ stats->rx_errors += stats->rx_length_errors + stats->rx_crc_errors +
+ stats->rx_frame_errors;
+ stats->tx_errors += stats->tx_aborted_errors + stats->tx_carrier_errors;
}
static void mlx5e_nic_set_rx_mode(struct mlx5e_priv *priv)
@@ -6842,6 +6844,7 @@ static void _mlx5e_remove(struct auxiliary_device *adev)
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5_core_dev *mdev = edev->mdev;
+ mlx5_eswitch_safe_aux_devs_remove(mdev);
mlx5_core_uplink_netdev_set(mdev, NULL);
if (priv->profile)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index a8773b2342c2..424786f489ec 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -2147,11 +2147,14 @@ static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow,
static void mlx5e_tc_del_fdb_peers_flow(struct mlx5e_tc_flow *flow)
{
+ struct mlx5_devcom_comp_dev *devcom;
+ struct mlx5_devcom_comp_dev *pos;
+ struct mlx5_eswitch *peer_esw;
int i;
- for (i = 0; i < MLX5_MAX_PORTS; i++) {
- if (i == mlx5_get_dev_index(flow->priv->mdev))
- continue;
+ devcom = flow->priv->mdev->priv.eswitch->devcom;
+ mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
+ i = mlx5_get_dev_index(peer_esw->dev);
mlx5e_tc_del_fdb_peer_flow(flow, i);
}
}
@@ -5513,12 +5516,16 @@ int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags)
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
{
+ struct mlx5_devcom_comp_dev *devcom;
+ struct mlx5_devcom_comp_dev *pos;
struct mlx5e_tc_flow *flow, *tmp;
+ struct mlx5_eswitch *peer_esw;
int i;
- for (i = 0; i < MLX5_MAX_PORTS; i++) {
- if (i == mlx5_get_dev_index(esw->dev))
- continue;
+ devcom = esw->devcom;
+
+ mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
+ i = mlx5_get_dev_index(peer_esw->dev);
list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i])
mlx5e_tc_del_fdb_peers_flow(flow);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
index 1c37098e09ea..49a637829c59 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
@@ -188,7 +188,7 @@ int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
if (IS_ERR(vport->ingress.acl)) {
err = PTR_ERR(vport->ingress.acl);
vport->ingress.acl = NULL;
- return err;
+ goto out;
}
err = esw_acl_ingress_lgcy_groups_create(esw, vport);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index ad1073f7b79f..714ad28e8445 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -929,6 +929,7 @@ int mlx5_esw_ipsec_vf_packet_offload_set(struct mlx5_eswitch *esw, struct mlx5_v
int mlx5_esw_ipsec_vf_packet_offload_supported(struct mlx5_core_dev *dev,
u16 vport_num);
bool mlx5_esw_host_functions_enabled(const struct mlx5_core_dev *dev);
+void mlx5_eswitch_safe_aux_devs_remove(struct mlx5_core_dev *dev);
#else /* CONFIG_MLX5_ESWITCH */
/* eswitch API stubs */
static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
@@ -1009,9 +1010,12 @@ mlx5_esw_host_functions_enabled(const struct mlx5_core_dev *dev)
static inline bool
mlx5_esw_vport_vhca_id(struct mlx5_eswitch *esw, u16 vportn, u16 *vhca_id)
{
- return -EOPNOTSUPP;
+ return false;
}
+static inline void
+mlx5_eswitch_safe_aux_devs_remove(struct mlx5_core_dev *dev) {}
+
#endif /* CONFIG_MLX5_ESWITCH */
#endif /* __MLX5_ESWITCH_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index ea94a727633f..02b7e474586d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -3981,6 +3981,32 @@ static bool mlx5_devlink_switchdev_active_mode_change(struct mlx5_eswitch *esw,
return true;
}
+#define MLX5_ESW_HOLD_TIMEOUT_MS 7000
+#define MLX5_ESW_HOLD_RETRY_DELAY_MS 500
+
+void mlx5_eswitch_safe_aux_devs_remove(struct mlx5_core_dev *dev)
+{
+ unsigned long timeout;
+ bool hold_esw = true;
+
+ /* Wait for any concurrent eswitch mode transition to complete. */
+ if (!mlx5_esw_hold(dev)) {
+ timeout = jiffies + msecs_to_jiffies(MLX5_ESW_HOLD_TIMEOUT_MS);
+ while (!mlx5_esw_hold(dev)) {
+ if (!time_before(jiffies, timeout)) {
+ hold_esw = false;
+ break;
+ }
+ msleep(MLX5_ESW_HOLD_RETRY_DELAY_MS);
+ }
+ }
+ if (hold_esw) {
+ if (mlx5_eswitch_mode(dev) == MLX5_ESWITCH_OFFLOADS)
+ mlx5_core_reps_aux_devs_remove(dev);
+ mlx5_esw_release(dev);
+ }
+}
+
int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
struct netlink_ext_ack *extack)
{
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
index ced747bef641..c348ee62cd3a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
@@ -1198,7 +1198,8 @@ int mlx5_fs_cmd_set_tx_flow_table_root(struct mlx5_core_dev *dev, u32 ft_id, boo
u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)] = {};
u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)] = {};
- if (disconnect && MLX5_CAP_FLOWTABLE_NIC_TX(dev, reset_root_to_default))
+ if (disconnect &&
+ !MLX5_CAP_FLOWTABLE_NIC_TX(dev, reset_root_to_default))
return -EOPNOTSUPP;
MLX5_SET(set_flow_table_root_in, in, opcode,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 4209da722f9a..55b4e0cceae2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1806,16 +1806,6 @@ err:
return -ENOMEM;
}
-static int vhca_id_show(struct seq_file *file, void *priv)
-{
- struct mlx5_core_dev *dev = file->private;
-
- seq_printf(file, "0x%x\n", MLX5_CAP_GEN(dev, vhca_id));
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(vhca_id);
-
static int mlx5_notifiers_init(struct mlx5_core_dev *dev)
{
int err;
@@ -1884,7 +1874,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
priv->numa_node = dev_to_node(mlx5_core_dma_dev(dev));
priv->dbg.dbg_root = debugfs_create_dir(dev_name(dev->device),
mlx5_debugfs_root);
- debugfs_create_file("vhca_id", 0400, priv->dbg.dbg_root, dev, &vhca_id_fops);
+
INIT_LIST_HEAD(&priv->traps);
err = mlx5_cmd_init(dev);
@@ -2022,6 +2012,8 @@ static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_init_one;
}
+ mlx5_vhca_debugfs_init(dev);
+
pci_save_state(pdev);
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index cfebc110c02f..f2d74382fb85 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -258,6 +258,7 @@ int mlx5_wait_for_pages(struct mlx5_core_dev *dev, int *pages);
void mlx5_cmd_flush(struct mlx5_core_dev *dev);
void mlx5_cq_debugfs_init(struct mlx5_core_dev *dev);
void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev);
+void mlx5_vhca_debugfs_init(struct mlx5_core_dev *dev);
int mlx5_query_pcam_reg(struct mlx5_core_dev *dev, u32 *pcam, u8 feature_group,
u8 access_reg_group);
@@ -290,6 +291,7 @@ int mlx5_register_device(struct mlx5_core_dev *dev);
void mlx5_unregister_device(struct mlx5_core_dev *dev);
void mlx5_dev_set_lightweight(struct mlx5_core_dev *dev);
bool mlx5_dev_is_lightweight(struct mlx5_core_dev *dev);
+void mlx5_core_reps_aux_devs_remove(struct mlx5_core_dev *dev);
void mlx5_fw_reporters_create(struct mlx5_core_dev *dev);
int mlx5_query_mtpps(struct mlx5_core_dev *dev, u32 *mtpps, u32 mtpps_size);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c
index b706f1486504..c45540fe7d9d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c
@@ -76,6 +76,7 @@ static int mlx5_sf_dev_probe(struct auxiliary_device *adev, const struct auxilia
goto init_one_err;
}
+ mlx5_vhca_debugfs_init(mdev);
return 0;
init_one_err:
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index 36af94a2e062..2794f75df8fc 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -1524,9 +1524,8 @@ static void rocker_world_port_post_fini(struct rocker_port *rocker_port)
{
struct rocker_world_ops *wops = rocker_port->rocker->wops;
- if (!wops->port_post_fini)
- return;
- wops->port_post_fini(rocker_port);
+ if (wops->port_post_fini)
+ wops->port_post_fini(rocker_port);
kfree(rocker_port->wpriv);
}
diff --git a/drivers/net/ethernet/sfc/mcdi_filters.c b/drivers/net/ethernet/sfc/mcdi_filters.c
index 6ef96292909a..3db589b90b68 100644
--- a/drivers/net/ethernet/sfc/mcdi_filters.c
+++ b/drivers/net/ethernet/sfc/mcdi_filters.c
@@ -2182,12 +2182,7 @@ int efx_mcdi_rx_pull_rss_context_config(struct efx_nic *efx,
int efx_mcdi_rx_pull_rss_config(struct efx_nic *efx)
{
- int rc;
-
- mutex_lock(&efx->net_dev->ethtool->rss_lock);
- rc = efx_mcdi_rx_pull_rss_context_config(efx, &efx->rss_context);
- mutex_unlock(&efx->net_dev->ethtool->rss_lock);
- return rc;
+ return efx_mcdi_rx_pull_rss_context_config(efx, &efx->rss_context);
}
void efx_mcdi_rx_restore_rss_contexts(struct efx_nic *efx)
diff --git a/drivers/net/ethernet/spacemit/k1_emac.c b/drivers/net/ethernet/spacemit/k1_emac.c
index 220eb5ce7583..88e9424d2d51 100644
--- a/drivers/net/ethernet/spacemit/k1_emac.c
+++ b/drivers/net/ethernet/spacemit/k1_emac.c
@@ -1099,7 +1099,13 @@ static int emac_read_stat_cnt(struct emac_priv *priv, u8 cnt, u32 *res,
100, 10000);
if (ret) {
- netdev_err(priv->ndev, "Read stat timeout\n");
+ /*
+ * This could be caused by the PHY stopping its refclk even when
+ * the link is up, for power saving. See also comments in
+ * emac_stats_update().
+ */
+ dev_err_ratelimited(&priv->ndev->dev,
+ "Read stat timeout. PHY clock stopped?\n");
return ret;
}
@@ -1147,17 +1153,25 @@ static void emac_stats_update(struct emac_priv *priv)
assert_spin_locked(&priv->stats_lock);
- if (!netif_running(priv->ndev) || !netif_device_present(priv->ndev)) {
- /* Not up, don't try to update */
+ /*
+ * We can't read statistics if the interface is not up. Also, some PHYs
+ * stop their reference clocks for link down power saving, which also
+ * causes reading statistics to time out. Don't update and don't
+ * reschedule in these cases.
+ */
+ if (!netif_running(priv->ndev) ||
+ !netif_carrier_ok(priv->ndev) ||
+ !netif_device_present(priv->ndev)) {
return;
}
for (i = 0; i < sizeof(priv->tx_stats) / sizeof(*tx_stats); i++) {
/*
- * If reading stats times out, everything is broken and there's
- * nothing we can do. Reading statistics also can't return an
- * error, so just return without updating and without
- * rescheduling.
+ * If reading stats times out anyway, the stat registers will be
+ * stuck, and we can't really recover from that.
+ *
+ * Reading statistics also can't return an error, so just return
+ * without updating and without rescheduling.
*/
if (emac_tx_read_stat_cnt(priv, i, &res))
return;
@@ -1636,6 +1650,12 @@ static void emac_adjust_link(struct net_device *dev)
emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
emac_set_fc_autoneg(priv);
+
+ /*
+ * Reschedule stats updates now that link is up. See comments in
+ * emac_stats_update().
+ */
+ mod_timer(&priv->stats_timer, jiffies);
}
phy_print_status(phydev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index b3730312aeed..3f42843cd9ed 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -4359,11 +4359,11 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned int first_entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct stmmac_tx_queue *tx_q;
+ bool set_ic, is_last_segment;
u32 pay_len, mss, queue;
int i, first_tx, nfrags;
u8 proto_hdr_len, hdr;
dma_addr_t des;
- bool set_ic;
/* Always insert VLAN tag to SKB payload for TSO frames.
*
@@ -4551,10 +4551,16 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_enable_tx_timestamp(priv, first);
}
+ /* If we only have one entry used, then the first entry is the last
+ * segment.
+ */
+ is_last_segment = ((tx_q->cur_tx - first_entry) &
+ (priv->dma_conf.dma_tx_size - 1)) == 1;
+
/* Complete the first descriptor before granting the DMA */
stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,
- tx_q->tx_skbuff_dma[first_entry].last_segment,
- hdr / 4, (skb->len - proto_hdr_len));
+ is_last_segment, hdr / 4,
+ skb->len - proto_hdr_len);
/* If context desc is used to change MSS */
if (mss_desc) {
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
index 62d7f47d4f8d..f0514251d4f3 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
@@ -70,7 +70,7 @@ int txgbe_test_hostif(struct wx *wx)
buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer),
- WX_HI_COMMAND_TIMEOUT, true);
+ WX_HI_COMMAND_TIMEOUT, false);
}
int txgbe_read_eeprom_hostif(struct wx *wx,
@@ -148,7 +148,7 @@ static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int
buffer.duplex = duplex;
return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer),
- WX_HI_COMMAND_TIMEOUT, true);
+ WX_HI_COMMAND_TIMEOUT, false);
}
static void txgbe_get_link_capabilities(struct wx *wx, int *speed,
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
index 50de3ee204db..80f84fc87008 100644
--- a/drivers/net/ipvlan/ipvlan.h
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -69,7 +69,6 @@ struct ipvl_dev {
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
netdev_features_t sfeatures;
u32 msg_enable;
- spinlock_t addrs_lock;
};
struct ipvl_addr {
@@ -90,6 +89,7 @@ struct ipvl_port {
struct net_device *dev;
possible_net_t pnet;
struct hlist_head hlhead[IPVLAN_HASH_SIZE];
+ spinlock_t addrs_lock; /* guards hash-table and addrs */
struct list_head ipvlans;
u16 mode;
u16 flags;
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index 2efa3ba148aa..bdb3a46b327c 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -107,17 +107,15 @@ void ipvlan_ht_addr_del(struct ipvl_addr *addr)
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
const void *iaddr, bool is_v6)
{
- struct ipvl_addr *addr, *ret = NULL;
+ struct ipvl_addr *addr;
- rcu_read_lock();
- list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) {
- if (addr_equal(is_v6, addr, iaddr)) {
- ret = addr;
- break;
- }
+ assert_spin_locked(&ipvlan->port->addrs_lock);
+
+ list_for_each_entry(addr, &ipvlan->addrs, anode) {
+ if (addr_equal(is_v6, addr, iaddr))
+ return addr;
}
- rcu_read_unlock();
- return ret;
+ return NULL;
}
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index 660f3db11766..baccdad695fd 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -75,6 +75,7 @@ static int ipvlan_port_create(struct net_device *dev)
for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++)
INIT_HLIST_HEAD(&port->hlhead[idx]);
+ spin_lock_init(&port->addrs_lock);
skb_queue_head_init(&port->backlog);
INIT_WORK(&port->wq, ipvlan_process_multicast);
ida_init(&port->ida);
@@ -181,6 +182,7 @@ static void ipvlan_uninit(struct net_device *dev)
static int ipvlan_open(struct net_device *dev)
{
struct ipvl_dev *ipvlan = netdev_priv(dev);
+ struct ipvl_port *port = ipvlan->port;
struct ipvl_addr *addr;
if (ipvlan->port->mode == IPVLAN_MODE_L3 ||
@@ -189,10 +191,10 @@ static int ipvlan_open(struct net_device *dev)
else
dev->flags &= ~IFF_NOARP;
- rcu_read_lock();
- list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
+ spin_lock_bh(&port->addrs_lock);
+ list_for_each_entry(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_add(ipvlan, addr);
- rcu_read_unlock();
+ spin_unlock_bh(&port->addrs_lock);
return 0;
}
@@ -206,10 +208,10 @@ static int ipvlan_stop(struct net_device *dev)
dev_uc_unsync(phy_dev, dev);
dev_mc_unsync(phy_dev, dev);
- rcu_read_lock();
- list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
+ spin_lock_bh(&ipvlan->port->addrs_lock);
+ list_for_each_entry(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_del(addr);
- rcu_read_unlock();
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return 0;
}
@@ -579,7 +581,6 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
if (!tb[IFLA_MTU])
ipvlan_adjust_mtu(ipvlan, phy_dev);
INIT_LIST_HEAD(&ipvlan->addrs);
- spin_lock_init(&ipvlan->addrs_lock);
/* TODO Probably put random address here to be presented to the
* world but keep using the physical-dev address for the outgoing
@@ -657,13 +658,13 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
struct ipvl_dev *ipvlan = netdev_priv(dev);
struct ipvl_addr *addr, *next;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
ipvlan_ht_addr_del(addr);
list_del_rcu(&addr->anode);
kfree_rcu(addr, rcu);
}
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
ida_free(&ipvlan->port->ida, dev->dev_id);
list_del_rcu(&ipvlan->pnode);
@@ -817,6 +818,8 @@ static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
{
struct ipvl_addr *addr;
+ assert_spin_locked(&ipvlan->port->addrs_lock);
+
addr = kzalloc(sizeof(struct ipvl_addr), GFP_ATOMIC);
if (!addr)
return -ENOMEM;
@@ -847,16 +850,16 @@ static void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
{
struct ipvl_addr *addr;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
addr = ipvlan_find_addr(ipvlan, iaddr, is_v6);
if (!addr) {
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return;
}
ipvlan_ht_addr_del(addr);
list_del_rcu(&addr->anode);
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
kfree_rcu(addr, rcu);
}
@@ -878,14 +881,14 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
{
int ret = -EINVAL;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true))
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv6=%pI6c addr for %s intf\n",
ip6_addr, ipvlan->dev->name);
else
ret = ipvlan_add_addr(ipvlan, ip6_addr, true);
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return ret;
}
@@ -924,21 +927,24 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr;
struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
struct ipvl_dev *ipvlan = netdev_priv(dev);
+ int ret = NOTIFY_OK;
if (!ipvlan_is_valid_dev(dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) {
NL_SET_ERR_MSG(i6vi->extack,
"Address already assigned to an ipvlan device");
- return notifier_from_errno(-EADDRINUSE);
+ ret = notifier_from_errno(-EADDRINUSE);
}
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
break;
}
- return NOTIFY_OK;
+ return ret;
}
#endif
@@ -946,14 +952,14 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
{
int ret = -EINVAL;
- spin_lock_bh(&ipvlan->addrs_lock);
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false))
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv4=%pI4 on %s intf.\n",
ip4_addr, ipvlan->dev->name);
else
ret = ipvlan_add_addr(ipvlan, ip4_addr, false);
- spin_unlock_bh(&ipvlan->addrs_lock);
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
return ret;
}
@@ -995,21 +1001,24 @@ static int ipvlan_addr4_validator_event(struct notifier_block *unused,
struct in_validator_info *ivi = (struct in_validator_info *)ptr;
struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev;
struct ipvl_dev *ipvlan = netdev_priv(dev);
+ int ret = NOTIFY_OK;
if (!ipvlan_is_valid_dev(dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
+ spin_lock_bh(&ipvlan->port->addrs_lock);
if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) {
NL_SET_ERR_MSG(ivi->extack,
"Address already assigned to an ipvlan device");
- return notifier_from_errno(-EADDRINUSE);
+ ret = notifier_from_errno(-EADDRINUSE);
}
+ spin_unlock_bh(&ipvlan->port->addrs_lock);
break;
}
- return NOTIFY_OK;
+ return ret;
}
static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c
index 49537d3c4120..5f17f68f3c08 100644
--- a/drivers/net/netdevsim/bpf.c
+++ b/drivers/net/netdevsim/bpf.c
@@ -244,7 +244,9 @@ static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev,
&state->state, &nsim_bpf_string_fops);
debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded);
+ mutex_lock(&nsim_dev->progs_list_lock);
list_add_tail(&state->l, &nsim_dev->bpf_bound_progs);
+ mutex_unlock(&nsim_dev->progs_list_lock);
prog->aux->offload->dev_priv = state;
@@ -273,12 +275,16 @@ static int nsim_bpf_translate(struct bpf_prog *prog)
static void nsim_bpf_destroy_prog(struct bpf_prog *prog)
{
struct nsim_bpf_bound_prog *state;
+ struct nsim_dev *nsim_dev;
state = prog->aux->offload->dev_priv;
+ nsim_dev = state->nsim_dev;
WARN(state->is_loaded,
"offload state destroyed while program still bound");
debugfs_remove_recursive(state->ddir);
+ mutex_lock(&nsim_dev->progs_list_lock);
list_del(&state->l);
+ mutex_unlock(&nsim_dev->progs_list_lock);
kfree(state);
}
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index 2683a989873e..dfd571b22107 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -1647,6 +1647,7 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
nsim_dev->test2 = NSIM_DEV_TEST2_DEFAULT;
spin_lock_init(&nsim_dev->fa_cookie_lock);
+ mutex_init(&nsim_dev->progs_list_lock);
dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
@@ -1785,6 +1786,7 @@ void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
devl_unregister(devlink);
kfree(nsim_dev->vfconfigs);
kfree(nsim_dev->fa_cookie);
+ mutex_destroy(&nsim_dev->progs_list_lock);
devl_unlock(devlink);
devlink_free(devlink);
dev_set_drvdata(&nsim_bus_dev->dev, NULL);
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index d1a941e2b18f..46c67983c517 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -324,6 +324,7 @@ struct nsim_dev {
u32 prog_id_gen;
struct list_head bpf_bound_progs;
struct list_head bpf_bound_maps;
+ struct mutex progs_list_lock;
struct netdev_phys_item_id switch_id;
struct list_head port_list;
bool fw_update_status;
diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c
index 149ddf51d785..87df3a9dfc9b 100644
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
@@ -93,12 +93,10 @@ static unsigned int mtk_pcs_lynxi_inband_caps(struct phylink_pcs *pcs,
{
switch (interface) {
case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_SGMII:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
- case PHY_INTERFACE_MODE_2500BASEX:
- return LINK_INBAND_DISABLE;
-
default:
return 0;
}
diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c
index 9766dd99afaa..12ff4c1f285d 100644
--- a/drivers/net/phy/intel-xway.c
+++ b/drivers/net/phy/intel-xway.c
@@ -277,7 +277,7 @@ static int xway_gphy_init_leds(struct phy_device *phydev)
static int xway_gphy_config_init(struct phy_device *phydev)
{
- struct device_node *np = phydev->mdio.dev.of_node;
+ struct device_node *np;
int err;
/* Mask all interrupts */
@@ -286,7 +286,10 @@ static int xway_gphy_config_init(struct phy_device *phydev)
return err;
/* Use default LED configuration if 'leds' node isn't defined */
- if (!of_get_child_by_name(np, "leds"))
+ np = of_get_child_by_name(phydev->mdio.dev.of_node, "leds");
+ if (np)
+ of_node_put(np);
+ else
xway_gphy_init_leds(phydev);
/* Clear all pending interrupts */
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 05de68b9f719..8208ecbb575c 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -2643,11 +2643,21 @@ static int kszphy_probe(struct phy_device *phydev)
kszphy_parse_led_mode(phydev);
- clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, "rmii-ref");
+ clk = devm_clk_get_optional(&phydev->mdio.dev, "rmii-ref");
/* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
if (!IS_ERR_OR_NULL(clk)) {
- unsigned long rate = clk_get_rate(clk);
bool rmii_ref_clk_sel_25_mhz;
+ unsigned long rate;
+ int err;
+
+ err = clk_prepare_enable(clk);
+ if (err) {
+ phydev_err(phydev, "Failed to enable rmii-ref clock\n");
+ return err;
+ }
+
+ rate = clk_get_rate(clk);
+ clk_disable_unprepare(clk);
if (type)
priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
@@ -2665,13 +2675,12 @@ static int kszphy_probe(struct phy_device *phydev)
}
} else if (!clk) {
/* unnamed clock from the generic ethernet-phy binding */
- clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, NULL);
+ clk = devm_clk_get_optional(&phydev->mdio.dev, NULL);
}
if (IS_ERR(clk))
return PTR_ERR(clk);
- clk_disable_unprepare(clk);
priv->clk = clk;
if (ksz8041_fiber_mode(phydev))
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 84bef5099dda..47f095bd91ce 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -519,6 +519,8 @@ static const struct sfp_quirk sfp_quirks[] = {
SFP_QUIRK_F("HALNy", "HL-GSFP", sfp_fixup_halny_gsfp),
+ SFP_QUIRK_F("H-COM", "SPP425H-GAB4", sfp_fixup_potron),
+
// HG MXPD-483II-F 2.5G supports 2500Base-X, but incorrectly reports
// 2600MBd in their EERPOM
SFP_QUIRK_S("HG GENUINE", "MXPD-483II", sfp_quirk_2500basex),
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index 8b6d6a1b3c2e..2b4716ccf0c5 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -604,10 +604,6 @@ static const struct usb_device_id products[] = {
.driver_info = (unsigned long)&dm9601_info,
},
{
- USB_DEVICE(0x0fe6, 0x9700), /* DM9601 USB to Fast Ethernet Adapter */
- .driver_info = (unsigned long)&dm9601_info,
- },
- {
USB_DEVICE(0x0a46, 0x9000), /* DM9000E */
.driver_info = (unsigned long)&dm9601_info,
},
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 36742e64cff7..9280ef544bbb 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1821,9 +1821,12 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
if ((dev->driver_info->flags & FLAG_NOARP) != 0)
net->flags |= IFF_NOARP;
- /* maybe the remote can't receive an Ethernet MTU */
- if (net->mtu > (dev->hard_mtu - net->hard_header_len))
- net->mtu = dev->hard_mtu - net->hard_header_len;
+ if (net->max_mtu > (dev->hard_mtu - net->hard_header_len))
+ net->max_mtu = dev->hard_mtu - net->hard_header_len;
+
+ if (net->mtu > net->max_mtu)
+ net->mtu = net->max_mtu;
+
} else if (!info->in || !info->out)
status = usbnet_get_endpoints(dev, udev);
else {
@@ -1984,6 +1987,7 @@ int usbnet_resume(struct usb_interface *intf)
} else {
netif_trans_update(dev->net);
__skb_queue_tail(&dev->txq, skb);
+ netdev_sent_queue(dev->net, skb->len);
}
}
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 14e6f2a2fb77..9982412fd7f2 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -228,16 +228,20 @@ static void veth_get_ethtool_stats(struct net_device *dev,
const struct veth_rq_stats *rq_stats = &rcv_priv->rq[i].stats;
const void *base = (void *)&rq_stats->vs;
unsigned int start, tx_idx = idx;
+ u64 buf[VETH_TQ_STATS_LEN];
size_t offset;
- tx_idx += (i % dev->real_num_tx_queues) * VETH_TQ_STATS_LEN;
do {
start = u64_stats_fetch_begin(&rq_stats->syncp);
for (j = 0; j < VETH_TQ_STATS_LEN; j++) {
offset = veth_tq_stats_desc[j].offset;
- data[tx_idx + j] += *(u64 *)(base + offset);
+ buf[j] = *(u64 *)(base + offset);
}
} while (u64_stats_fetch_retry(&rq_stats->syncp, start));
+
+ tx_idx += (i % dev->real_num_tx_queues) * VETH_TQ_STATS_LEN;
+ for (j = 0; j < VETH_TQ_STATS_LEN; j++)
+ data[tx_idx + j] += buf[j];
}
pp_idx = idx + dev->real_num_tx_queues * VETH_TQ_STATS_LEN;
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index 7bbda46cfd93..82f120ee1c66 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -1727,8 +1727,8 @@ static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
(ce_state->src_ring->nentries *
sizeof(struct ce_desc) +
CE_DESC_RING_ALIGN),
- ce_state->src_ring->base_addr_owner_space,
- ce_state->src_ring->base_addr_ce_space);
+ ce_state->src_ring->base_addr_owner_space_unaligned,
+ ce_state->src_ring->base_addr_ce_space_unaligned);
kfree(ce_state->src_ring);
}
@@ -1737,8 +1737,8 @@ static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
(ce_state->dest_ring->nentries *
sizeof(struct ce_desc) +
CE_DESC_RING_ALIGN),
- ce_state->dest_ring->base_addr_owner_space,
- ce_state->dest_ring->base_addr_ce_space);
+ ce_state->dest_ring->base_addr_owner_space_unaligned,
+ ce_state->dest_ring->base_addr_ce_space_unaligned);
kfree(ce_state->dest_ring);
}
@@ -1758,8 +1758,8 @@ static void _ath10k_ce_free_pipe_64(struct ath10k *ar, int ce_id)
(ce_state->src_ring->nentries *
sizeof(struct ce_desc_64) +
CE_DESC_RING_ALIGN),
- ce_state->src_ring->base_addr_owner_space,
- ce_state->src_ring->base_addr_ce_space);
+ ce_state->src_ring->base_addr_owner_space_unaligned,
+ ce_state->src_ring->base_addr_ce_space_unaligned);
kfree(ce_state->src_ring);
}
@@ -1768,8 +1768,8 @@ static void _ath10k_ce_free_pipe_64(struct ath10k *ar, int ce_id)
(ce_state->dest_ring->nentries *
sizeof(struct ce_desc_64) +
CE_DESC_RING_ALIGN),
- ce_state->dest_ring->base_addr_owner_space,
- ce_state->dest_ring->base_addr_ce_space);
+ ce_state->dest_ring->base_addr_owner_space_unaligned,
+ ce_state->dest_ring->base_addr_ce_space_unaligned);
kfree(ce_state->dest_ring);
}
diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c
index 9a63608838ac..4aea58446838 100644
--- a/drivers/net/wireless/ath/ath12k/ce.c
+++ b/drivers/net/wireless/ath/ath12k/ce.c
@@ -984,8 +984,8 @@ void ath12k_ce_free_pipes(struct ath12k_base *ab)
dma_free_coherent(ab->dev,
pipe->src_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
- pipe->src_ring->base_addr_owner_space,
- pipe->src_ring->base_addr_ce_space);
+ pipe->src_ring->base_addr_owner_space_unaligned,
+ pipe->src_ring->base_addr_ce_space_unaligned);
kfree(pipe->src_ring);
pipe->src_ring = NULL;
}
@@ -995,8 +995,8 @@ void ath12k_ce_free_pipes(struct ath12k_base *ab)
dma_free_coherent(ab->dev,
pipe->dest_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
- pipe->dest_ring->base_addr_owner_space,
- pipe->dest_ring->base_addr_ce_space);
+ pipe->dest_ring->base_addr_owner_space_unaligned,
+ pipe->dest_ring->base_addr_ce_space_unaligned);
kfree(pipe->dest_ring);
pipe->dest_ring = NULL;
}
@@ -1007,8 +1007,8 @@ void ath12k_ce_free_pipes(struct ath12k_base *ab)
dma_free_coherent(ab->dev,
pipe->status_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
- pipe->status_ring->base_addr_owner_space,
- pipe->status_ring->base_addr_ce_space);
+ pipe->status_ring->base_addr_owner_space_unaligned,
+ pipe->status_ring->base_addr_ce_space_unaligned);
kfree(pipe->status_ring);
pipe->status_ring = NULL;
}
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index f7a2a544bef2..e0e49f782bf8 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -5495,7 +5495,8 @@ static void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw,
for_each_set_bit(link_id, &links_map, ATH12K_NUM_MAX_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
- if (!arvif || arvif->is_started)
+ if (!arvif || !arvif->is_created ||
+ arvif->ar->scan.arvif != arvif)
continue;
ar = arvif->ar;
@@ -9172,7 +9173,10 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
return;
}
} else {
- link_id = 0;
+ if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ link_id = ATH12K_FIRST_SCAN_LINK;
+ else
+ link_id = 0;
}
arvif = rcu_dereference(ahvif->link[link_id]);
@@ -12142,6 +12146,9 @@ static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *v
if (drop)
return;
+ for_each_ar(ah, ar, i)
+ wiphy_work_flush(hw->wiphy, &ar->wmi_mgmt_tx_work);
+
/* vif can be NULL when flush() is considered for hw */
if (!vif) {
for_each_ar(ah, ar, i)
@@ -12149,9 +12156,6 @@ static void ath12k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *v
return;
}
- for_each_ar(ah, ar, i)
- wiphy_work_flush(hw->wiphy, &ar->wmi_mgmt_tx_work);
-
ahvif = ath12k_vif_to_ahvif(vif);
links = ahvif->links_map;
for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
@@ -13343,7 +13347,7 @@ static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
ath12k_scan_abort(ar);
cancel_delayed_work_sync(&ar->scan.timeout);
- wiphy_work_cancel(hw->wiphy, &ar->scan.vdev_clean_wk);
+ wiphy_work_flush(hw->wiphy, &ar->scan.vdev_clean_wk);
return 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index be8b2943094f..3ce5fcb0e460 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -6575,16 +6575,9 @@ static int freq_to_idx(struct ath12k *ar, int freq)
if (!sband)
continue;
- for (ch = 0; ch < sband->n_channels; ch++, idx++) {
- if (sband->channels[ch].center_freq <
- KHZ_TO_MHZ(ar->freq_range.start_freq) ||
- sband->channels[ch].center_freq >
- KHZ_TO_MHZ(ar->freq_range.end_freq))
- continue;
-
+ for (ch = 0; ch < sband->n_channels; ch++, idx++)
if (sband->channels[ch].center_freq == freq)
goto exit;
- }
}
exit:
diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
index 354c5ce66045..f3397dc6c422 100644
--- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
@@ -825,7 +825,7 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
bool coex_flag)
{
- u8 i;
+ u8 i, j;
u32 rx_win_size;
struct mwifiex_private *priv;
@@ -863,8 +863,8 @@ static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
if (rx_win_size != priv->add_ba_param.rx_win_size) {
if (!priv->media_connected)
continue;
- for (i = 0; i < MAX_NUM_TID; i++)
- mwifiex_11n_delba(priv, i);
+ for (j = 0; j < MAX_NUM_TID; j++)
+ mwifiex_11n_delba(priv, j);
}
}
}
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index f3a853edfc11..8c8e074a3a70 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -2035,6 +2035,7 @@ int rsi_mac80211_attach(struct rsi_common *common)
hw->queues = MAX_HW_QUEUES;
hw->extra_tx_headroom = RSI_NEEDED_HEADROOM;
+ hw->vif_data_size = sizeof(struct vif_priv);
hw->max_rates = 1;
hw->max_rate_tries = MAX_RETRIES;
diff --git a/drivers/net/wwan/mhi_wwan_mbim.c b/drivers/net/wwan/mhi_wwan_mbim.c
index 0dace12f5ad0..f8bc9a39bfa3 100644
--- a/drivers/net/wwan/mhi_wwan_mbim.c
+++ b/drivers/net/wwan/mhi_wwan_mbim.c
@@ -78,9 +78,8 @@ struct mhi_mbim_context {
struct mbim_tx_hdr {
struct usb_cdc_ncm_nth16 nth16;
-
- /* Must be last as it ends in a flexible-array member. */
struct usb_cdc_ncm_ndp16 ndp16;
+ struct usb_cdc_ncm_dpe16 dpe16[2];
} __packed;
static struct mhi_mbim_link *mhi_mbim_get_link_rcu(struct mhi_mbim_context *mbim,
@@ -108,20 +107,20 @@ static int mhi_mbim_get_link_mux_id(struct mhi_controller *cntrl)
static struct sk_buff *mbim_tx_fixup(struct sk_buff *skb, unsigned int session,
u16 tx_seq)
{
- DEFINE_RAW_FLEX(struct mbim_tx_hdr, mbim_hdr, ndp16.dpe16, 2);
unsigned int dgram_size = skb->len;
struct usb_cdc_ncm_nth16 *nth16;
struct usb_cdc_ncm_ndp16 *ndp16;
+ struct mbim_tx_hdr *mbim_hdr;
/* Only one NDP is sent, containing the IP packet (no aggregation) */
/* Ensure we have enough headroom for crafting MBIM header */
- if (skb_cow_head(skb, __struct_size(mbim_hdr))) {
+ if (skb_cow_head(skb, sizeof(struct mbim_tx_hdr))) {
dev_kfree_skb_any(skb);
return NULL;
}
- mbim_hdr = skb_push(skb, __struct_size(mbim_hdr));
+ mbim_hdr = skb_push(skb, sizeof(struct mbim_tx_hdr));
/* Fill NTB header */
nth16 = &mbim_hdr->nth16;
@@ -134,11 +133,12 @@ static struct sk_buff *mbim_tx_fixup(struct sk_buff *skb, unsigned int session,
/* Fill the unique NDP */
ndp16 = &mbim_hdr->ndp16;
ndp16->dwSignature = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN | (session << 24));
- ndp16->wLength = cpu_to_le16(struct_size(ndp16, dpe16, 2));
+ ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16)
+ + sizeof(struct usb_cdc_ncm_dpe16) * 2);
ndp16->wNextNdpIndex = 0;
/* Datagram follows the mbim header */
- ndp16->dpe16[0].wDatagramIndex = cpu_to_le16(__struct_size(mbim_hdr));
+ ndp16->dpe16[0].wDatagramIndex = cpu_to_le16(sizeof(struct mbim_tx_hdr));
ndp16->dpe16[0].wDatagramLength = cpu_to_le16(dgram_size);
/* null termination */
@@ -584,8 +584,7 @@ static void mhi_mbim_setup(struct net_device *ndev)
{
ndev->header_ops = NULL; /* No header */
ndev->type = ARPHRD_RAWIP;
- ndev->needed_headroom =
- struct_size_t(struct mbim_tx_hdr, ndp16.dpe16, 2);
+ ndev->needed_headroom = sizeof(struct mbim_tx_hdr);
ndev->hard_header_len = 0;
ndev->addr_len = 0;
ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
index b76bea6ab2d7..5af90ca6e063 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
@@ -395,6 +395,7 @@ static int t7xx_dpmaif_set_frag_to_skb(const struct dpmaif_rx_queue *rxq,
struct sk_buff *skb)
{
unsigned long long data_bus_addr, data_base_addr;
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
struct device *dev = rxq->dpmaif_ctrl->dev;
struct dpmaif_bat_page *page_info;
unsigned int data_len;
@@ -402,18 +403,22 @@ static int t7xx_dpmaif_set_frag_to_skb(const struct dpmaif_rx_queue *rxq,
page_info = rxq->bat_frag->bat_skb;
page_info += t7xx_normal_pit_bid(pkt_info);
- dma_unmap_page(dev, page_info->data_bus_addr, page_info->data_len, DMA_FROM_DEVICE);
if (!page_info->page)
return -EINVAL;
+ if (shinfo->nr_frags >= MAX_SKB_FRAGS)
+ return -EINVAL;
+
+ dma_unmap_page(dev, page_info->data_bus_addr, page_info->data_len, DMA_FROM_DEVICE);
+
data_bus_addr = le32_to_cpu(pkt_info->pd.data_addr_h);
data_bus_addr = (data_bus_addr << 32) + le32_to_cpu(pkt_info->pd.data_addr_l);
data_base_addr = page_info->data_bus_addr;
data_offset = data_bus_addr - data_base_addr;
data_offset += page_info->offset;
data_len = FIELD_GET(PD_PIT_DATA_LEN, le32_to_cpu(pkt_info->header));
- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page,
+ skb_add_rx_frag(skb, shinfo->nr_frags, page_info->page,
data_offset, data_len, page_info->data_len);
page_info->page = NULL;
diff --git a/drivers/nfc/virtual_ncidev.c b/drivers/nfc/virtual_ncidev.c
index 9ef8ef2d4363..b957fce83b7c 100644
--- a/drivers/nfc/virtual_ncidev.c
+++ b/drivers/nfc/virtual_ncidev.c
@@ -125,10 +125,6 @@ static ssize_t virtual_ncidev_write(struct file *file,
kfree_skb(skb);
return -EFAULT;
}
- if (strnlen(skb->data, count) != count) {
- kfree_skb(skb);
- return -EINVAL;
- }
nci_recv_frame(vdev->ndev, skb);
return count;
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index eb875e3db2e3..71d4bb25f7fd 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1394,6 +1394,7 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)
goto err2;
}
+ mutex_init(&nt->link_event_lock);
INIT_DELAYED_WORK(&nt->link_work, ntb_transport_link_work);
INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup_work);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 58f3097888a7..c2bee32332fe 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -806,8 +806,8 @@ static void nvme_unmap_data(struct request *req)
if (!blk_rq_dma_unmap(req, dma_dev, &iod->dma_state, iod->total_len,
map)) {
if (nvme_pci_cmd_use_sgl(&iod->cmd))
- nvme_free_sgls(req, iod->descriptors[0],
- &iod->cmd.common.dptr.sgl, attrs);
+ nvme_free_sgls(req, &iod->cmd.common.dptr.sgl,
+ iod->descriptors[0], attrs);
else
nvme_free_prps(req, attrs);
}
diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c
index 8d246b8ca604..0103815542d4 100644
--- a/drivers/nvme/target/io-cmd-bdev.c
+++ b/drivers/nvme/target/io-cmd-bdev.c
@@ -180,9 +180,10 @@ u16 blk_to_nvme_status(struct nvmet_req *req, blk_status_t blk_sts)
static void nvmet_bio_done(struct bio *bio)
{
struct nvmet_req *req = bio->bi_private;
+ blk_status_t blk_status = bio->bi_status;
- nvmet_req_complete(req, blk_to_nvme_status(req, bio->bi_status));
nvmet_req_bio_put(req, bio);
+ nvmet_req_complete(req, blk_to_nvme_status(req, blk_status));
}
#ifdef CONFIG_BLK_DEV_INTEGRITY
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 0b65039ece53..57420806c1a2 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1942,13 +1942,17 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
end--;
len = end - start;
- if (kstrtoint(end, 10, &id) < 0)
+ if (kstrtoint(end, 10, &id) < 0) {
+ of_node_put(np);
continue;
+ }
/* Allocate an alias_prop with enough space for the stem */
ap = dt_alloc(sizeof(*ap) + len + 1, __alignof__(*ap));
- if (!ap)
+ if (!ap) {
+ of_node_put(np);
continue;
+ }
memset(ap, 0, sizeof(*ap) + len + 1);
ap->alias = start;
of_alias_add(ap, np, id, start, len);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 5619ec917858..a2a13617c6f4 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -157,13 +157,19 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
phys_addr_t base, size;
int i, len;
const __be32 *prop;
- bool nomap;
+ bool nomap, default_cma;
prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
if (!prop)
return -ENOENT;
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
+
+ if (default_cma && cma_skip_dt_default_reserved_mem()) {
+ pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
+ return -EINVAL;
+ }
for (i = 0; i < len; i++) {
u64 b, s;
@@ -248,10 +254,13 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
fdt_for_each_subnode(child, fdt, node) {
const char *uname;
+ bool default_cma = of_get_flat_dt_prop(child, "linux,cma-default", NULL);
u64 b, s;
if (!of_fdt_device_is_available(fdt, child))
continue;
+ if (default_cma && cma_skip_dt_default_reserved_mem())
+ continue;
if (!of_flat_dt_get_addr_size(child, "reg", &b, &s))
continue;
@@ -389,7 +398,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
phys_addr_t base = 0, align = 0, size;
int i, len;
const __be32 *prop;
- bool nomap;
+ bool nomap, default_cma;
int ret;
prop = of_get_flat_dt_prop(node, "size", &len);
@@ -413,6 +422,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
}
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
+
+ if (default_cma && cma_skip_dt_default_reserved_mem()) {
+ pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
+ return -EINVAL;
+ }
/* Need adjust the alignment to satisfy the CMA requirement */
if (IS_ENABLED(CONFIG_CMA)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index f77cb19973a5..a6dca3a005aa 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -569,7 +569,7 @@ static int __init of_platform_default_populate_init(void)
node = of_find_node_by_path("/firmware");
if (node) {
- of_platform_populate(node, NULL, NULL, NULL);
+ of_platform_default_populate(node, NULL, NULL);
of_node_put(node);
}
diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c
index ecdebdeb2dff..39f8cf3b70d5 100644
--- a/drivers/pci/rebar.c
+++ b/drivers/pci/rebar.c
@@ -295,7 +295,6 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size,
int exclude_bars)
{
struct pci_host_bridge *host;
- int old, ret;
/* Check if we must preserve the firmware's resource assignment */
host = pci_find_host_bridge(dev->bus);
@@ -308,21 +307,6 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size,
if (!pci_rebar_size_supported(dev, resno, size))
return -EINVAL;
- old = pci_rebar_get_current_size(dev, resno);
- if (old < 0)
- return old;
-
- ret = pci_rebar_set_size(dev, resno, size);
- if (ret)
- return ret;
-
- ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
- if (ret)
- goto error_resize;
- return 0;
-
-error_resize:
- pci_rebar_set_size(dev, resno, old);
- return ret;
+ return pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
}
EXPORT_SYMBOL(pci_resize_resource);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 6e90f46f52af..a61d38777cdc 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -2504,12 +2504,20 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
struct resource *b_win, *r;
LIST_HEAD(saved);
unsigned int i;
- int ret = 0;
+ int old, ret;
b_win = pbus_select_window(bus, res);
if (!b_win)
return -EINVAL;
+ old = pci_rebar_get_current_size(pdev, resno);
+ if (old < 0)
+ return old;
+
+ ret = pci_rebar_set_size(pdev, resno, size);
+ if (ret)
+ return ret;
+
pci_dev_for_each_resource(pdev, r, i) {
if (i >= PCI_BRIDGE_RESOURCES)
break;
@@ -2542,7 +2550,15 @@ out:
return ret;
restore:
- /* Revert to the old configuration */
+ /*
+ * Revert to the old configuration.
+ *
+ * BAR Size must be restored first because it affects the read-only
+ * bits in BAR (the old address might not be restorable otherwise
+ * due to low address bits).
+ */
+ pci_rebar_set_size(pdev, resno, old);
+
list_for_each_entry(dev_res, &saved, list) {
struct resource *res = dev_res->res;
struct pci_dev *dev = dev_res->dev;
@@ -2556,8 +2572,7 @@ restore:
restore_dev_resource(dev_res);
- ret = pci_claim_resource(dev, i);
- if (ret)
+ if (pci_claim_resource(dev, i))
continue;
if (i < PCI_BRIDGE_RESOURCES) {
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 18295b15ecd9..4507dc8b5563 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -619,7 +619,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
pc->chip.set = meson_gpio_set;
pc->chip.base = -1;
pc->chip.ngpio = pc->data->num_pins;
- pc->chip.can_sleep = false;
+ pc->chip.can_sleep = true;
ret = gpiochip_add_data(&pc->chip, pc);
if (ret) {
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index e44ef262beec..2fc67aeafdb3 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -3545,10 +3545,9 @@ static int rockchip_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
return 0;
}
-static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned offset,
- bool input)
+static int rockchip_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset)
{
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
struct rockchip_pin_bank *bank;
@@ -3562,7 +3561,7 @@ static const struct pinmux_ops rockchip_pmx_ops = {
.get_function_name = rockchip_pmx_get_func_name,
.get_function_groups = rockchip_pmx_get_groups,
.set_mux = rockchip_pmx_set,
- .gpio_set_direction = rockchip_pmx_gpio_set_direction,
+ .gpio_request_enable = rockchip_pmx_gpio_request_enable,
};
/*
diff --git a/drivers/pinctrl/pinctrl-th1520.c b/drivers/pinctrl/pinctrl-th1520.c
index e641bad6728c..83e9c9f77370 100644
--- a/drivers/pinctrl/pinctrl-th1520.c
+++ b/drivers/pinctrl/pinctrl-th1520.c
@@ -287,7 +287,7 @@ static const struct pinctrl_pin_desc th1520_group3_pins[] = {
TH1520_PAD(5, QSPI0_D0_MOSI, QSPI, PWM, I2S, GPIO, ____, ____, 0),
TH1520_PAD(6, QSPI0_D1_MISO, QSPI, PWM, I2S, GPIO, ____, ____, 0),
TH1520_PAD(7, QSPI0_D2_WP, QSPI, PWM, I2S, GPIO, ____, ____, 0),
- TH1520_PAD(8, QSPI1_D3_HOLD, QSPI, ____, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(8, QSPI0_D3_HOLD, QSPI, ____, I2S, GPIO, ____, ____, 0),
TH1520_PAD(9, I2C2_SCL, I2C, UART, ____, GPIO, ____, ____, 0),
TH1520_PAD(10, I2C2_SDA, I2C, UART, ____, GPIO, ____, ____, 0),
TH1520_PAD(11, I2C3_SCL, I2C, ____, ____, GPIO, ____, ____, 0),
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index c480e8b78503..f56592411cf6 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -61,13 +61,14 @@ config PINCTRL_LPASS_LPI
(Low Power Island) found on the Qualcomm Technologies Inc SoCs.
config PINCTRL_SC7280_LPASS_LPI
- tristate "Qualcomm Technologies Inc SC7280 LPASS LPI pin controller driver"
+ tristate "Qualcomm Technologies Inc SC7280 and SM8350 LPASS LPI pin controller driver"
depends on ARM64 || COMPILE_TEST
depends on PINCTRL_LPASS_LPI
help
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI
- (Low Power Island) found on the Qualcomm Technologies Inc SC7280 platform.
+ (Low Power Island) found on the Qualcomm Technologies Inc SC7280
+ and SM8350 platforms.
config PINCTRL_SDM660_LPASS_LPI
tristate "Qualcomm Technologies Inc SDM660 LPASS LPI pin controller driver"
@@ -106,16 +107,6 @@ config PINCTRL_SM8250_LPASS_LPI
Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI
(Low Power Island) found on the Qualcomm Technologies Inc SM8250 platform.
-config PINCTRL_SM8350_LPASS_LPI
- tristate "Qualcomm Technologies Inc SM8350 LPASS LPI pin controller driver"
- depends on ARM64 || COMPILE_TEST
- depends on PINCTRL_LPASS_LPI
- help
- This is the pinctrl, pinmux, pinconf and gpiolib driver for the
- Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI
- (Low Power Island) found on the Qualcomm Technologies Inc SM8350
- platform.
-
config PINCTRL_SM8450_LPASS_LPI
tristate "Qualcomm Technologies Inc SM8450 LPASS LPI pin controller driver"
depends on ARM64 || COMPILE_TEST
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 748b17a77b2c..4269d1781015 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -64,7 +64,6 @@ obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o
obj-$(CONFIG_PINCTRL_SM8250) += pinctrl-sm8250.o
obj-$(CONFIG_PINCTRL_SM8250_LPASS_LPI) += pinctrl-sm8250-lpass-lpi.o
obj-$(CONFIG_PINCTRL_SM8350) += pinctrl-sm8350.o
-obj-$(CONFIG_PINCTRL_SM8350_LPASS_LPI) += pinctrl-sm8350-lpass-lpi.o
obj-$(CONFIG_PINCTRL_SM8450) += pinctrl-sm8450.o
obj-$(CONFIG_PINCTRL_SM8450_LPASS_LPI) += pinctrl-sm8450-lpass-lpi.o
obj-$(CONFIG_PINCTRL_SM8550) += pinctrl-sm8550.o
diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
index 78212f992843..76aed3296279 100644
--- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
+++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
@@ -312,6 +312,22 @@ static const struct pinconf_ops lpi_gpio_pinconf_ops = {
.pin_config_group_set = lpi_config_set,
};
+static int lpi_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
+{
+ unsigned long config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, 0);
+ struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ unsigned long arg;
+ int ret;
+
+ ret = lpi_config_get(state->ctrl, pin, &config);
+ if (ret)
+ return ret;
+
+ arg = pinconf_to_config_argument(config);
+
+ return arg ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
static int lpi_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
{
struct lpi_pinctrl *state = gpiochip_get_data(chip);
@@ -409,6 +425,7 @@ static void lpi_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
#endif
static const struct gpio_chip lpi_gpio_template = {
+ .get_direction = lpi_gpio_get_direction,
.direction_input = lpi_gpio_direction_input,
.direction_output = lpi_gpio_direction_output,
.get = lpi_gpio_get,
diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c
index 1161f0a91a00..750f410311a8 100644
--- a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c
+++ b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c
@@ -131,6 +131,9 @@ static const struct of_device_id lpi_pinctrl_of_match[] = {
{
.compatible = "qcom,sc7280-lpass-lpi-pinctrl",
.data = &sc7280_lpi_data,
+ }, {
+ .compatible = "qcom,sm8350-lpass-lpi-pinctrl",
+ .data = &sc7280_lpi_data,
},
{ }
};
diff --git a/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c
deleted file mode 100644
index 7b146b4acfdf..000000000000
--- a/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c
+++ /dev/null
@@ -1,151 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2020-2023 Linaro Ltd.
- */
-
-#include <linux/gpio/driver.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include "pinctrl-lpass-lpi.h"
-
-enum lpass_lpi_functions {
- LPI_MUX_dmic1_clk,
- LPI_MUX_dmic1_data,
- LPI_MUX_dmic2_clk,
- LPI_MUX_dmic2_data,
- LPI_MUX_dmic3_clk,
- LPI_MUX_dmic3_data,
- LPI_MUX_i2s1_clk,
- LPI_MUX_i2s1_data,
- LPI_MUX_i2s1_ws,
- LPI_MUX_i2s2_clk,
- LPI_MUX_i2s2_data,
- LPI_MUX_i2s2_ws,
- LPI_MUX_qua_mi2s_data,
- LPI_MUX_qua_mi2s_sclk,
- LPI_MUX_qua_mi2s_ws,
- LPI_MUX_swr_rx_clk,
- LPI_MUX_swr_rx_data,
- LPI_MUX_swr_tx_clk,
- LPI_MUX_swr_tx_data,
- LPI_MUX_wsa_swr_clk,
- LPI_MUX_wsa_swr_data,
- LPI_MUX_gpio,
- LPI_MUX__,
-};
-
-static const struct pinctrl_pin_desc sm8350_lpi_pins[] = {
- PINCTRL_PIN(0, "gpio0"),
- PINCTRL_PIN(1, "gpio1"),
- PINCTRL_PIN(2, "gpio2"),
- PINCTRL_PIN(3, "gpio3"),
- PINCTRL_PIN(4, "gpio4"),
- PINCTRL_PIN(5, "gpio5"),
- PINCTRL_PIN(6, "gpio6"),
- PINCTRL_PIN(7, "gpio7"),
- PINCTRL_PIN(8, "gpio8"),
- PINCTRL_PIN(9, "gpio9"),
- PINCTRL_PIN(10, "gpio10"),
- PINCTRL_PIN(11, "gpio11"),
- PINCTRL_PIN(12, "gpio12"),
- PINCTRL_PIN(13, "gpio13"),
- PINCTRL_PIN(14, "gpio14"),
-};
-
-static const char * const swr_tx_clk_groups[] = { "gpio0" };
-static const char * const swr_tx_data_groups[] = { "gpio1", "gpio2", "gpio5", "gpio14" };
-static const char * const swr_rx_clk_groups[] = { "gpio3" };
-static const char * const swr_rx_data_groups[] = { "gpio4", "gpio5" };
-static const char * const dmic1_clk_groups[] = { "gpio6" };
-static const char * const dmic1_data_groups[] = { "gpio7" };
-static const char * const dmic2_clk_groups[] = { "gpio8" };
-static const char * const dmic2_data_groups[] = { "gpio9" };
-static const char * const i2s2_clk_groups[] = { "gpio10" };
-static const char * const i2s2_ws_groups[] = { "gpio11" };
-static const char * const dmic3_clk_groups[] = { "gpio12" };
-static const char * const dmic3_data_groups[] = { "gpio13" };
-static const char * const qua_mi2s_sclk_groups[] = { "gpio0" };
-static const char * const qua_mi2s_ws_groups[] = { "gpio1" };
-static const char * const qua_mi2s_data_groups[] = { "gpio2", "gpio3", "gpio4" };
-static const char * const i2s1_clk_groups[] = { "gpio6" };
-static const char * const i2s1_ws_groups[] = { "gpio7" };
-static const char * const i2s1_data_groups[] = { "gpio8", "gpio9" };
-static const char * const wsa_swr_clk_groups[] = { "gpio10" };
-static const char * const wsa_swr_data_groups[] = { "gpio11" };
-static const char * const i2s2_data_groups[] = { "gpio12", "gpio12" };
-
-static const struct lpi_pingroup sm8350_groups[] = {
- LPI_PINGROUP(0, 0, swr_tx_clk, qua_mi2s_sclk, _, _),
- LPI_PINGROUP(1, 2, swr_tx_data, qua_mi2s_ws, _, _),
- LPI_PINGROUP(2, 4, swr_tx_data, qua_mi2s_data, _, _),
- LPI_PINGROUP(3, 8, swr_rx_clk, qua_mi2s_data, _, _),
- LPI_PINGROUP(4, 10, swr_rx_data, qua_mi2s_data, _, _),
- LPI_PINGROUP(5, 12, swr_tx_data, swr_rx_data, _, _),
- LPI_PINGROUP(6, LPI_NO_SLEW, dmic1_clk, i2s1_clk, _, _),
- LPI_PINGROUP(7, LPI_NO_SLEW, dmic1_data, i2s1_ws, _, _),
- LPI_PINGROUP(8, LPI_NO_SLEW, dmic2_clk, i2s1_data, _, _),
- LPI_PINGROUP(9, LPI_NO_SLEW, dmic2_data, i2s1_data, _, _),
- LPI_PINGROUP(10, 16, i2s2_clk, wsa_swr_clk, _, _),
- LPI_PINGROUP(11, 18, i2s2_ws, wsa_swr_data, _, _),
- LPI_PINGROUP(12, LPI_NO_SLEW, dmic3_clk, i2s2_data, _, _),
- LPI_PINGROUP(13, LPI_NO_SLEW, dmic3_data, i2s2_data, _, _),
- LPI_PINGROUP(14, 6, swr_tx_data, _, _, _),
-};
-
-static const struct lpi_function sm8350_functions[] = {
- LPI_FUNCTION(dmic1_clk),
- LPI_FUNCTION(dmic1_data),
- LPI_FUNCTION(dmic2_clk),
- LPI_FUNCTION(dmic2_data),
- LPI_FUNCTION(dmic3_clk),
- LPI_FUNCTION(dmic3_data),
- LPI_FUNCTION(i2s1_clk),
- LPI_FUNCTION(i2s1_data),
- LPI_FUNCTION(i2s1_ws),
- LPI_FUNCTION(i2s2_clk),
- LPI_FUNCTION(i2s2_data),
- LPI_FUNCTION(i2s2_ws),
- LPI_FUNCTION(qua_mi2s_data),
- LPI_FUNCTION(qua_mi2s_sclk),
- LPI_FUNCTION(qua_mi2s_ws),
- LPI_FUNCTION(swr_rx_clk),
- LPI_FUNCTION(swr_rx_data),
- LPI_FUNCTION(swr_tx_clk),
- LPI_FUNCTION(swr_tx_data),
- LPI_FUNCTION(wsa_swr_clk),
- LPI_FUNCTION(wsa_swr_data),
-};
-
-static const struct lpi_pinctrl_variant_data sm8350_lpi_data = {
- .pins = sm8350_lpi_pins,
- .npins = ARRAY_SIZE(sm8350_lpi_pins),
- .groups = sm8350_groups,
- .ngroups = ARRAY_SIZE(sm8350_groups),
- .functions = sm8350_functions,
- .nfunctions = ARRAY_SIZE(sm8350_functions),
-};
-
-static const struct of_device_id lpi_pinctrl_of_match[] = {
- {
- .compatible = "qcom,sm8350-lpass-lpi-pinctrl",
- .data = &sm8350_lpi_data,
- },
- { }
-};
-MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
-
-static struct platform_driver lpi_pinctrl_driver = {
- .driver = {
- .name = "qcom-sm8350-lpass-lpi-pinctrl",
- .of_match_table = lpi_pinctrl_of_match,
- },
- .probe = lpi_pinctrl_probe,
- .remove = lpi_pinctrl_remove,
-};
-module_platform_driver(lpi_pinctrl_driver);
-
-MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
-MODULE_DESCRIPTION("QTI SM8350 LPI GPIO pin control driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/platform/mellanox/mlx-platform.c b/drivers/platform/mellanox/mlx-platform.c
index d0df18be93c7..efd0c074ad93 100644
--- a/drivers/platform/mellanox/mlx-platform.c
+++ b/drivers/platform/mellanox/mlx-platform.c
@@ -7381,7 +7381,7 @@ static int __init mlxplat_dmi_ng400_hi171_matched(const struct dmi_system_id *dm
mlxplat_hotplug = &mlxplat_mlxcpld_ng800_hi171_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
- mlxplat_led = &mlxplat_default_ng_led_data;
+ mlxplat_led = &mlxplat_xdr_led_data;
mlxplat_regs_io = &mlxplat_default_ng_regs_io_data;
mlxplat_fan = &mlxplat_xdr_fan_data;
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index bf97381faf58..e0eaaefb13d0 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -455,6 +455,11 @@ static struct quirk_entry quirk_acer_travelmate_2490 = {
.mailled = 1,
};
+static struct quirk_entry quirk_acer_nitro_an515_58 = {
+ .predator_v4 = 1,
+ .pwm = 1,
+};
+
static struct quirk_entry quirk_acer_predator_ph315_53 = {
.turbo = 1,
.cpu_fans = 1,
@@ -655,7 +660,7 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Nitro AN515-58"),
},
- .driver_data = &quirk_acer_predator_v4,
+ .driver_data = &quirk_acer_nitro_an515_58,
},
{
.callback = dmi_matched,
@@ -2065,7 +2070,8 @@ static int acer_toggle_turbo(void)
WMID_gaming_set_u64(0x1, ACER_CAP_TURBO_LED);
/* Set FAN mode to auto */
- WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_AUTO);
+ if (has_cap(ACER_CAP_TURBO_FAN))
+ WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_AUTO);
/* Set OC to normal */
if (has_cap(ACER_CAP_TURBO_OC)) {
@@ -2079,7 +2085,8 @@ static int acer_toggle_turbo(void)
WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED);
/* Set FAN mode to turbo */
- WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_TURBO);
+ if (has_cap(ACER_CAP_TURBO_FAN))
+ WMID_gaming_set_fan_mode(ACER_WMID_FAN_MODE_TURBO);
/* Set OC to turbo mode */
if (has_cap(ACER_CAP_TURBO_OC)) {
diff --git a/drivers/platform/x86/amd/wbrf.c b/drivers/platform/x86/amd/wbrf.c
index dd197b3aebe0..0f58d252b620 100644
--- a/drivers/platform/x86/amd/wbrf.c
+++ b/drivers/platform/x86/amd/wbrf.c
@@ -104,8 +104,10 @@ static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ran
obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
WBRF_REVISION, WBRF_RECORD, &argv4);
- if (!obj)
+ if (!obj) {
+ kfree(tmp);
return -EINVAL;
+ }
if (obj->type != ACPI_TYPE_INTEGER) {
ret = -EINVAL;
diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h
index 3ac7aea37838..6e9703bd5017 100644
--- a/drivers/platform/x86/asus-armoury.h
+++ b/drivers/platform/x86/asus-armoury.h
@@ -348,6 +348,37 @@ struct power_data {
static const struct dmi_system_id power_limits[] = {
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "FA401UV"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 80,
+ .ppt_pl2_sppt_min = 35,
+ .ppt_pl2_sppt_max = 80,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 80,
+ .nv_dynamic_boost_min = 5,
+ .nv_dynamic_boost_max = 25,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ .nv_tgp_min = 55,
+ .nv_tgp_max = 75,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 25,
+ .ppt_pl1_spl_max = 35,
+ .ppt_pl2_sppt_min = 31,
+ .ppt_pl2_sppt_max = 44,
+ .ppt_pl3_fppt_min = 45,
+ .ppt_pl3_fppt_max = 65,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "FA401W"),
},
.driver_data = &(struct power_data) {
@@ -580,8 +611,8 @@ static const struct dmi_system_id power_limits[] = {
.ppt_pl2_sppt_def = 54,
.ppt_pl2_sppt_max = 90,
.ppt_pl3_fppt_min = 35,
- .ppt_pl3_fppt_def = 90,
- .ppt_pl3_fppt_max = 65,
+ .ppt_pl3_fppt_def = 65,
+ .ppt_pl3_fppt_max = 90,
.nv_dynamic_boost_min = 10,
.nv_dynamic_boost_max = 15,
.nv_temp_target_min = 75,
@@ -703,6 +734,25 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "FA617XT"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_apu_sppt_min = 15,
+ .ppt_apu_sppt_max = 80,
+ .ppt_platform_sppt_min = 30,
+ .ppt_platform_sppt_max = 145,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_apu_sppt_min = 25,
+ .ppt_apu_sppt_max = 35,
+ .ppt_platform_sppt_min = 45,
+ .ppt_platform_sppt_max = 100,
+ },
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "FX507VI"),
},
.driver_data = &(struct power_data) {
@@ -843,7 +893,7 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
- DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
+ DMI_MATCH(DMI_BOARD_NAME, "GA403UI"),
},
.driver_data = &(struct power_data) {
.ac_data = &(struct power_limits) {
@@ -875,6 +925,70 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GA403UV"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 80,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 80,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 80,
+ .nv_dynamic_boost_min = 5,
+ .nv_dynamic_boost_max = 25,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ .nv_tgp_min = 55,
+ .nv_tgp_max = 65,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 35,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 35,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 65,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ .requires_fan_curve = true,
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GA403WM"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 80,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 80,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 80,
+ .nv_dynamic_boost_min = 0,
+ .nv_dynamic_boost_max = 15,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ .nv_tgp_min = 55,
+ .nv_tgp_max = 85,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 35,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 35,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 65,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ .requires_fan_curve = true,
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "GA403WR"),
},
.driver_data = &(struct power_data) {
@@ -907,6 +1021,38 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GA403WW"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 80,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 80,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 80,
+ .nv_dynamic_boost_min = 0,
+ .nv_dynamic_boost_max = 25,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ .nv_tgp_min = 80,
+ .nv_tgp_max = 95,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 35,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 35,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 65,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ .requires_fan_curve = true,
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "GA503QR"),
},
.driver_data = &(struct power_data) {
@@ -1189,6 +1335,33 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GV302XV"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 55,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 60,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 65,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 15,
+ .ppt_pl1_spl_max = 35,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 35,
+ .ppt_pl3_fppt_min = 35,
+ .ppt_pl3_fppt_max = 65,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "GV601R"),
},
.driver_data = &(struct power_data) {
@@ -1318,6 +1491,22 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "G513QY"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ /* Advantage Edition Laptop, no PL1 or PL2 limits */
+ .ppt_apu_sppt_min = 15,
+ .ppt_apu_sppt_max = 100,
+ .ppt_platform_sppt_min = 70,
+ .ppt_platform_sppt_max = 190,
+ },
+ .dc_data = NULL,
+ .requires_fan_curve = true,
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "G513R"),
},
.driver_data = &(struct power_data) {
@@ -1569,6 +1758,35 @@ static const struct dmi_system_id power_limits[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "G835LR"),
+ },
+ .driver_data = &(struct power_data) {
+ .ac_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 28,
+ .ppt_pl1_spl_def = 140,
+ .ppt_pl1_spl_max = 175,
+ .ppt_pl2_sppt_min = 28,
+ .ppt_pl2_sppt_max = 175,
+ .nv_dynamic_boost_min = 5,
+ .nv_dynamic_boost_max = 25,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ .nv_tgp_min = 65,
+ .nv_tgp_max = 115,
+ },
+ .dc_data = &(struct power_limits) {
+ .ppt_pl1_spl_min = 25,
+ .ppt_pl1_spl_max = 55,
+ .ppt_pl2_sppt_min = 25,
+ .ppt_pl2_sppt_max = 70,
+ .nv_temp_target_min = 75,
+ .nv_temp_target_max = 87,
+ },
+ .requires_fan_curve = true,
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "G835LW"),
},
.driver_data = &(struct power_data) {
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 4aec7ec69250..0775fadedd10 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -4889,7 +4889,6 @@ static int asus_wmi_add(struct platform_device *pdev)
asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU);
asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU);
asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE);
- asus->oobe_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_OOBE);
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE))
asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
@@ -4902,6 +4901,8 @@ static int asus_wmi_add(struct platform_device *pdev)
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX_VIVO;
#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
+ asus->oobe_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_OOBE);
+
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY;
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO))
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 5bfa7159f5bc..dbe096eefa75 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
@@ -10,6 +10,8 @@
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/string.h>
#include <linux/wmi.h>
#include "bioscfg.h"
#include "../../firmware_attributes_class.h"
@@ -781,6 +783,12 @@ static int hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type,
if (ret < 0)
goto buff_attr_exit;
+ if (strlen(str) == 0) {
+ pr_debug("Ignoring attribute with empty name\n");
+ ret = 0;
+ goto buff_attr_exit;
+ }
+
if (attr_type == HPWMI_PASSWORD_TYPE ||
attr_type == HPWMI_SECURE_PLATFORM_TYPE)
temp_kset = bioscfg_drv.authentication_dir_kset;
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
index 3166ef328eba..f1eec0e4ba07 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
@@ -10,6 +10,7 @@
#include <linux/wmi.h>
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -56,14 +57,14 @@ enum mechanism_values {
#define PASSWD_MECHANISM_TYPES "password"
-#define HP_WMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
+#define HP_WMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
-#define HP_WMI_BIOS_STRING_GUID "988D08E3-68F4-4c35-AF3E-6A1B8106F83C"
+#define HP_WMI_BIOS_STRING_GUID "988D08E3-68F4-4C35-AF3E-6A1B8106F83C"
#define HP_WMI_BIOS_INTEGER_GUID "8232DE3D-663D-4327-A8F4-E293ADB9BF05"
#define HP_WMI_BIOS_ENUMERATION_GUID "2D114B49-2DFB-4130-B8FE-4A3C09E75133"
#define HP_WMI_BIOS_ORDERED_LIST_GUID "14EA9746-CE1F-4098-A0E0-7045CB4DA745"
#define HP_WMI_BIOS_PASSWORD_GUID "322F2028-0F84-4901-988E-015176049E2D"
-#define HP_WMI_SET_BIOS_SETTING_GUID "1F4C91EB-DC5C-460b-951D-C7CB9B4B8D5E"
+#define HP_WMI_SET_BIOS_SETTING_GUID "1F4C91EB-DC5C-460B-951D-C7CB9B4B8D5E"
enum hp_wmi_spm_commandtype {
HPWMI_SECUREPLATFORM_GET_STATE = 0x10,
@@ -285,8 +286,9 @@ enum hp_wmi_data_elements {
{ \
int i; \
\
- for (i = 0; i <= bioscfg_drv.type##_instances_count; i++) { \
- if (!strcmp(kobj->name, bioscfg_drv.type##_data[i].attr_name_kobj->name)) \
+ for (i = 0; i < bioscfg_drv.type##_instances_count; i++) { \
+ if (bioscfg_drv.type##_data[i].attr_name_kobj && \
+ !strcmp(kobj->name, bioscfg_drv.type##_data[i].attr_name_kobj->name)) \
return i; \
} \
return -EIO; \
diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c
index 5c83e5599f1e..74bf4936991d 100644
--- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c
+++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c
@@ -846,22 +846,25 @@ static int imx8mq_vpu_power_notifier(struct notifier_block *nb,
return NOTIFY_OK;
}
+/*
+ * For i.MX8MQ, the ADB in the VPUMIX domain has no separate reset and clock
+ * enable bits, but is ungated and reset together with the VPUs.
+ * Resetting G1 or G2 separately may led to system hang.
+ * Remove the rst_mask and clk_mask from the domain data of G1 and G2,
+ * Let imx8mq_vpu_power_notifier() do really vpu reset.
+ */
static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = {
[IMX8MQ_VPUBLK_PD_G1] = {
.name = "vpublk-g1",
.clk_names = (const char *[]){ "g1", },
.num_clks = 1,
.gpc_name = "g1",
- .rst_mask = BIT(1),
- .clk_mask = BIT(1),
},
[IMX8MQ_VPUBLK_PD_G2] = {
.name = "vpublk-g2",
.clk_names = (const char *[]){ "g2", },
.num_clks = 1,
.gpc_name = "g2",
- .rst_mask = BIT(0),
- .clk_mask = BIT(0),
},
};
diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c
index a8b37037c6fe..19849703be4a 100644
--- a/drivers/pmdomain/qcom/rpmhpd.c
+++ b/drivers/pmdomain/qcom/rpmhpd.c
@@ -246,6 +246,8 @@ static struct rpmhpd *sa8540p_rpmhpds[] = {
[SC8280XP_MMCX_AO] = &mmcx_ao,
[SC8280XP_MX] = &mx,
[SC8280XP_MX_AO] = &mx_ao,
+ [SC8280XP_MXC] = &mxc,
+ [SC8280XP_MXC_AO] = &mxc_ao,
[SC8280XP_NSP] = &nsp,
};
@@ -700,6 +702,8 @@ static struct rpmhpd *sc8280xp_rpmhpds[] = {
[SC8280XP_MMCX_AO] = &mmcx_ao,
[SC8280XP_MX] = &mx,
[SC8280XP_MX_AO] = &mx_ao,
+ [SC8280XP_MXC] = &mxc,
+ [SC8280XP_MXC_AO] = &mxc_ao,
[SC8280XP_NSP] = &nsp,
[SC8280XP_QPHY] = &qphy,
};
diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
index 4f1336a0f49a..997e93c12951 100644
--- a/drivers/pmdomain/rockchip/pm-domains.c
+++ b/drivers/pmdomain/rockchip/pm-domains.c
@@ -879,6 +879,16 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
pd->genpd.name = pd->info->name;
else
pd->genpd.name = kbasename(node->full_name);
+
+ /*
+ * power domain's needing a regulator should default to off, since
+ * the regulator state is unknown at probe time. Also the regulator
+ * state cannot be checked, since that usually requires IP needing
+ * (a different) power domain.
+ */
+ if (pd->info->need_regulator)
+ rockchip_pd_power(pd, false);
+
pd->genpd.power_off = rockchip_pd_power_off;
pd->genpd.power_on = rockchip_pd_power_on;
pd->genpd.attach_dev = rockchip_pd_attach_dev;
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index cd06229db394..ec8731515333 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -2295,8 +2295,9 @@ static long pwm_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long ar
.duty_offset_ns = wf.duty_offset_ns,
};
- return copy_to_user((struct pwmchip_waveform __user *)arg,
- &cwf, sizeof(cwf));
+ ret = copy_to_user((struct pwmchip_waveform __user *)arg,
+ &cwf, sizeof(cwf));
+ return ret ? -EFAULT : 0;
}
case PWM_IOCTL_GETWF:
@@ -2329,8 +2330,9 @@ static long pwm_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long ar
.duty_offset_ns = wf.duty_offset_ns,
};
- return copy_to_user((struct pwmchip_waveform __user *)arg,
- &cwf, sizeof(cwf));
+ ret = copy_to_user((struct pwmchip_waveform __user *)arg,
+ &cwf, sizeof(cwf));
+ return ret ? -EFAULT : 0;
}
case PWM_IOCTL_SETROUNDEDWF:
diff --git a/drivers/pwm/pwm-max7360.c b/drivers/pwm/pwm-max7360.c
index 16261958ce7f..732969303dd7 100644
--- a/drivers/pwm/pwm-max7360.c
+++ b/drivers/pwm/pwm-max7360.c
@@ -153,6 +153,7 @@ static int max7360_pwm_read_waveform(struct pwm_chip *chip,
}
static const struct pwm_ops max7360_pwm_ops = {
+ .sizeof_wfhw = sizeof(struct max7360_pwm_waveform),
.request = max7360_pwm_request,
.round_waveform_tohw = max7360_pwm_round_waveform_tohw,
.round_waveform_fromhw = max7360_pwm_round_waveform_fromhw,
diff --git a/drivers/regulator/fp9931.c b/drivers/regulator/fp9931.c
index 69b3c712e5d5..7fbcc6327cc6 100644
--- a/drivers/regulator/fp9931.c
+++ b/drivers/regulator/fp9931.c
@@ -439,6 +439,9 @@ static int fp9931_probe(struct i2c_client *client)
int i;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
data->regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(data->regmap))
return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c
index 8102c8134c49..8b0ad6f582ec 100644
--- a/drivers/s390/crypto/ap_card.c
+++ b/drivers/s390/crypto/ap_card.c
@@ -43,7 +43,7 @@ static ssize_t depth_show(struct device *dev, struct device_attribute *attr,
{
struct ap_card *ac = to_ap_card(dev);
- return sysfs_emit(buf, "%d\n", ac->hwinfo.qd);
+ return sysfs_emit(buf, "%d\n", ac->hwinfo.qd + 1);
}
static DEVICE_ATTR_RO(depth);
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
index 4a32c1e19a1e..a80ab87cad62 100644
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -285,7 +285,7 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq)
list_move_tail(&ap_msg->list, &aq->pendingq);
aq->requestq_count--;
aq->pendingq_count++;
- if (aq->queue_count < aq->card->hwinfo.qd) {
+ if (aq->queue_count < aq->card->hwinfo.qd + 1) {
aq->sm_state = AP_SM_STATE_WORKING;
return AP_SM_WAIT_AGAIN;
}
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 4e899ec1477d..b1cba986f0fb 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1025,6 +1025,7 @@ unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba)
&nonemb_cmd->dma,
GFP_KERNEL);
if (!nonemb_cmd->va) {
+ free_mcc_wrb(ctrl, tag);
mutex_unlock(&ctrl->mbox_lock);
return 0;
}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index a3971afc2dd1..a04a5aa0d005 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -878,6 +878,9 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
payload_size = sizeof(purex->els_frame_payload);
}
+ if (total_bytes > sizeof(item->iocb.iocb))
+ total_bytes = sizeof(item->iocb.iocb);
+
pending_bytes = total_bytes;
no_bytes = (pending_bytes > payload_size) ? payload_size :
pending_bytes;
@@ -1163,6 +1166,10 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
- PURX_ELS_HEADER_SIZE;
+
+ if (total_bytes > sizeof(item->iocb.iocb))
+ total_bytes = sizeof(item->iocb.iocb);
+
pending_bytes = total_bytes;
entry_count = entry_count_remaining = purex->entry_count;
no_bytes = (pending_bytes > sizeof(purex->els_frame_payload)) ?
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 16a44c0917e1..e939bc88e151 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4489,7 +4489,7 @@ fail_lsrjt:
fail_elsrej:
dma_pool_destroy(ha->purex_dma_pool);
fail_flt:
- dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
ha->flt, ha->flt_dma);
fail_flt_buffer:
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index eebca96c1fc1..b6e8730e049e 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -282,11 +282,20 @@ static void scsi_eh_inc_host_failed(struct rcu_head *head)
{
struct scsi_cmnd *scmd = container_of(head, typeof(*scmd), rcu);
struct Scsi_Host *shost = scmd->device->host;
- unsigned int busy = scsi_host_busy(shost);
+ unsigned int busy;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
shost->host_failed++;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ /*
+ * The counting of busy requests needs to occur after adding to
+ * host_failed or after the lock acquire for adding to host_failed
+ * to prevent a race with host unbusy and missing an eh wakeup.
+ */
+ busy = scsi_host_busy(shost);
+
+ spin_lock_irqsave(shost->host_lock, flags);
scsi_eh_wakeup(shost, busy);
spin_unlock_irqrestore(shost->host_lock, flags);
}
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index c7d6b76c86d2..4a902c9dfd8b 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -376,6 +376,14 @@ static void scsi_dec_host_busy(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
rcu_read_lock();
__clear_bit(SCMD_STATE_INFLIGHT, &cmd->state);
if (unlikely(scsi_host_in_recovery(shost))) {
+ /*
+ * Ensure the clear of SCMD_STATE_INFLIGHT is visible to
+ * other CPUs before counting busy requests. Otherwise,
+ * reordering can cause CPUs to race and miss an eh wakeup
+ * when no CPU sees all busy requests as done or timed out.
+ */
+ smp_mb();
+
unsigned int busy = scsi_host_busy(shost);
spin_lock_irqsave(shost->host_lock, flags);
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 6e4112143c76..b43d876747b7 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1144,7 +1144,7 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device,
* The current SCSI handling on the host side does
* not correctly handle:
* INQUIRY command with page code parameter set to 0x80
- * MODE_SENSE command with cmd[2] == 0x1c
+ * MODE_SENSE and MODE_SENSE_10 command with cmd[2] == 0x1c
* MAINTENANCE_IN is not supported by HyperV FC passthrough
*
* Setup srb and scsi status so this won't be fatal.
@@ -1154,6 +1154,7 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device,
if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) ||
(stor_pkt->vm_srb.cdb[0] == MODE_SENSE) ||
+ (stor_pkt->vm_srb.cdb[0] == MODE_SENSE_10) ||
(stor_pkt->vm_srb.cdb[0] == MAINTENANCE_IN &&
hv_dev_is_fc(device))) {
vstor_packet->vm_srb.scsi_status = 0;
diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c
index 005fa2ef100f..5079d3271ee8 100644
--- a/drivers/slimbus/core.c
+++ b/drivers/slimbus/core.c
@@ -146,6 +146,7 @@ static void slim_dev_release(struct device *dev)
{
struct slim_device *sbdev = to_slim_device(dev);
+ of_node_put(sbdev->dev.of_node);
kfree(sbdev);
}
@@ -280,7 +281,6 @@ EXPORT_SYMBOL_GPL(slim_register_controller);
/* slim_remove_device: Remove the effect of slim_add_device() */
static void slim_remove_device(struct slim_device *sbdev)
{
- of_node_put(sbdev->dev.of_node);
device_unregister(&sbdev->dev);
}
@@ -366,6 +366,9 @@ static struct slim_device *find_slim_device(struct slim_controller *ctrl,
* @ctrl: Controller on which this device will be added/queried
* @e_addr: Enumeration address of the device to be queried
*
+ * Takes a reference to the embedded struct device which needs to be dropped
+ * after use.
+ *
* Return: pointer to a device if it has already reported. Creates a new
* device and returns pointer to it if the device has not yet enumerated.
*/
@@ -379,14 +382,27 @@ struct slim_device *slim_get_device(struct slim_controller *ctrl,
sbdev = slim_alloc_device(ctrl, e_addr, NULL);
if (!sbdev)
return ERR_PTR(-ENOMEM);
+
+ get_device(&sbdev->dev);
}
return sbdev;
}
EXPORT_SYMBOL_GPL(slim_get_device);
-static struct slim_device *of_find_slim_device(struct slim_controller *ctrl,
- struct device_node *np)
+/**
+ * of_slim_get_device() - get handle to a device using dt node.
+ *
+ * @ctrl: Controller on which this device will be queried
+ * @np: node pointer to device
+ *
+ * Takes a reference to the embedded struct device which needs to be dropped
+ * after use.
+ *
+ * Return: pointer to a device if it has been registered, otherwise NULL.
+ */
+struct slim_device *of_slim_get_device(struct slim_controller *ctrl,
+ struct device_node *np)
{
struct slim_device *sbdev;
struct device *dev;
@@ -399,21 +415,6 @@ static struct slim_device *of_find_slim_device(struct slim_controller *ctrl,
return NULL;
}
-
-/**
- * of_slim_get_device() - get handle to a device using dt node.
- *
- * @ctrl: Controller on which this device will be added/queried
- * @np: node pointer to device
- *
- * Return: pointer to a device if it has already reported. Creates a new
- * device and returns pointer to it if the device has not yet enumerated.
- */
-struct slim_device *of_slim_get_device(struct slim_controller *ctrl,
- struct device_node *np)
-{
- return of_find_slim_device(ctrl, np);
-}
EXPORT_SYMBOL_GPL(of_slim_get_device);
static int slim_device_alloc_laddr(struct slim_device *sbdev,
@@ -489,21 +490,24 @@ int slim_device_report_present(struct slim_controller *ctrl,
if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
ctrl->sched.clk_state, ret);
- goto slimbus_not_active;
+ goto out_put_rpm;
}
sbdev = slim_get_device(ctrl, e_addr);
- if (IS_ERR(sbdev))
- return -ENODEV;
+ if (IS_ERR(sbdev)) {
+ ret = -ENODEV;
+ goto out_put_rpm;
+ }
if (sbdev->is_laddr_valid) {
*laddr = sbdev->laddr;
- return 0;
+ ret = 0;
+ } else {
+ ret = slim_device_alloc_laddr(sbdev, true);
}
- ret = slim_device_alloc_laddr(sbdev, true);
-
-slimbus_not_active:
+ put_device(&sbdev->dev);
+out_put_rpm:
pm_runtime_mark_last_busy(ctrl->dev);
pm_runtime_put_autosuspend(ctrl->dev);
return ret;
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 340a1ff7e92b..2a8ae79a11af 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -445,6 +445,7 @@ config ARCH_R9A07G043
depends on RISCV_SBI
select ARCH_RZG2L
select AX45MP_L2_CACHE
+ select CACHEMAINT_FOR_DMA
select DMA_GLOBAL_POOL
select ERRATA_ANDES
select ERRATA_ANDES_CMO
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 47054da630d0..41b5b58cbfac 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -729,6 +729,7 @@ static int cdns_spi_probe(struct platform_device *pdev)
ctlr->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
+ ctlr->flags = SPI_CONTROLLER_MUST_TX;
if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c
index dadf558dd9c0..80a1a15de0bc 100644
--- a/drivers/spi/spi-hisi-kunpeng.c
+++ b/drivers/spi/spi-hisi-kunpeng.c
@@ -161,10 +161,8 @@ static const struct debugfs_reg32 hisi_spi_regs[] = {
static int hisi_spi_debugfs_init(struct hisi_spi *hs)
{
char name[32];
+ struct spi_controller *host = dev_get_drvdata(hs->dev);
- struct spi_controller *host;
-
- host = container_of(hs->dev, struct spi_controller, dev);
snprintf(name, 32, "hisi_spi%d", host->bus_num);
hs->debugfs = debugfs_create_dir(name, NULL);
if (IS_ERR(hs->debugfs))
diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c
index b8c572394aac..bce3d149bea1 100644
--- a/drivers/spi/spi-intel-pci.c
+++ b/drivers/spi/spi-intel-pci.c
@@ -81,6 +81,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x5794), (unsigned long)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x5825), (unsigned long)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x6e24), (unsigned long)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x7723), (unsigned long)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info },
diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c
index 262c11d977ea..f25b34a91756 100644
--- a/drivers/spi/spi-sprd-adi.c
+++ b/drivers/spi/spi-sprd-adi.c
@@ -528,7 +528,7 @@ static int sprd_adi_probe(struct platform_device *pdev)
pdev->id = of_alias_get_id(np, "spi");
num_chipselect = of_get_child_count(np);
- ctlr = spi_alloc_host(&pdev->dev, sizeof(struct sprd_adi));
+ ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(struct sprd_adi));
if (!ctlr)
return -ENOMEM;
@@ -536,10 +536,8 @@ static int sprd_adi_probe(struct platform_device *pdev)
sadi = spi_controller_get_devdata(ctlr);
sadi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(sadi->base)) {
- ret = PTR_ERR(sadi->base);
- goto put_ctlr;
- }
+ if (IS_ERR(sadi->base))
+ return PTR_ERR(sadi->base);
sadi->slave_vbase = (unsigned long)sadi->base +
data->slave_offset;
@@ -551,18 +549,15 @@ static int sprd_adi_probe(struct platform_device *pdev)
if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) {
sadi->hwlock =
devm_hwspin_lock_request_specific(&pdev->dev, ret);
- if (!sadi->hwlock) {
- ret = -ENXIO;
- goto put_ctlr;
- }
+ if (!sadi->hwlock)
+ return -ENXIO;
} else {
switch (ret) {
case -ENOENT:
dev_info(&pdev->dev, "no hardware spinlock supplied\n");
break;
default:
- dev_err_probe(&pdev->dev, ret, "failed to find hwlock id\n");
- goto put_ctlr;
+ return dev_err_probe(&pdev->dev, ret, "failed to find hwlock id\n");
}
}
@@ -579,26 +574,18 @@ static int sprd_adi_probe(struct platform_device *pdev)
ctlr->transfer_one = sprd_adi_transfer_one;
ret = devm_spi_register_controller(&pdev->dev, ctlr);
- if (ret) {
- dev_err(&pdev->dev, "failed to register SPI controller\n");
- goto put_ctlr;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to register SPI controller\n");
if (sadi->data->restart) {
ret = devm_register_restart_handler(&pdev->dev,
sadi->data->restart,
sadi);
- if (ret) {
- dev_err(&pdev->dev, "can not register restart handler\n");
- goto put_ctlr;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "can not register restart handler\n");
}
return 0;
-
-put_ctlr:
- spi_controller_put(ctlr);
- return ret;
}
static struct sprd_adi_data sc9860_data = {
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index e25df9990f82..2badacc7a91c 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -4761,17 +4761,9 @@ EXPORT_SYMBOL_GPL(spi_write_then_read);
/*-------------------------------------------------------------------------*/
-#if IS_ENABLED(CONFIG_OF_DYNAMIC)
-/* Must call put_device() when done with returned spi_device device */
-static struct spi_device *of_find_spi_device_by_node(struct device_node *node)
-{
- struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node);
-
- return dev ? to_spi_device(dev) : NULL;
-}
-
+#if IS_ENABLED(CONFIG_OF)
/* The spi controllers are not using spi_bus, so we find it with another way */
-static struct spi_controller *of_find_spi_controller_by_node(struct device_node *node)
+struct spi_controller *of_find_spi_controller_by_node(struct device_node *node)
{
struct device *dev;
@@ -4784,6 +4776,17 @@ static struct spi_controller *of_find_spi_controller_by_node(struct device_node
/* Reference got in class_find_device */
return container_of(dev, struct spi_controller, dev);
}
+EXPORT_SYMBOL_GPL(of_find_spi_controller_by_node);
+#endif
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+/* Must call put_device() when done with returned spi_device device */
+static struct spi_device *of_find_spi_device_by_node(struct device_node *node)
+{
+ struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node);
+
+ return dev ? to_spi_device(dev) : NULL;
+}
static int of_spi_notify(struct notifier_block *nb, unsigned long action,
void *arg)
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 5e6cf34929b5..c1888c42afdd 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -741,8 +741,11 @@ void iscsit_dec_session_usage_count(struct iscsit_session *sess)
spin_lock_bh(&sess->session_usage_lock);
sess->session_usage_count--;
- if (!sess->session_usage_count && sess->session_waiting_on_uc)
+ if (!sess->session_usage_count && sess->session_waiting_on_uc) {
+ spin_unlock_bh(&sess->session_usage_lock);
complete(&sess->session_waiting_on_uc_comp);
+ return;
+ }
spin_unlock_bh(&sess->session_usage_lock);
}
@@ -810,8 +813,11 @@ void iscsit_dec_conn_usage_count(struct iscsit_conn *conn)
spin_lock_bh(&conn->conn_usage_lock);
conn->conn_usage_count--;
- if (!conn->conn_usage_count && conn->conn_waiting_on_uc)
+ if (!conn->conn_usage_count && conn->conn_waiting_on_uc) {
+ spin_unlock_bh(&conn->conn_usage_lock);
complete(&conn->conn_waiting_on_uc_comp);
+ return;
+ }
spin_unlock_bh(&conn->conn_usage_lock);
}
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index 9f167ff8da7b..09120a538a40 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -1960,12 +1960,12 @@ static struct se_portal_group *sbp_make_tpg(struct se_wwn *wwn,
container_of(wwn, struct sbp_tport, tport_wwn);
struct sbp_tpg *tpg;
- unsigned long tpgt;
+ u16 tpgt;
int ret;
if (strstr(name, "tpgt_") != name)
return ERR_PTR(-EINVAL);
- if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ if (kstrtou16(name + 5, 10, &tpgt))
return ERR_PTR(-EINVAL);
if (tport->tpg) {
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index c5a932f48f74..3efe075ef7b2 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1658,7 +1658,7 @@ static int pci_fintek_rs485_config(struct uart_port *port, struct ktermios *term
}
static const struct serial_rs485 pci_fintek_rs485_supported = {
- .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND,
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND,
/* F81504/508/512 does not support RTS delay before or after send */
};
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 6ce6528f5c10..e6b0a55f0cfb 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1888,12 +1888,6 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
if (ret)
goto error;
- devm_pm_runtime_enable(port->se.dev);
-
- ret = uart_add_one_port(drv, uport);
- if (ret)
- goto error;
-
if (port->wakeup_irq > 0) {
device_init_wakeup(&pdev->dev, true);
ret = dev_pm_set_dedicated_wake_irq(&pdev->dev,
@@ -1901,11 +1895,16 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
if (ret) {
device_init_wakeup(&pdev->dev, false);
ida_free(&port_ida, uport->line);
- uart_remove_one_port(drv, uport);
goto error;
}
}
+ devm_pm_runtime_enable(port->se.dev);
+
+ ret = uart_add_one_port(drv, uport);
+ if (ret)
+ goto error;
+
return 0;
error:
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 9930023e924c..2805cad10511 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -3074,6 +3074,12 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
if (uport->cons && uport->dev)
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
+ /*
+ * TTY port has to be linked with the driver before register_console()
+ * in uart_configure_port(), because user-space could open the console
+ * immediately after.
+ */
+ tty_port_link_device(port, drv->tty_driver, uport->line);
uart_configure_port(drv, state, uport);
port->console = uart_console(uport);
diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
index 40543db621a1..6c454ae8a9c8 100644
--- a/drivers/ufs/host/ufs-amd-versal2.c
+++ b/drivers/ufs/host/ufs-amd-versal2.c
@@ -367,7 +367,7 @@ static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba,
{
int ret = 0;
- if (status == PRE_CHANGE) {
+ if (status == POST_CHANGE) {
ret = ufs_versal2_phy_init(hba);
if (ret)
dev_err(hba->dev, "Phy init failed (%d)\n", ret);
diff --git a/drivers/uio/uio_pci_generic_sva.c b/drivers/uio/uio_pci_generic_sva.c
index 97e9ab9a081a..4a46acd994a8 100644
--- a/drivers/uio/uio_pci_generic_sva.c
+++ b/drivers/uio/uio_pci_generic_sva.c
@@ -29,7 +29,7 @@ static int uio_pci_sva_open(struct uio_info *info, struct inode *inode)
struct uio_pci_sva_dev *udev = info->priv;
struct iommu_domain *domain;
- if (!udev && !udev->pdev)
+ if (!udev || !udev->pdev)
return -ENODEV;
domain = iommu_get_domain_for_dev(&udev->pdev->dev);
@@ -51,7 +51,7 @@ static int uio_pci_sva_release(struct uio_info *info, struct inode *inode)
{
struct uio_pci_sva_dev *udev = info->priv;
- if (!udev && !udev->pdev)
+ if (!udev || !udev->pdev)
return -ENODEV;
iommu_sva_unbind_device(udev->sva_handle);
diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
index d4d0f7d08c53..4be4a85005cb 100644
--- a/drivers/vfio/pci/vfio_pci_dmabuf.c
+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
@@ -20,6 +20,16 @@ struct vfio_pci_dma_buf {
u8 revoked : 1;
};
+static int vfio_pci_dma_buf_pin(struct dma_buf_attachment *attachment)
+{
+ return -EOPNOTSUPP;
+}
+
+static void vfio_pci_dma_buf_unpin(struct dma_buf_attachment *attachment)
+{
+ /* Do nothing */
+}
+
static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attachment)
{
@@ -76,6 +86,8 @@ static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)
}
static const struct dma_buf_ops vfio_pci_dmabuf_ops = {
+ .pin = vfio_pci_dma_buf_pin,
+ .unpin = vfio_pci_dma_buf_unpin,
.attach = vfio_pci_dma_buf_attach,
.map_dma_buf = vfio_pci_dma_buf_map,
.unmap_dma_buf = vfio_pci_dma_buf_unmap,
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 9ccedb3264fb..832e3da94b20 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -1836,53 +1836,35 @@ static ssize_t alarms_store(struct device *device,
struct w1_slave *sl = dev_to_w1_slave(device);
struct therm_info info;
u8 new_config_register[3]; /* array of data to be written */
- int temp, ret;
- char *token = NULL;
+ long long temp;
+ int ret = 0;
s8 tl, th; /* 1 byte per value + temp ring order */
- char *p_args, *orig;
-
- p_args = orig = kmalloc(size, GFP_KERNEL);
- /* Safe string copys as buf is const */
- if (!p_args) {
- dev_warn(device,
- "%s: error unable to allocate memory %d\n",
- __func__, -ENOMEM);
- return size;
- }
- strcpy(p_args, buf);
-
- /* Split string using space char */
- token = strsep(&p_args, " ");
-
- if (!token) {
- dev_info(device,
- "%s: error parsing args %d\n", __func__, -EINVAL);
- goto free_m;
- }
-
- /* Convert 1st entry to int */
- ret = kstrtoint (token, 10, &temp);
+ const char *p = buf;
+ char *endp;
+
+ temp = simple_strtoll(p, &endp, 10);
+ if (p == endp || *endp != ' ')
+ ret = -EINVAL;
+ else if (temp < INT_MIN || temp > INT_MAX)
+ ret = -ERANGE;
if (ret) {
dev_info(device,
"%s: error parsing args %d\n", __func__, ret);
- goto free_m;
+ return size;
}
tl = int_to_short(temp);
- /* Split string using space char */
- token = strsep(&p_args, " ");
- if (!token) {
- dev_info(device,
- "%s: error parsing args %d\n", __func__, -EINVAL);
- goto free_m;
- }
- /* Convert 2nd entry to int */
- ret = kstrtoint (token, 10, &temp);
+ p = endp + 1;
+ temp = simple_strtoll(p, &endp, 10);
+ if (p == endp)
+ ret = -EINVAL;
+ else if (temp < INT_MIN || temp > INT_MAX)
+ ret = -ERANGE;
if (ret) {
dev_info(device,
"%s: error parsing args %d\n", __func__, ret);
- goto free_m;
+ return size;
}
/* Prepare to cast to short by eliminating out of range values */
@@ -1905,7 +1887,7 @@ static ssize_t alarms_store(struct device *device,
dev_info(device,
"%s: error reading from the slave device %d\n",
__func__, ret);
- goto free_m;
+ return size;
}
/* Write data in the device RAM */
@@ -1913,7 +1895,7 @@ static ssize_t alarms_store(struct device *device,
dev_info(device,
"%s: Device not supported by the driver %d\n",
__func__, -ENODEV);
- goto free_m;
+ return size;
}
ret = SLAVE_SPECIFIC_FUNC(sl)->write_data(sl, new_config_register);
@@ -1922,10 +1904,6 @@ static ssize_t alarms_store(struct device *device,
"%s: error writing to the slave device %d\n",
__func__, ret);
-free_m:
- /* free allocated memory */
- kfree(orig);
-
return size;
}
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 002d2639aa12..5f78b0a0b766 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -758,8 +758,6 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
if (err < 0) {
dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__,
sl->name);
- dev->slave_count--;
- w1_family_put(sl->family);
atomic_dec(&sl->master->refcnt);
kfree(sl);
return err;
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index 0c51edfd13dc..7d5117e5efe0 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -1262,6 +1262,7 @@ static void scsiback_remove(struct xenbus_device *dev)
gnttab_page_cache_shrink(&info->free_pages, 0);
dev_set_drvdata(&dev->dev, NULL);
+ kfree(info);
}
static int scsiback_probe(struct xenbus_device *dev,
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c
index e0d34e4e9076..af7f72abbb76 100644
--- a/fs/9p/vfs_dir.c
+++ b/fs/9p/vfs_dir.c
@@ -242,6 +242,7 @@ const struct file_operations v9fs_dir_operations = {
.iterate_shared = v9fs_dir_readdir,
.open = v9fs_file_open,
.release = v9fs_dir_release,
+ .setlease = simple_nosetlease,
};
const struct file_operations v9fs_dir_operations_dotl = {
@@ -251,4 +252,5 @@ const struct file_operations v9fs_dir_operations_dotl = {
.open = v9fs_file_open,
.release = v9fs_dir_release,
.fsync = v9fs_file_fsync_dotl,
+ .setlease = simple_nosetlease,
};
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index d8ca5b6e88e0..2833b44f4b4f 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -498,28 +498,6 @@ static int btree_migrate_folio(struct address_space *mapping,
#define btree_migrate_folio NULL
#endif
-static int btree_writepages(struct address_space *mapping,
- struct writeback_control *wbc)
-{
- int ret;
-
- if (wbc->sync_mode == WB_SYNC_NONE) {
- struct btrfs_fs_info *fs_info;
-
- if (wbc->for_kupdate)
- return 0;
-
- fs_info = inode_to_fs_info(mapping->host);
- /* this is a bit racy, but that's ok */
- ret = __percpu_counter_compare(&fs_info->dirty_metadata_bytes,
- BTRFS_DIRTY_METADATA_THRESH,
- fs_info->dirty_metadata_batch);
- if (ret < 0)
- return 0;
- }
- return btree_write_cache_pages(mapping, wbc);
-}
-
static bool btree_release_folio(struct folio *folio, gfp_t gfp_flags)
{
if (folio_test_writeback(folio) || folio_test_dirty(folio))
@@ -1661,7 +1639,7 @@ static void backup_super_roots(struct btrfs_fs_info *info)
btrfs_set_backup_chunk_root_level(root_backup,
btrfs_header_level(info->chunk_root->node));
- if (!btrfs_fs_compat_ro(info, BLOCK_GROUP_TREE)) {
+ if (!btrfs_fs_incompat(info, EXTENT_TREE_V2)) {
struct btrfs_root *extent_root = btrfs_extent_root(info, 0);
struct btrfs_root *csum_root = btrfs_csum_root(info, 0);
@@ -3255,6 +3233,15 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
return 0;
}
+static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
+{
+ if (!sb_rdonly(fs_info->sb))
+ return false;
+ if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
+ return true;
+ return false;
+}
+
int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices)
{
u32 sectorsize;
@@ -3363,6 +3350,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR)
WRITE_ONCE(fs_info->fs_error, -EUCLEAN);
+ /* If the fs has any rescue options, no transaction is allowed. */
+ if (fs_is_full_ro(fs_info))
+ WRITE_ONCE(fs_info->fs_error, -EROFS);
+
/* Set up fs_info before parsing mount options */
nodesize = btrfs_super_nodesize(disk_super);
sectorsize = btrfs_super_sectorsize(disk_super);
@@ -3489,6 +3480,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
fs_info->generation == btrfs_super_uuid_tree_generation(disk_super))
set_bit(BTRFS_FS_UPDATE_UUID_TREE_GEN, &fs_info->flags);
+ if (unlikely(btrfs_verify_dev_items(fs_info))) {
+ ret = -EUCLEAN;
+ goto fail_block_groups;
+ }
ret = btrfs_verify_dev_extents(fs_info);
if (ret) {
btrfs_err(fs_info,
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index a4b74023618d..f6cca3c97166 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2286,8 +2286,7 @@ void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start,
}
}
-int btree_write_cache_pages(struct address_space *mapping,
- struct writeback_control *wbc)
+int btree_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
struct btrfs_eb_write_context ctx = { .wbc = wbc };
struct btrfs_fs_info *fs_info = inode_to_fs_info(mapping->host);
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 02ebb2f238af..73571d5d3d5a 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -237,8 +237,7 @@ void extent_write_locked_range(struct inode *inode, const struct folio *locked_f
u64 start, u64 end, struct writeback_control *wbc,
bool pages_dirty);
int btrfs_writepages(struct address_space *mapping, struct writeback_control *wbc);
-int btree_write_cache_pages(struct address_space *mapping,
- struct writeback_control *wbc);
+int btree_writepages(struct address_space *mapping, struct writeback_control *wbc);
void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start, u64 end);
void btrfs_readahead(struct readahead_control *rac);
int set_folio_extent_mapped(struct folio *folio);
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 0f7e1ef27891..8ffbc40ebe45 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -264,6 +264,14 @@ enum {
BTRFS_MOUNT_REF_TRACKER = (1ULL << 33),
};
+/* These mount options require a full read-only fs, no new transaction is allowed. */
+#define BTRFS_MOUNT_FULL_RO_MASK \
+ (BTRFS_MOUNT_NOLOGREPLAY | \
+ BTRFS_MOUNT_IGNOREBADROOTS | \
+ BTRFS_MOUNT_IGNOREDATACSUMS | \
+ BTRFS_MOUNT_IGNOREMETACSUMS | \
+ BTRFS_MOUNT_IGNORESUPERFLAGS)
+
/*
* Compat flags that we support. If any incompat flags are set other than the
* ones specified below then we will fail to mount
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 2d9d38b82daa..6cffcf0c3e7a 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2798,7 +2798,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
nritems = btrfs_header_nritems(eb);
for (wc->log_slot = 0; wc->log_slot < nritems; wc->log_slot++) {
- struct btrfs_inode_item *inode_item;
+ struct btrfs_inode_item *inode_item = NULL;
btrfs_item_key_to_cpu(eb, &wc->log_key, wc->log_slot);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 13c514684cfb..8a08412f3529 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1364,7 +1364,9 @@ struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev,
(bytenr + BTRFS_SUPER_INFO_SIZE) >> PAGE_SHIFT);
}
+ filemap_invalidate_lock(mapping);
page = read_cache_page_gfp(mapping, bytenr >> PAGE_SHIFT, GFP_NOFS);
+ filemap_invalidate_unlock(mapping);
if (IS_ERR(page))
return ERR_CAST(page);
@@ -7257,6 +7259,7 @@ static int read_one_dev(struct extent_buffer *leaf,
return -EINVAL;
}
}
+ set_bit(BTRFS_DEV_STATE_ITEM_FOUND, &device->dev_state);
set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state);
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) &&
!test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) {
@@ -8083,6 +8086,45 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info)
}
/*
+ * Ensure that all devices registered in the fs have their device items in the
+ * chunk tree.
+ *
+ * Return true if unexpected device is found.
+ * Return false otherwise.
+ */
+bool btrfs_verify_dev_items(const struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_fs_devices *seed_devs;
+ struct btrfs_device *dev;
+ bool ret = false;
+
+ mutex_lock(&uuid_mutex);
+ list_for_each_entry(dev, &fs_info->fs_devices->devices, dev_list) {
+ if (!test_bit(BTRFS_DEV_STATE_ITEM_FOUND, &dev->dev_state)) {
+ btrfs_err(fs_info,
+ "devid %llu path %s is registered but not found in chunk tree",
+ dev->devid, btrfs_dev_name(dev));
+ ret = true;
+ }
+ }
+ list_for_each_entry(seed_devs, &fs_info->fs_devices->seed_list, seed_list) {
+ list_for_each_entry(dev, &seed_devs->devices, dev_list) {
+ if (!test_bit(BTRFS_DEV_STATE_ITEM_FOUND, &dev->dev_state)) {
+ btrfs_err(fs_info,
+ "devid %llu path %s is registered but not found in chunk tree",
+ dev->devid, btrfs_dev_name(dev));
+ ret = true;
+ }
+ }
+ }
+ mutex_unlock(&uuid_mutex);
+ if (ret)
+ btrfs_err(fs_info,
+"remove the above devices or use 'btrfs device scan --forget <dev>' to unregister them before mount");
+ return ret;
+}
+
+/*
* Check whether the given block group or device is pinned by any inode being
* used as a swapfile.
*/
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 34b854c1a303..f20abeb16bce 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -100,6 +100,9 @@ enum btrfs_raid_types {
#define BTRFS_DEV_STATE_FLUSH_SENT (4)
#define BTRFS_DEV_STATE_NO_READA (5)
+/* Set when the device item is found in chunk tree, used to catch unexpected registered device. */
+#define BTRFS_DEV_STATE_ITEM_FOUND (7)
+
/* Special value encoding failure to write primary super block. */
#define BTRFS_SUPER_PRIMARY_WRITE_ERROR (INT_MAX / 2)
@@ -893,6 +896,7 @@ enum btrfs_raid_types __attribute_const__ btrfs_bg_flags_to_raid_index(u64 flags
int btrfs_bg_type_to_factor(u64 flags);
const char *btrfs_bg_type_to_raid_name(u64 flags);
int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info);
+bool btrfs_verify_dev_items(const struct btrfs_fs_info *fs_info);
bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical);
bool btrfs_pinned_by_swapfile(struct btrfs_fs_info *fs_info, void *ptr);
diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c
index 6caba8be7c84..10ed48d4a846 100644
--- a/fs/btrfs/zlib.c
+++ b/fs/btrfs/zlib.c
@@ -139,6 +139,7 @@ static int copy_data_into_buffer(struct address_space *mapping,
data_in = kmap_local_folio(folio, offset);
memcpy(workspace->buf + cur - filepos, data_in, copy_length);
kunmap_local(data_in);
+ folio_put(folio);
cur += copy_length;
}
return 0;
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 86d7aa594ea9..804588524cd5 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -2214,6 +2214,7 @@ const struct file_operations ceph_dir_fops = {
.fsync = ceph_fsync,
.lock = ceph_lock,
.flock = ceph_flock,
+ .setlease = simple_nosetlease,
};
const struct file_operations ceph_snapdir_fops = {
@@ -2221,6 +2222,7 @@ const struct file_operations ceph_snapdir_fops = {
.llseek = ceph_dir_llseek,
.open = ceph_open,
.release = ceph_release,
+ .setlease = simple_nosetlease,
};
const struct inode_operations ceph_dir_iops = {
diff --git a/fs/dcache.c b/fs/dcache.c
index dc2fff4811d1..66dd1bb830d1 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1104,6 +1104,16 @@ struct dentry *d_find_alias_rcu(struct inode *inode)
return de;
}
+/**
+ * d_dispose_if_unused - move unreferenced dentries to shrink list
+ * @dentry: dentry in question
+ * @dispose: head of shrink list
+ *
+ * If dentry has no external references, move it to shrink list.
+ *
+ * NOTE!!! The caller is responsible for preventing eviction of the dentry by
+ * holding dentry->d_inode->i_lock or equivalent.
+ */
void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose)
{
spin_lock(&dentry->d_lock);
diff --git a/fs/efivarfs/vars.c b/fs/efivarfs/vars.c
index 6edc10958ecf..70e13db260db 100644
--- a/fs/efivarfs/vars.c
+++ b/fs/efivarfs/vars.c
@@ -552,7 +552,7 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
err = __efivar_entry_get(entry, attributes, size, data);
efivar_unlock();
- return 0;
+ return err;
}
/**
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 6800886c4d10..5444fc706ac7 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -2492,7 +2492,9 @@ static void wakeup_dirtytime_writeback(struct work_struct *w)
wb_wakeup(wb);
}
rcu_read_unlock();
- schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ if (dirtytime_expire_interval)
+ schedule_delayed_work(&dirtytime_work,
+ round_jiffies_relative(dirtytime_expire_interval * HZ));
}
static int dirtytime_interval_handler(const struct ctl_table *table, int write,
@@ -2501,8 +2503,12 @@ static int dirtytime_interval_handler(const struct ctl_table *table, int write,
int ret;
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- if (ret == 0 && write)
- mod_delayed_work(system_percpu_wq, &dirtytime_work, 0);
+ if (ret == 0 && write) {
+ if (dirtytime_expire_interval)
+ mod_delayed_work(system_percpu_wq, &dirtytime_work, 0);
+ else
+ cancel_delayed_work_sync(&dirtytime_work);
+ }
return ret;
}
@@ -2519,7 +2525,9 @@ static const struct ctl_table vm_fs_writeback_table[] = {
static int __init start_dirtytime_writeback(void)
{
- schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ if (dirtytime_expire_interval)
+ schedule_delayed_work(&dirtytime_work,
+ round_jiffies_relative(dirtytime_expire_interval * HZ));
register_sysctl_init("vm", vm_fs_writeback_table);
return 0;
}
@@ -2750,8 +2758,13 @@ static void wait_sb_inodes(struct super_block *sb)
* The mapping can appear untagged while still on-list since we
* do not have the mapping lock. Skip it here, wb completion
* will remove it.
+ *
+ * If the mapping does not have data integrity semantics,
+ * there's no need to wait for the writeout to complete, as the
+ * mapping cannot guarantee that data is persistently stored.
*/
- if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK))
+ if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) ||
+ mapping_no_data_integrity(mapping))
continue;
spin_unlock_irq(&sb->s_inode_wblist_lock);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 4b6b3d2758ff..3927cb069236 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -32,9 +32,9 @@ struct dentry_bucket {
spinlock_t lock;
};
-#define HASH_BITS 5
-#define HASH_SIZE (1 << HASH_BITS)
-static struct dentry_bucket dentry_hash[HASH_SIZE];
+#define FUSE_HASH_BITS 5
+#define FUSE_HASH_SIZE (1 << FUSE_HASH_BITS)
+static struct dentry_bucket dentry_hash[FUSE_HASH_SIZE];
struct delayed_work dentry_tree_work;
/* Minimum invalidation work queue frequency */
@@ -83,7 +83,7 @@ MODULE_PARM_DESC(inval_wq,
static inline struct dentry_bucket *get_dentry_bucket(struct dentry *dentry)
{
- int i = hash_ptr(dentry, HASH_BITS);
+ int i = hash_ptr(dentry, FUSE_HASH_BITS);
return &dentry_hash[i];
}
@@ -164,25 +164,31 @@ static void fuse_dentry_tree_work(struct work_struct *work)
struct rb_node *node;
int i;
- for (i = 0; i < HASH_SIZE; i++) {
+ for (i = 0; i < FUSE_HASH_SIZE; i++) {
spin_lock(&dentry_hash[i].lock);
node = rb_first(&dentry_hash[i].tree);
while (node) {
fd = rb_entry(node, struct fuse_dentry, node);
- if (time_after64(get_jiffies_64(), fd->time)) {
- rb_erase(&fd->node, &dentry_hash[i].tree);
- RB_CLEAR_NODE(&fd->node);
+ if (!time_before64(fd->time, get_jiffies_64()))
+ break;
+
+ rb_erase(&fd->node, &dentry_hash[i].tree);
+ RB_CLEAR_NODE(&fd->node);
+ spin_lock(&fd->dentry->d_lock);
+ /* If dentry is still referenced, let next dput release it */
+ fd->dentry->d_flags |= DCACHE_OP_DELETE;
+ spin_unlock(&fd->dentry->d_lock);
+ d_dispose_if_unused(fd->dentry, &dispose);
+ if (need_resched()) {
spin_unlock(&dentry_hash[i].lock);
- d_dispose_if_unused(fd->dentry, &dispose);
cond_resched();
spin_lock(&dentry_hash[i].lock);
- } else
- break;
+ }
node = rb_first(&dentry_hash[i].tree);
}
spin_unlock(&dentry_hash[i].lock);
- shrink_dentry_list(&dispose);
}
+ shrink_dentry_list(&dispose);
if (inval_wq)
schedule_delayed_work(&dentry_tree_work,
@@ -213,7 +219,7 @@ void fuse_dentry_tree_init(void)
{
int i;
- for (i = 0; i < HASH_SIZE; i++) {
+ for (i = 0; i < FUSE_HASH_SIZE; i++) {
spin_lock_init(&dentry_hash[i].lock);
dentry_hash[i].tree = RB_ROOT;
}
@@ -227,7 +233,7 @@ void fuse_dentry_tree_cleanup(void)
inval_wq = 0;
cancel_delayed_work_sync(&dentry_tree_work);
- for (i = 0; i < HASH_SIZE; i++)
+ for (i = 0; i < FUSE_HASH_SIZE; i++)
WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree));
}
@@ -479,18 +485,12 @@ static int fuse_dentry_init(struct dentry *dentry)
return 0;
}
-static void fuse_dentry_prune(struct dentry *dentry)
+static void fuse_dentry_release(struct dentry *dentry)
{
struct fuse_dentry *fd = dentry->d_fsdata;
if (!RB_EMPTY_NODE(&fd->node))
fuse_dentry_tree_del_node(dentry);
-}
-
-static void fuse_dentry_release(struct dentry *dentry)
-{
- struct fuse_dentry *fd = dentry->d_fsdata;
-
kfree_rcu(fd, rcu);
}
@@ -527,7 +527,6 @@ const struct dentry_operations fuse_dentry_operations = {
.d_revalidate = fuse_dentry_revalidate,
.d_delete = fuse_dentry_delete,
.d_init = fuse_dentry_init,
- .d_prune = fuse_dentry_prune,
.d_release = fuse_dentry_release,
.d_automount = fuse_dentry_automount,
};
@@ -1584,8 +1583,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
{
int err = -ENOTDIR;
struct inode *parent;
- struct dentry *dir;
- struct dentry *entry;
+ struct dentry *dir = NULL;
+ struct dentry *entry = NULL;
parent = fuse_ilookup(fc, parent_nodeid, NULL);
if (!parent)
@@ -1598,11 +1597,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
dir = d_find_alias(parent);
if (!dir)
goto put_parent;
-
- entry = start_removing_noperm(dir, name);
- dput(dir);
- if (IS_ERR(entry))
- goto put_parent;
+ while (!entry) {
+ struct dentry *child = try_lookup_noperm(name, dir);
+ if (!child || IS_ERR(child))
+ goto put_parent;
+ entry = start_removing_dentry(dir, child);
+ dput(child);
+ if (IS_ERR(entry))
+ goto put_parent;
+ if (!d_same_name(entry, dir, name)) {
+ end_removing(entry);
+ entry = NULL;
+ }
+ }
fuse_dir_changed(parent);
if (!(flags & FUSE_EXPIRE_ONLY))
@@ -1640,6 +1647,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
end_removing(entry);
put_parent:
+ dput(dir);
iput(parent);
return err;
}
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 01bc894e9c2b..3b2a171e652f 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -3200,8 +3200,10 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags)
inode->i_fop = &fuse_file_operations;
inode->i_data.a_ops = &fuse_file_aops;
- if (fc->writeback_cache)
+ if (fc->writeback_cache) {
mapping_set_writeback_may_deadlock_on_reclaim(&inode->i_data);
+ mapping_set_no_data_integrity(&inode->i_data);
+ }
INIT_LIST_HEAD(&fi->write_files);
INIT_LIST_HEAD(&fi->queued_writes);
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index b2d23c98c996..86376f0dbf3a 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -1608,6 +1608,7 @@ const struct file_operations gfs2_dir_fops = {
.lock = gfs2_lock,
.flock = gfs2_flock,
.llseek = default_llseek,
+ .setlease = simple_nosetlease,
.fop_flags = FOP_ASYNC_LOCK,
};
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index fd9a2cf95620..6beb876658c0 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -851,6 +851,7 @@ static struct folio *__iomap_get_folio(struct iomap_iter *iter,
}
folio_get(folio);
+ folio_wait_stable(folio);
return folio;
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 8f9ea79b7882..e3654f4a1b9a 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -66,6 +66,7 @@ const struct file_operations nfs_dir_operations = {
.open = nfs_opendir,
.release = nfs_closedir,
.fsync = nfs_fsync_dir,
+ .setlease = simple_nosetlease,
};
const struct address_space_operations nfs_dir_aops = {
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 7317f26892c5..7f43e890d356 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -431,8 +431,6 @@ void nfs42_ssc_unregister_ops(void)
static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease,
void **priv)
{
- if (!S_ISREG(file_inode(file)->i_mode))
- return -EINVAL;
return nfs4_proc_setlease(file, arg, lease, priv);
}
diff --git a/fs/readdir.c b/fs/readdir.c
index 7764b8638978..73707b6816e9 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -316,6 +316,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct getdents_callback buf = {
.ctx.actor = filldir,
.ctx.count = count,
+ .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR,
.current_dir = dirent
};
int error;
@@ -400,6 +401,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
struct getdents_callback64 buf = {
.ctx.actor = filldir64,
.ctx.count = count,
+ .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR,
.current_dir = dirent
};
int error;
@@ -569,6 +571,7 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct compat_getdents_callback buf = {
.ctx.actor = compat_filldir,
.ctx.count = count,
+ .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR,
.current_dir = dirent,
};
int error;
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 360b00854115..ac55193bf398 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -458,7 +458,10 @@ static int romfs_fill_super(struct super_block *sb, struct fs_context *fc)
#ifdef CONFIG_BLOCK
if (!sb->s_mtd) {
- sb_set_blocksize(sb, ROMBSIZE);
+ if (!sb_set_blocksize(sb, ROMBSIZE)) {
+ errorf(fc, "romfs: unable to set blocksize\n");
+ return -EINVAL;
+ }
} else {
sb->s_blocksize = ROMBSIZE;
sb->s_blocksize_bits = blksize_bits(ROMBSIZE);
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index d9664634144d..a3dc7cb1ab54 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1149,9 +1149,6 @@ cifs_setlease(struct file *file, int arg, struct file_lease **lease, void **priv
struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data;
- if (!S_ISREG(inode->i_mode))
- return -EINVAL;
-
/* Check if file is oplocked if this is request for new lease */
if (arg == F_UNLCK ||
((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
@@ -1712,6 +1709,7 @@ const struct file_operations cifs_dir_ops = {
.remap_file_range = cifs_remap_file_range,
.llseek = generic_file_llseek,
.fsync = cifs_dir_fsync,
+ .setlease = simple_nosetlease,
};
static void
diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
index f585359684d4..e4273932e7e4 100644
--- a/fs/smb/server/transport_rdma.c
+++ b/fs/smb/server/transport_rdma.c
@@ -1353,14 +1353,12 @@ static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nen
static int get_mapped_sg_list(struct ib_device *device, void *buf, int size,
struct scatterlist *sg_list, int nentries,
- enum dma_data_direction dir)
+ enum dma_data_direction dir, int *npages)
{
- int npages;
-
- npages = get_sg_list(buf, size, sg_list, nentries);
- if (npages < 0)
+ *npages = get_sg_list(buf, size, sg_list, nentries);
+ if (*npages < 0)
return -EINVAL;
- return ib_dma_map_sg(device, sg_list, npages, dir);
+ return ib_dma_map_sg(device, sg_list, *npages, dir);
}
static int post_sendmsg(struct smbdirect_socket *sc,
@@ -1431,12 +1429,13 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc,
for (i = 0; i < niov; i++) {
struct ib_sge *sge;
int sg_cnt;
+ int npages;
sg_init_table(sg, SMBDIRECT_SEND_IO_MAX_SGE - 1);
sg_cnt = get_mapped_sg_list(sc->ib.dev,
iov[i].iov_base, iov[i].iov_len,
sg, SMBDIRECT_SEND_IO_MAX_SGE - 1,
- DMA_TO_DEVICE);
+ DMA_TO_DEVICE, &npages);
if (sg_cnt <= 0) {
pr_err("failed to map buffer\n");
ret = -ENOMEM;
@@ -1444,7 +1443,7 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc,
} else if (sg_cnt + msg->num_sge > SMBDIRECT_SEND_IO_MAX_SGE) {
pr_err("buffer not fitted into sges\n");
ret = -E2BIG;
- ib_dma_unmap_sg(sc->ib.dev, sg, sg_cnt,
+ ib_dma_unmap_sg(sc->ib.dev, sg, npages,
DMA_TO_DEVICE);
goto err;
}
@@ -2708,6 +2707,7 @@ int ksmbd_rdma_init(void)
{
int ret;
+ smb_direct_port = SMB_DIRECT_PORT_INFINIBAND;
smb_direct_listener.cm_id = NULL;
ret = ib_register_client(&smb_direct_ib_client);
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index f891344bd76b..b8e648b8300f 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -1227,7 +1227,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
}
/**
- * ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal
+ * ksmbd_vfs_kern_path_start_removing() - lookup a file and get path info prior to removal
* @work: work
* @filepath: file path that is relative to share
* @flags: lookup flags
diff --git a/fs/vboxsf/dir.c b/fs/vboxsf/dir.c
index 42bedc4ec7af..230d7589d15c 100644
--- a/fs/vboxsf/dir.c
+++ b/fs/vboxsf/dir.c
@@ -186,6 +186,7 @@ const struct file_operations vboxsf_dir_fops = {
.release = vboxsf_dir_release,
.read = generic_read_dir,
.llseek = generic_file_llseek,
+ .setlease = simple_nosetlease,
};
/*
diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
index 1fff717cae51..4d679d2a206b 100644
--- a/include/asm-generic/tlb.h
+++ b/include/asm-generic/tlb.h
@@ -46,7 +46,8 @@
*
* The mmu_gather API consists of:
*
- * - tlb_gather_mmu() / tlb_gather_mmu_fullmm() / tlb_finish_mmu()
+ * - tlb_gather_mmu() / tlb_gather_mmu_fullmm() / tlb_gather_mmu_vma() /
+ * tlb_finish_mmu()
*
* start and finish a mmu_gather
*
@@ -364,6 +365,20 @@ struct mmu_gather {
unsigned int vma_huge : 1;
unsigned int vma_pfn : 1;
+ /*
+ * Did we unshare (unmap) any shared page tables? For now only
+ * used for hugetlb PMD table sharing.
+ */
+ unsigned int unshared_tables : 1;
+
+ /*
+ * Did we unshare any page tables such that they are now exclusive
+ * and could get reused+modified by the new owner? When setting this
+ * flag, "unshared_tables" will be set as well. For now only used
+ * for hugetlb PMD table sharing.
+ */
+ unsigned int fully_unshared_tables : 1;
+
unsigned int batch_count;
#ifndef CONFIG_MMU_GATHER_NO_GATHER
@@ -400,6 +415,7 @@ static inline void __tlb_reset_range(struct mmu_gather *tlb)
tlb->cleared_pmds = 0;
tlb->cleared_puds = 0;
tlb->cleared_p4ds = 0;
+ tlb->unshared_tables = 0;
/*
* Do not reset mmu_gather::vma_* fields here, we do not
* call into tlb_start_vma() again to set them if there is an
@@ -484,7 +500,7 @@ static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb)
* these bits.
*/
if (!(tlb->freed_tables || tlb->cleared_ptes || tlb->cleared_pmds ||
- tlb->cleared_puds || tlb->cleared_p4ds))
+ tlb->cleared_puds || tlb->cleared_p4ds || tlb->unshared_tables))
return;
tlb_flush(tlb);
@@ -773,6 +789,63 @@ static inline bool huge_pmd_needs_flush(pmd_t oldpmd, pmd_t newpmd)
}
#endif
+#ifdef CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING
+static inline void tlb_unshare_pmd_ptdesc(struct mmu_gather *tlb, struct ptdesc *pt,
+ unsigned long addr)
+{
+ /*
+ * The caller must make sure that concurrent unsharing + exclusive
+ * reuse is impossible until tlb_flush_unshared_tables() was called.
+ */
+ VM_WARN_ON_ONCE(!ptdesc_pmd_is_shared(pt));
+ ptdesc_pmd_pts_dec(pt);
+
+ /* Clearing a PUD pointing at a PMD table with PMD leaves. */
+ tlb_flush_pmd_range(tlb, addr & PUD_MASK, PUD_SIZE);
+
+ /*
+ * If the page table is now exclusively owned, we fully unshared
+ * a page table.
+ */
+ if (!ptdesc_pmd_is_shared(pt))
+ tlb->fully_unshared_tables = true;
+ tlb->unshared_tables = true;
+}
+
+static inline void tlb_flush_unshared_tables(struct mmu_gather *tlb)
+{
+ /*
+ * As soon as the caller drops locks to allow for reuse of
+ * previously-shared tables, these tables could get modified and
+ * even reused outside of hugetlb context, so we have to make sure that
+ * any page table walkers (incl. TLB, GUP-fast) are aware of that
+ * change.
+ *
+ * Even if we are not fully unsharing a PMD table, we must
+ * flush the TLB for the unsharer now.
+ */
+ if (tlb->unshared_tables)
+ tlb_flush_mmu_tlbonly(tlb);
+
+ /*
+ * Similarly, we must make sure that concurrent GUP-fast will not
+ * walk previously-shared page tables that are getting modified+reused
+ * elsewhere. So broadcast an IPI to wait for any concurrent GUP-fast.
+ *
+ * We only perform this when we are the last sharer of a page table,
+ * as the IPI will reach all CPUs: any GUP-fast.
+ *
+ * Note that on configs where tlb_remove_table_sync_one() is a NOP,
+ * the expectation is that the tlb_flush_mmu_tlbonly() would have issued
+ * required IPIs already for us.
+ */
+ if (tlb->fully_unshared_tables) {
+ tlb_remove_table_sync_one();
+ tlb->fully_unshared_tables = false;
+ }
+}
+#endif /* CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING */
+
#endif /* CONFIG_MMU */
#endif /* _ASM_GENERIC__TLB_H */
diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h
index 70a7991f784f..eb29e5309f0a 100644
--- a/include/drm/drm_pagemap.h
+++ b/include/drm/drm_pagemap.h
@@ -209,6 +209,19 @@ struct drm_pagemap_devmem_ops {
struct dma_fence *pre_migrate_fence);
};
+#if IS_ENABLED(CONFIG_ZONE_DEVICE)
+
+struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
+
+#else
+
+static inline struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page)
+{
+ return NULL;
+}
+
+#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */
+
/**
* struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation
*
@@ -233,6 +246,8 @@ struct drm_pagemap_devmem {
struct dma_fence *pre_migrate_fence;
};
+#if IS_ENABLED(CONFIG_ZONE_DEVICE)
+
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
@@ -243,8 +258,6 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation);
const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void);
-struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
-
void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation,
struct device *dev, struct mm_struct *mm,
const struct drm_pagemap_devmem_ops *ops,
@@ -256,4 +269,6 @@ int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
struct mm_struct *mm,
unsigned long timeslice_ms);
+#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */
+
#endif
diff --git a/include/dt-bindings/power/qcom,rpmhpd.h b/include/dt-bindings/power/qcom,rpmhpd.h
index 50e7c886709d..06851363ae0e 100644
--- a/include/dt-bindings/power/qcom,rpmhpd.h
+++ b/include/dt-bindings/power/qcom,rpmhpd.h
@@ -264,5 +264,6 @@
#define SC8280XP_NSP 13
#define SC8280XP_QPHY 14
#define SC8280XP_XO 15
+#define SC8280XP_MXC_AO 16
#endif
diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
index 469186df7826..08965970c17d 100644
--- a/include/hyperv/hvhdk.h
+++ b/include/hyperv/hvhdk.h
@@ -800,6 +800,53 @@ struct hv_x64_memory_intercept_message {
u8 instruction_bytes[16];
} __packed;
+#if IS_ENABLED(CONFIG_ARM64)
+union hv_arm64_vp_execution_state {
+ u16 as_uint16;
+ struct {
+ u16 cpl:2; /* Exception Level (EL) */
+ u16 debug_active:1;
+ u16 interruption_pending:1;
+ u16 vtl:4;
+ u16 virtualization_fault_active:1;
+ u16 reserved:7;
+ } __packed;
+};
+
+struct hv_arm64_intercept_message_header {
+ u32 vp_index;
+ u8 instruction_length;
+ u8 intercept_access_type;
+ union hv_arm64_vp_execution_state execution_state;
+ u64 pc;
+ u64 cpsr;
+} __packed;
+
+union hv_arm64_memory_access_info {
+ u8 as_uint8;
+ struct {
+ u8 gva_valid:1;
+ u8 gva_gpa_valid:1;
+ u8 hypercall_output_pending:1;
+ u8 reserved:5;
+ } __packed;
+};
+
+struct hv_arm64_memory_intercept_message {
+ struct hv_arm64_intercept_message_header header;
+ u32 cache_type; /* enum hv_cache_type */
+ u8 instruction_byte_count;
+ union hv_arm64_memory_access_info memory_access_info;
+ u16 reserved1;
+ u8 instruction_bytes[4];
+ u32 reserved2;
+ u64 guest_virtual_address;
+ u64 guest_physical_address;
+ u64 syndrome;
+} __packed;
+
+#endif /* CONFIG_ARM64 */
+
/*
* Dispatch state for the VP communicated by the hypervisor to the
* VP-dispatching thread in the root on return from HVCALL_DISPATCH_VP.
diff --git a/include/linux/cma.h b/include/linux/cma.h
index 62d9c1cf6326..2e6931735880 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -57,6 +57,15 @@ extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long e
extern void cma_reserve_pages_on_error(struct cma *cma);
+#ifdef CONFIG_DMA_CMA
+extern bool cma_skip_dt_default_reserved_mem(void);
+#else
+static inline bool cma_skip_dt_default_reserved_mem(void)
+{
+ return false;
+}
+#endif
+
#ifdef CONFIG_CMA
struct folio *cma_alloc_folio(struct cma *cma, int order, gfp_t gfp);
bool cma_free_folio(struct cma *cma, const struct folio *folio);
diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h
index cd8e0f0a634b..bbc67ec513ed 100644
--- a/include/linux/device/driver.h
+++ b/include/linux/device/driver.h
@@ -85,6 +85,8 @@ enum probe_type {
* uevent.
* @p: Driver core's private data, no one other than the driver
* core can touch this.
+ * @p_cb: Callbacks private to the driver core; no one other than the
+ * driver core is allowed to touch this.
*
* The device driver-model tracks all of the drivers known to the system.
* The main reason for this tracking is to enable the driver core to match
@@ -119,6 +121,13 @@ struct device_driver {
void (*coredump) (struct device *dev);
struct driver_private *p;
+ struct {
+ /*
+ * Called after remove() and after all devres entries have been
+ * processed. This is a Rust only callback.
+ */
+ void (*post_unbind_rust)(struct device *dev);
+ } p_cb;
};
diff --git a/include/linux/firmware/cirrus/cs_dsp_test_utils.h b/include/linux/firmware/cirrus/cs_dsp_test_utils.h
index 1f97764fdfd7..51e99f47e90e 100644
--- a/include/linux/firmware/cirrus/cs_dsp_test_utils.h
+++ b/include/linux/firmware/cirrus/cs_dsp_test_utils.h
@@ -126,7 +126,7 @@ struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
unsigned int fw_version);
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
- int type, unsigned int offset,
+ int type, u16 offset, u32 offset32,
const void *payload_data, size_t payload_len_bytes);
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
const char *info);
@@ -136,6 +136,10 @@ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
int mem_region, unsigned int reg_addr_offset,
const void *payload_data, size_t payload_len_bytes);
+void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder,
+ unsigned int alg_id, unsigned int alg_ver,
+ int mem_region, unsigned int reg_addr_offset,
+ const void *payload_data, size_t payload_len_bytes);
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder);
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
diff --git a/include/linux/firmware/cirrus/wmfw.h b/include/linux/firmware/cirrus/wmfw.h
index 74e5a4f6c13a..eae24dde9e41 100644
--- a/include/linux/firmware/cirrus/wmfw.h
+++ b/include/linux/firmware/cirrus/wmfw.h
@@ -172,7 +172,7 @@ struct wmfw_coeff_item {
__le16 type;
__le32 id;
__le32 ver;
- __le32 sr;
+ __le32 offset32;
__le32 len;
u8 data[];
} __packed;
@@ -200,4 +200,9 @@ struct wmfw_coeff_item {
#define WMFW_HALO_XM_PACKED 0x11
#define WMFW_HALO_YM_PACKED 0x12
+#define WMFW_ADSP2_XM_LONG 0xf405
+#define WMFW_ADSP2_YM_LONG 0xf406
+#define WMFW_HALO_XM_PACKED_LONG 0xf411
+#define WMFW_HALO_YM_PACKED_LONG 0xf412
+
#endif
diff --git a/include/linux/firmware/imx/sm.h b/include/linux/firmware/imx/sm.h
index a33b45027356..ba5d93bd6158 100644
--- a/include/linux/firmware/imx/sm.h
+++ b/include/linux/firmware/imx/sm.h
@@ -26,6 +26,8 @@
#define SCMI_IMX94_CTRL_SAI3_MCLK 5U /*!< WAKE SAI3 MCLK */
#define SCMI_IMX94_CTRL_SAI4_MCLK 6U /*!< WAKE SAI4 MCLK */
+#define SCMI_IMX952_CTRL_BYPASS_AUDMIX 8U /* WAKE AUDMIX */
+
#if IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV)
int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val);
int scmi_imx_misc_ctrl_set(u32 id, u32 val);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f5c9cf28c4dc..a01621fa636a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1855,6 +1855,8 @@ struct dir_context {
* INT_MAX unlimited
*/
int count;
+ /* @actor supports these flags in d_type high bits */
+ unsigned int dt_flags_mask;
};
/* If OR-ed with d_type, pending signals are not checked */
@@ -3524,7 +3526,9 @@ static inline bool dir_emit(struct dir_context *ctx,
const char *name, int namelen,
u64 ino, unsigned type)
{
- return ctx->actor(ctx, name, namelen, ctx->pos, ino, type);
+ unsigned int dt_mask = S_DT_MASK | ctx->dt_flags_mask;
+
+ return ctx->actor(ctx, name, namelen, ctx->pos, ino, type & dt_mask);
}
static inline bool dir_emit_dot(struct file *file, struct dir_context *ctx)
{
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 019a1c5281e4..e51b8ef0cebd 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -240,8 +240,9 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
pte_t *huge_pte_offset(struct mm_struct *mm,
unsigned long addr, unsigned long sz);
unsigned long hugetlb_mask_last_page(struct hstate *h);
-int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep);
+int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep);
+void huge_pmd_unshare_flush(struct mmu_gather *tlb, struct vm_area_struct *vma);
void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
unsigned long *start, unsigned long *end);
@@ -300,13 +301,17 @@ static inline struct address_space *hugetlb_folio_mapping_lock_write(
return NULL;
}
-static inline int huge_pmd_unshare(struct mm_struct *mm,
- struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
+static inline int huge_pmd_unshare(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
{
return 0;
}
+static inline void huge_pmd_unshare_flush(struct mmu_gather *tlb,
+ struct vm_area_struct *vma)
+{
+}
+
static inline void adjust_range_if_pmd_sharing_possible(
struct vm_area_struct *vma,
unsigned long *start, unsigned long *end)
@@ -1326,7 +1331,7 @@ static inline __init void hugetlb_cma_reserve(int order)
#ifdef CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING
static inline bool hugetlb_pmd_shared(pte_t *pte)
{
- return page_count(virt_to_page(pte)) > 1;
+ return ptdesc_pmd_is_shared(virt_to_ptdesc(pte));
}
#else
static inline bool hugetlb_pmd_shared(pte_t *pte)
diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
index 4247497f3f8b..b87841a355f8 100644
--- a/include/linux/iio/iio-opaque.h
+++ b/include/linux/iio/iio-opaque.h
@@ -14,6 +14,7 @@
* @mlock: lock used to prevent simultaneous device state changes
* @mlock_key: lockdep class for iio_dev lock
* @info_exist_lock: lock to prevent use during removal
+ * @info_exist_key: lockdep class for info_exist lock
* @trig_readonly: mark the current trigger immutable
* @event_interface: event chrdevs associated with interrupt lines
* @attached_buffers: array of buffers statically attached by the driver
@@ -47,6 +48,7 @@ struct iio_dev_opaque {
struct mutex mlock;
struct lock_class_key mlock_key;
struct mutex info_exist_lock;
+ struct lock_class_key info_exist_key;
bool trig_readonly;
struct iio_event_interface *event_interface;
struct iio_buffer **attached_buffers;
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 9c6ac4b62eb9..338a1921a50a 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -641,6 +641,17 @@ kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms,
__kasan_unpoison_vmap_areas(vms, nr_vms, flags);
}
+void __kasan_vrealloc(const void *start, unsigned long old_size,
+ unsigned long new_size);
+
+static __always_inline void kasan_vrealloc(const void *start,
+ unsigned long old_size,
+ unsigned long new_size)
+{
+ if (kasan_enabled())
+ __kasan_vrealloc(start, old_size, new_size);
+}
+
#else /* CONFIG_KASAN_VMALLOC */
static inline void kasan_populate_early_vm_area_shadow(void *start,
@@ -670,6 +681,9 @@ kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms,
kasan_vmalloc_flags_t flags)
{ }
+static inline void kasan_vrealloc(const void *start, unsigned long old_size,
+ unsigned long new_size) { }
+
#endif /* CONFIG_KASAN_VMALLOC */
#if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \
diff --git a/include/linux/memfd.h b/include/linux/memfd.h
index cc74de3dbcfe..c328a7b356d0 100644
--- a/include/linux/memfd.h
+++ b/include/linux/memfd.h
@@ -17,6 +17,7 @@ struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);
* to by vm_flags_ptr.
*/
int memfd_check_seals_mmap(struct file *file, vm_flags_t *vm_flags_ptr);
+struct file *memfd_alloc_file(const char *name, unsigned int flags);
#else
static inline long memfd_fcntl(struct file *f, unsigned int c, unsigned int a)
{
@@ -31,6 +32,11 @@ static inline int memfd_check_seals_mmap(struct file *file,
{
return 0;
}
+
+static inline struct file *memfd_alloc_file(const char *name, unsigned int flags)
+{
+ return ERR_PTR(-EINVAL);
+}
#endif
#endif /* __LINUX_MEMFD_H */
diff --git a/include/linux/memremap.h b/include/linux/memremap.h
index 713ec0435b48..e3c2ccf872a8 100644
--- a/include/linux/memremap.h
+++ b/include/linux/memremap.h
@@ -224,7 +224,8 @@ static inline bool is_fsdax_page(const struct page *page)
}
#ifdef CONFIG_ZONE_DEVICE
-void zone_device_page_init(struct page *page, unsigned int order);
+void zone_device_page_init(struct page *page, struct dev_pagemap *pgmap,
+ unsigned int order);
void *memremap_pages(struct dev_pagemap *pgmap, int nid);
void memunmap_pages(struct dev_pagemap *pgmap);
void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap);
@@ -234,9 +235,11 @@ bool pgmap_pfn_valid(struct dev_pagemap *pgmap, unsigned long pfn);
unsigned long memremap_compat_align(void);
-static inline void zone_device_folio_init(struct folio *folio, unsigned int order)
+static inline void zone_device_folio_init(struct folio *folio,
+ struct dev_pagemap *pgmap,
+ unsigned int order)
{
- zone_device_page_init(&folio->page, order);
+ zone_device_page_init(&folio->page, pgmap, order);
if (order)
folio_set_large_rmappable(folio);
}
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6f959d8ca4b4..f0d5be9dc736 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -608,7 +608,11 @@ enum {
/*
* Flags which should result in page tables being copied on fork. These are
* flags which indicate that the VMA maps page tables which cannot be
- * reconsistuted upon page fault, so necessitate page table copying upon
+ * reconsistuted upon page fault, so necessitate page table copying upon fork.
+ *
+ * Note that these flags should be compared with the DESTINATION VMA not the
+ * source, as VM_UFFD_WP may not be propagated to destination, while all other
+ * flags will be.
*
* VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be
* reasonably reconstructed on page fault.
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 42af2292951d..78950eb8926d 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1329,7 +1329,7 @@ struct mm_struct {
* The mm_cpumask needs to be at the end of mm_struct, because it
* is dynamically sized based on nr_cpu_ids.
*/
- unsigned long cpu_bitmap[];
+ char flexible_array[] __aligned(__alignof__(unsigned long));
};
/* Copy value to the first system word of mm flags, non-atomically. */
@@ -1366,19 +1366,24 @@ static inline void __mm_flags_set_mask_bits_word(struct mm_struct *mm,
MT_FLAGS_USE_RCU)
extern struct mm_struct init_mm;
+#define MM_STRUCT_FLEXIBLE_ARRAY_INIT \
+{ \
+ [0 ... sizeof(cpumask_t) + MM_CID_STATIC_SIZE - 1] = 0 \
+}
+
/* Pointer magic because the dynamic array size confuses some compilers. */
static inline void mm_init_cpumask(struct mm_struct *mm)
{
unsigned long cpu_bitmap = (unsigned long)mm;
- cpu_bitmap += offsetof(struct mm_struct, cpu_bitmap);
+ cpu_bitmap += offsetof(struct mm_struct, flexible_array);
cpumask_clear((struct cpumask *)cpu_bitmap);
}
/* Future-safe accessor for struct mm_struct's cpu_vm_mask. */
static inline cpumask_t *mm_cpumask(struct mm_struct *mm)
{
- return (struct cpumask *)&mm->cpu_bitmap;
+ return (struct cpumask *)&mm->flexible_array;
}
#ifdef CONFIG_LRU_GEN
@@ -1469,7 +1474,7 @@ static inline cpumask_t *mm_cpus_allowed(struct mm_struct *mm)
{
unsigned long bitmap = (unsigned long)mm;
- bitmap += offsetof(struct mm_struct, cpu_bitmap);
+ bitmap += offsetof(struct mm_struct, flexible_array);
/* Skip cpu_bitmap */
bitmap += cpumask_size();
return (struct cpumask *)bitmap;
@@ -1495,7 +1500,7 @@ static inline int mm_alloc_cid_noprof(struct mm_struct *mm, struct task_struct *
mm_init_cid(mm, p);
return 0;
}
-#define mm_alloc_cid(...) alloc_hooks(mm_alloc_cid_noprof(__VA_ARGS__))
+# define mm_alloc_cid(...) alloc_hooks(mm_alloc_cid_noprof(__VA_ARGS__))
static inline void mm_destroy_cid(struct mm_struct *mm)
{
@@ -1509,6 +1514,8 @@ static inline unsigned int mm_cid_size(void)
return cpumask_size() + bitmap_size(num_possible_cpus());
}
+/* Use 2 * NR_CPUS as worse case for static allocation. */
+# define MM_CID_STATIC_SIZE (2 * sizeof(cpumask_t))
#else /* CONFIG_SCHED_MM_CID */
static inline void mm_init_cid(struct mm_struct *mm, struct task_struct *p) { }
static inline int mm_alloc_cid(struct mm_struct *mm, struct task_struct *p) { return 0; }
@@ -1517,11 +1524,13 @@ static inline unsigned int mm_cid_size(void)
{
return 0;
}
+# define MM_CID_STATIC_SIZE 0
#endif /* CONFIG_SCHED_MM_CID */
struct mmu_gather;
extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm);
extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm);
+void tlb_gather_mmu_vma(struct mmu_gather *tlb, struct vm_area_struct *vma);
extern void tlb_finish_mmu(struct mmu_gather *tlb);
struct vm_fault;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 75ef7c9f9307..fc5d6c88d2f0 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -1648,14 +1648,15 @@ static inline int is_highmem(const struct zone *zone)
return is_highmem_idx(zone_idx(zone));
}
-#ifdef CONFIG_ZONE_DMA
-bool has_managed_dma(void);
-#else
+bool has_managed_zone(enum zone_type zone);
static inline bool has_managed_dma(void)
{
+#ifdef CONFIG_ZONE_DMA
+ return has_managed_zone(ZONE_DMA);
+#else
return false;
-}
#endif
+}
#ifndef CONFIG_NUMA
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 31a848485ad9..ec442af3f886 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -210,6 +210,7 @@ enum mapping_flags {
AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM = 9,
AS_KERNEL_FILE = 10, /* mapping for a fake kernel file that shouldn't
account usage to user cgroups */
+ AS_NO_DATA_INTEGRITY = 11, /* no data integrity guarantees */
/* Bits 16-25 are used for FOLIO_ORDER */
AS_FOLIO_ORDER_BITS = 5,
AS_FOLIO_ORDER_MIN = 16,
@@ -345,6 +346,16 @@ static inline bool mapping_writeback_may_deadlock_on_reclaim(const struct addres
return test_bit(AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM, &mapping->flags);
}
+static inline void mapping_set_no_data_integrity(struct address_space *mapping)
+{
+ set_bit(AS_NO_DATA_INTEGRITY, &mapping->flags);
+}
+
+static inline bool mapping_no_data_integrity(const struct address_space *mapping)
+{
+ return test_bit(AS_NO_DATA_INTEGRITY, &mapping->flags);
+}
+
static inline gfp_t mapping_gfp_mask(const struct address_space *mapping)
{
return mapping->gfp_mask;
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index a9a089566b7c..5ed7846639bf 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2950,7 +2950,8 @@
#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1
#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2
#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3
-#define PCI_DEVICE_ID_INTEL_HDA_GML 0x3198
+/* In a few of the Intel documents the GML acronym is used for Gemini Lake */
+#define PCI_DEVICE_ID_INTEL_HDA_GLK 0x3198
#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340
#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429
#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a
@@ -3143,6 +3144,7 @@
#define PCI_DEVICE_ID_INTEL_HDA_CML_S 0xa3f0
#define PCI_DEVICE_ID_INTEL_HDA_LNL_P 0xa828
#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152
+#define PCI_DEVICE_ID_INTEL_HDA_NVL 0xd328
#define PCI_DEVICE_ID_INTEL_HDA_BMG 0xe2f7
#define PCI_DEVICE_ID_INTEL_HDA_PTL_H 0xe328
#define PCI_DEVICE_ID_INTEL_HDA_PTL 0xe428
diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h
index b9c8520b4bd3..509c5592aab0 100644
--- a/include/linux/platform_data/davinci_asp.h
+++ b/include/linux/platform_data/davinci_asp.h
@@ -59,7 +59,8 @@ struct davinci_mcasp_pdata {
bool i2s_accurate_sck;
/* McASP specific fields */
- int tdm_slots;
+ int tdm_slots_tx;
+ int tdm_slots_rx;
u8 op_mode;
u8 dismod;
u8 num_serializer;
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index b0b9be750d93..caff2240bdab 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -359,6 +359,10 @@ typedef void (*regmap_unlock)(void *);
* @reg_defaults: Power on reset values for registers (for use with
* register cache support).
* @num_reg_defaults: Number of elements in reg_defaults.
+ * @reg_default_cb: Optional callback to return default values for registers
+ * not listed in reg_defaults. This is only used for
+ * REGCACHE_FLAT population; drivers must ensure the readable_reg/
+ * writeable_reg callbacks are defined to handle holes.
*
* @read_flag_mask: Mask to be set in the top bytes of the register when doing
* a read.
@@ -449,6 +453,8 @@ struct regmap_config {
const struct regmap_access_table *rd_noinc_table;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
+ int (*reg_default_cb)(struct device *dev, unsigned int reg,
+ unsigned int *def);
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
@@ -1349,6 +1355,14 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg,
return regmap_update_bits_base(map, reg, mask, val, NULL, false, true);
}
+static inline int regmap_default_zero_cb(struct device *dev,
+ unsigned int reg,
+ unsigned int *def)
+{
+ *def = 0;
+ return 0;
+}
+
int regmap_get_val_bytes(struct regmap *map);
int regmap_get_max_register(struct regmap *map);
int regmap_get_reg_stride(struct regmap *map);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index da0133524d08..5f00b5ed0f3b 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1776,6 +1776,11 @@ static __always_inline bool is_percpu_thread(void)
(current->nr_cpus_allowed == 1);
}
+static __always_inline bool is_user_task(struct task_struct *task)
+{
+ return task->mm && !(task->flags & (PF_KTHREAD | PF_USER_WORKER));
+}
+
/* Per-process atomic flags. */
#define PFA_NO_NEW_PRIVS 0 /* May not gain new privileges. */
#define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */
diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h
index a532d1e4b1f4..6e1b1202e818 100644
--- a/include/linux/soc/qcom/apr.h
+++ b/include/linux/soc/qcom/apr.h
@@ -122,7 +122,7 @@ struct gpr_ibasic_rsp_accepted_t {
#define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF)
#define APR_SVC_MINOR_VERSION(v) (v & 0xFF)
-typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op);
+typedef int (*gpr_port_cb) (const struct gpr_resp_pkt *d, void *priv, int op);
struct packet_router;
struct pkt_router_svc {
struct device *dev;
@@ -155,8 +155,8 @@ struct apr_driver {
int (*probe)(struct apr_device *sl);
void (*remove)(struct apr_device *sl);
int (*callback)(struct apr_device *a,
- struct apr_resp_pkt *d);
- int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op);
+ const struct apr_resp_pkt *d);
+ gpr_port_cb gpr_callback;
struct device_driver driver;
const struct apr_device_id *id_table;
};
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index cb2c2df31089..8bc616b00343 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -882,6 +882,15 @@ extern int devm_spi_register_controller(struct device *dev,
struct spi_controller *ctlr);
extern void spi_unregister_controller(struct spi_controller *ctlr);
+#if IS_ENABLED(CONFIG_OF)
+extern struct spi_controller *of_find_spi_controller_by_node(struct device_node *node);
+#else
+static inline struct spi_controller *of_find_spi_controller_by_node(struct device_node *node)
+{
+ return NULL;
+}
+#endif
+
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SPI_MASTER)
extern struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev);
extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 49edc7da0586..462078403557 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -521,13 +521,14 @@ static inline int bond_is_ip6_target_ok(struct in6_addr *addr)
static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
struct slave *slave)
{
+ unsigned long tmp, ret = READ_ONCE(slave->target_last_arp_rx[0]);
int i = 1;
- unsigned long ret = slave->target_last_arp_rx[0];
-
- for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
- if (time_before(slave->target_last_arp_rx[i], ret))
- ret = slave->target_last_arp_rx[i];
+ for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++) {
+ tmp = READ_ONCE(slave->target_last_arp_rx[i]);
+ if (time_before(tmp, ret))
+ ret = tmp;
+ }
return ret;
}
@@ -537,7 +538,7 @@ static inline unsigned long slave_last_rx(struct bonding *bond,
if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
return slave_oldest_target_arp_rx(bond, slave);
- return slave->last_rx;
+ return READ_ONCE(slave->last_rx);
}
static inline void slave_update_last_tx(struct slave *slave)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 899f267b7cf9..2900202588a5 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3221,8 +3221,6 @@ struct cfg80211_auth_request {
* if this is %NULL for a link, that link is not requested
* @elems: extra elements for the per-STA profile for this link
* @elems_len: length of the elements
- * @disabled: If set this link should be included during association etc. but it
- * should not be used until enabled by the AP MLD.
* @error: per-link error code, must be <= 0. If there is an error, then the
* operation as a whole must fail.
*/
@@ -3230,7 +3228,6 @@ struct cfg80211_assoc_link {
struct cfg80211_bss *bss;
const u8 *elems;
size_t elems_len;
- bool disabled;
int error;
};
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 127e6c7d910d..c54df042db6b 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -219,6 +219,8 @@ static inline void nfc_free_device(struct nfc_dev *dev)
int nfc_register_device(struct nfc_dev *dev);
+void nfc_unregister_rfkill(struct nfc_dev *dev);
+void nfc_remove_device(struct nfc_dev *dev);
void nfc_unregister_device(struct nfc_dev *dev);
/**
diff --git a/include/sound/ak4641.h b/include/sound/ak4641.h
deleted file mode 100644
index 8b1941bbde52..000000000000
--- a/include/sound/ak4641.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * AK4641 ALSA SoC Codec driver
- *
- * Copyright 2009 Philipp Zabel
- */
-
-#ifndef __AK4641_H
-#define __AK4641_H
-
-/**
- * struct ak4641_platform_data - platform specific AK4641 configuration
- * @gpio_power: GPIO to control external power to AK4641
- * @gpio_npdn: GPIO connected to AK4641 nPDN pin
- *
- * Both GPIO parameters are optional.
- */
-struct ak4641_platform_data {
- int gpio_power;
- int gpio_npdn;
-};
-
-#endif /* __AK4641_H */
diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h
index 61e00017c9aa..e9aa86d76049 100644
--- a/include/sound/cs-amp-lib.h
+++ b/include/sound/cs-amp-lib.h
@@ -58,6 +58,9 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
const struct cirrus_amp_cal_data *in_data);
int cs_amp_get_vendor_spkid(struct device *dev);
+const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
+ int ssid_vendor,
+ int ssid_device);
struct dentry *cs_amp_create_debugfs(struct device *dev);
static inline u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
index 5928af539c46..ae1e1489b671 100644
--- a/include/sound/cs35l56.h
+++ b/include/sound/cs35l56.h
@@ -9,6 +9,7 @@
#ifndef __CS35L56_H
#define __CS35L56_H
+#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/regulator/consumer.h>
@@ -26,6 +27,9 @@ struct snd_ctl_elem_value;
#define CS35L56_GLOBAL_ENABLES 0x0002014
#define CS35L56_BLOCK_ENABLES 0x0002018
#define CS35L56_BLOCK_ENABLES2 0x000201C
+#define CS35L56_SYNC_GPIO1_CFG 0x0002410
+#define CS35L56_ASP2_DIO_GPIO13_CFG 0x0002440
+#define CS35L56_UPDATE_REGS 0x0002A0C
#define CS35L56_REFCLK_INPUT 0x0002C04
#define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C
#define CS35L56_OTP_MEM_53 0x00300D4
@@ -65,6 +69,9 @@ struct snd_ctl_elem_value;
#define CS35L56_IRQ1_MASK_8 0x000E0AC
#define CS35L56_IRQ1_MASK_18 0x000E0D4
#define CS35L56_IRQ1_MASK_20 0x000E0DC
+#define CS35L56_GPIO_STATUS1 0x000F000
+#define CS35L56_GPIO1_CTRL1 0x000F008
+#define CS35L56_GPIO13_CTRL1 0x000F038
#define CS35L56_MIXER_NGATE_CH1_CFG 0x0010004
#define CS35L56_MIXER_NGATE_CH2_CFG 0x0010008
#define CS35L56_DSP_MBOX_1_RAW 0x0011000
@@ -130,6 +137,17 @@ struct snd_ctl_elem_value;
#define CS35L56_MTLREVID_MASK 0x0000000F
#define CS35L56_REVID_B0 0x000000B0
+/* PAD_INTF */
+#define CS35L56_PAD_GPIO_PULL_MASK GENMASK(3, 2)
+#define CS35L56_PAD_GPIO_IE BIT(0)
+
+#define CS35L56_PAD_PULL_NONE 0
+#define CS35L56_PAD_PULL_UP 1
+#define CS35L56_PAD_PULL_DOWN 2
+
+/* UPDATE_REGS */
+#define CS35L56_UPDT_GPIO_PRES BIT(6)
+
/* ASP_ENABLES1 */
#define CS35L56_ASP_RX2_EN_SHIFT 17
#define CS35L56_ASP_RX1_EN_SHIFT 16
@@ -185,6 +203,12 @@ struct snd_ctl_elem_value;
/* MIXER_NGATE_CHn_CFG */
#define CS35L56_AUX_NGATE_CHn_EN 0x00000001
+/* GPIOn_CTRL1 */
+#define CS35L56_GPIO_DIR_MASK BIT(31)
+#define CS35L56_GPIO_FN_MASK GENMASK(2, 0)
+
+#define CS35L56_GPIO_FN_GPIO 0x00000001
+
/* Mixer input sources */
#define CS35L56_INPUT_SRC_NONE 0x00
#define CS35L56_INPUT_SRC_ASP1RX1 0x08
@@ -279,6 +303,7 @@ struct snd_ctl_elem_value;
#define CS35L56_HALO_STATE_TIMEOUT_US 250000
#define CS35L56_RESET_PULSE_MIN_US 1100
#define CS35L56_WAKE_HOLD_TIME_US 1000
+#define CS35L56_PAD_PULL_SETTLE_US 10
#define CS35L56_CALIBRATION_POLL_US (100 * USEC_PER_MSEC)
#define CS35L56_CALIBRATION_TIMEOUT_US (5 * USEC_PER_SEC)
@@ -289,6 +314,9 @@ struct snd_ctl_elem_value;
#define CS35L56_NUM_BULK_SUPPLIES 3
#define CS35L56_NUM_DSP_REGIONS 5
+#define CS35L56_MAX_GPIO 13
+#define CS35L63_MAX_GPIO 9
+
/* Additional margin for SYSTEM_RESET to control port ready on SPI */
#define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500)
@@ -338,6 +366,10 @@ struct cs35l56_base {
const struct cirrus_amp_cal_controls *calibration_controls;
struct dentry *debugfs;
u64 silicon_uid;
+ u8 onchip_spkid_gpios[5];
+ u8 num_onchip_spkid_gpios;
+ u8 onchip_spkid_pulls[5];
+ u8 num_onchip_spkid_pulls;
};
static inline bool cs35l56_is_otp_register(unsigned int reg)
@@ -413,6 +445,11 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base);
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);
+int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
+ const u32 *gpios, int num_gpios,
+ const u32 *pulls, int num_pulls);
+int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base);
+int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base);
int cs35l56_get_bclk_freq_id(unsigned int freq);
void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h
index 6e9391b3816c..79bd5a7a0f88 100644
--- a/include/sound/sdca_function.h
+++ b/include/sound/sdca_function.h
@@ -798,6 +798,7 @@ struct sdca_control_range {
* @sel: Identifier used for addressing.
* @nbits: Number of bits used in the Control.
* @values: Holds the Control value for constants and defaults.
+ * @reset: Defined reset value for the Control.
* @cn_list: A bitmask showing the valid Control Numbers within this Control,
* Control Numbers typically represent channels.
* @interrupt_position: SCDA interrupt line that will alert to changes on this
@@ -808,6 +809,7 @@ struct sdca_control_range {
* @layers: Bitmask of access layers of the Control.
* @deferrable: Indicates if the access to the Control can be deferred.
* @has_default: Indicates the Control has a default value to be written.
+ * @has_reset: Indicates the Control has a defined reset value.
* @has_fixed: Indicates the Control only supports a single value.
*/
struct sdca_control {
@@ -816,6 +818,7 @@ struct sdca_control {
int nbits;
int *values;
+ int reset;
u64 cn_list;
int interrupt_position;
@@ -827,6 +830,7 @@ struct sdca_control {
bool deferrable;
bool is_volatile;
bool has_default;
+ bool has_reset;
bool has_fixed;
};
diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index 8f13417d129a..9bcb5d8fd592 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -84,4 +84,11 @@ int sdca_irq_populate(struct sdca_function_data *function,
struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev,
struct regmap *regmap, int irq);
+void sdca_irq_enable_early(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+void sdca_irq_enable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+void sdca_irq_disable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+
#endif
diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
new file mode 100644
index 000000000000..3ec22046d3eb
--- /dev/null
+++ b/include/sound/sdca_jack.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright (C) 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __SDCA_JACK_H__
+#define __SDCA_JACK_H__
+
+struct sdca_interrupt;
+struct snd_kcontrol;
+struct snd_soc_jack;
+
+/**
+ * struct jack_state - Jack state structure to keep data between interrupts
+ * @kctl: Pointer to the ALSA control attached to this jack
+ * @jack: Pointer to the ASoC jack struct for this jack
+ */
+struct jack_state {
+ struct snd_kcontrol *kctl;
+ struct snd_soc_jack *jack;
+};
+
+int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
+int sdca_jack_process(struct sdca_interrupt *interrupt);
+int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack);
+int sdca_jack_report(struct sdca_interrupt *interrupt);
+
+#endif // __SDCA_JACK_H__
diff --git a/include/sound/soc-acpi-intel-ssp-common.h b/include/sound/soc-acpi-intel-ssp-common.h
index b4597c8dac78..fdb2fce42115 100644
--- a/include/sound/soc-acpi-intel-ssp-common.h
+++ b/include/sound/soc-acpi-intel-ssp-common.h
@@ -37,6 +37,9 @@
#define RT5682_ACPI_HID "10EC5682"
#define RT5682S_ACPI_HID "RTL5682"
+/* Texas Instruments */
+#define TAS2563_ACPI_HID "TXNW2563"
+
enum snd_soc_acpi_intel_codec {
CODEC_NONE,
@@ -63,6 +66,7 @@ enum snd_soc_acpi_intel_codec {
CODEC_RT1015P,
CODEC_RT1019P,
CODEC_RT1308,
+ CODEC_TAS2563,
};
enum snd_soc_acpi_intel_codec
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index d78cda866888..2a2b74b24a60 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -237,8 +237,7 @@ struct snd_soc_component {
* the driver will be marked as BROKEN when these fields are removed.
*/
- /* Don't use these, use snd_soc_component_get_dapm() */
- struct snd_soc_dapm_context dapm;
+ struct snd_soc_dapm_context *dapm;
/* machine specific init */
int (*init)(struct snd_soc_component *component);
@@ -268,12 +267,9 @@ struct snd_soc_component {
static inline struct snd_soc_dapm_context *snd_soc_component_to_dapm(
struct snd_soc_component *component)
{
- return &component->dapm;
+ return component->dapm;
}
-// FIXME
-#define snd_soc_component_get_dapm snd_soc_component_to_dapm
-
/**
* snd_soc_component_cache_sync() - Sync the register cache with the hardware
* @component: COMPONENT to sync
@@ -368,27 +364,6 @@ snd_soc_component_active(struct snd_soc_component *component)
return component->active;
}
-/* component pin */
-int snd_soc_component_enable_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_disable_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_nc_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_get_pin_status(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
- const char *pin);
-int snd_soc_component_force_enable_pin_unlocked(
- struct snd_soc_component *component,
- const char *pin);
-
/* component controls */
struct snd_kcontrol *snd_soc_component_get_kcontrol(struct snd_soc_component *component,
const char * const ctl);
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 75941324886b..49f0fe05db01 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -20,6 +20,7 @@ struct regulator;
struct soc_enum;
struct snd_pcm_substream;
struct snd_soc_pcm_runtime;
+struct snd_soc_dapm_context;
/* widget has no PM register bit */
#define SND_SOC_NOPM -1
@@ -579,28 +580,6 @@ struct snd_soc_dapm_update {
bool has_second_set;
};
-/* DAPM context */
-struct snd_soc_dapm_context {
- enum snd_soc_bias_level bias_level;
-
- bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */
-
- struct device *dev; /* from parent - for debug */ /* REMOVE ME */
- struct snd_soc_component *component; /* parent component */
- struct snd_soc_card *card; /* parent card */
-
- /* used during DAPM updates */
- enum snd_soc_bias_level target_bias_level;
- struct list_head list;
-
- struct snd_soc_dapm_widget *wcache_sink;
- struct snd_soc_dapm_widget *wcache_source;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_dapm;
-#endif
-};
-
/* A list of widgets associated with an object, typically a snd_kcontrol */
struct snd_soc_dapm_widget_list {
int num_widgets;
@@ -628,6 +607,8 @@ enum snd_soc_dapm_direction {
#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN)
#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT)
+struct snd_soc_dapm_context *snd_soc_dapm_alloc(struct device *dev);
+
int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
@@ -705,16 +686,6 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, co
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin);
void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card);
-/*
- * Marks the specified pin as being not connected, disabling it along
- * any parent or child widgets. At present this is identical to
- * snd_soc_dapm_disable_pin[_unlocked]() but in future it will be extended to do
- * additional things such as disabling controls which only affect
- * paths through the pin.
- */
-#define snd_soc_dapm_nc_pin snd_soc_dapm_disable_pin
-#define snd_soc_dapm_nc_pin_unlocked snd_soc_dapm_disable_pin_unlocked
-
/* dapm path query */
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list,
@@ -730,15 +701,6 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_so
enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm);
void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level);
-// REMOVE ME !!
-#define snd_soc_component_force_bias_level(c, l) snd_soc_dapm_force_bias_level(&(c)->dapm, l)
-#define snd_soc_component_get_bias_level(c) snd_soc_dapm_get_bias_level(&(c)->dapm)
-#define snd_soc_component_init_bias_level(c, l) snd_soc_dapm_init_bias_level(&(c)->dapm, l)
-#define snd_soc_dapm_kcontrol_widget snd_soc_dapm_kcontrol_to_widget
-#define snd_soc_dapm_kcontrol_dapm snd_soc_dapm_kcontrol_to_dapm
-#define dapm_kcontrol_get_value snd_soc_dapm_kcontrol_get_value
-#define snd_soc_dapm_kcontrol_component snd_soc_dapm_kcontrol_to_component
-
#define for_each_dapm_widgets(list, i, widget) \
for ((i) = 0; \
(i) < list->num_widgets && (widget = list->widgets[i]); \
diff --git a/include/sound/soc.h b/include/sound/soc.h
index aa0fe6b80293..7d8376c8e1be 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1076,7 +1076,7 @@ struct snd_soc_card {
struct list_head dobj_list;
/* Generic DAPM context for the card */
- struct snd_soc_dapm_context dapm;
+ struct snd_soc_dapm_context *dapm;
struct snd_soc_dapm_stats dapm_stats;
#ifdef CONFIG_DEBUG_FS
@@ -1136,7 +1136,7 @@ static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card)
static inline struct snd_soc_dapm_context *snd_soc_card_to_dapm(struct snd_soc_card *card)
{
- return &card->dapm;
+ return card->dapm;
}
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
diff --git a/include/sound/sof.h b/include/sound/sof.h
index eddea82c7b5a..38d6c8cb5e83 100644
--- a/include/sound/sof.h
+++ b/include/sound/sof.h
@@ -159,6 +159,9 @@ struct sof_dev_desc {
/* The platform supports DSPless mode */
bool dspless_mode_supported;
+ /* On demand DSP booting is possible on the platform */
+ bool on_demand_dsp_boot;
+
/* defaults paths for firmware, library and topology files */
const char *default_fw_path[SOF_IPC_TYPE_COUNT];
const char *default_lib_path[SOF_IPC_TYPE_COUNT];
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index 15fac532688e..4554e5e8cab5 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -352,6 +352,10 @@ struct sof_ipc4_base_module_cfg {
#define SOF_IPC4_MOD_EXT_DOMAIN_MASK BIT(28)
#define SOF_IPC4_MOD_EXT_DOMAIN(x) ((x) << SOF_IPC4_MOD_EXT_DOMAIN_SHIFT)
+#define SOF_IPC4_MOD_EXT_EXTENDED_INIT_SHIFT 29
+#define SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK BIT(29)
+#define SOF_IPC4_MOD_EXT_EXTENDED_INIT(x) ((x) << SOF_IPC4_MOD_EXT_EXTENDED_SHIFT)
+
/* bind/unbind module ipc msg */
#define SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT 0
#define SOF_IPC4_MOD_EXT_DST_MOD_ID_MASK GENMASK(15, 0)
@@ -586,6 +590,77 @@ struct sof_ipc4_notify_module_data {
#define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000
#define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0)
+/*
+ * Macros for creating struct sof_ipc4_module_init_ext_init payload
+ * with its associated data. ext_init payload should be the first
+ * piece of payload following SOF_IPC4_MOD_INIT_INSTANCE msg, and its
+ * existence is indicated with SOF_IPC4_MOD_EXT_EXTENDED-bit.
+ *
+ * The macros below apply to sof_ipc4_module_init_ext_init.word0
+ */
+#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_SHIFT 0
+#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_MASK BIT(0)
+#define SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN(x) ((x) << SOF_IPC4_MOD_INIT_EXT_RTOS_DOMAIN_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_GNA_USED_SHIFT 1
+#define SOF_IPC4_MOD_INIT_EXT_GNA_USED_MASK BIT(1)
+#define SOF_IPC4_MOD_INIT_EXT_GNA_USED(x) ((x) << SOF_IPC4_MOD_INIT_EXT_GNA_USED_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_SHIFT 2
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK BIT(2)
+#define SOF_IPC4_MOD_INIT_EXT_DATA_ARRAY(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_SHIFT)
+
+struct sof_ipc4_module_init_ext_init {
+ u32 word0;
+ u32 rsvd1;
+ u32 rsvd2;
+} __packed __aligned(4);
+
+/*
+ * SOF_IPC4_MOD_EXT_EXTENDED payload may be followed by arbitrary
+ * number of object array objects. SOF_IPC4_MOD_INIT_EXT_DATA_ARRAY
+ * -bit indicates that an array object follows struct
+ * sof_ipc4_module_init_ext_init.
+ *
+ * The object header's SOF_IPC4_MOD_INIT_EXT_OBJ_LAST-bit in struct
+ * sof_ipc4_module_init_ext_object indicates if the array is continued
+ * with another object. The header has also fields to identify the
+ * object, SOF_IPC4_MOD_INIT_EXT_OBJ_ID, and to indicate the object's
+ * size in 32-bit words, SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS, not
+ * including the header itself.
+ *
+ * The macros below apply to sof_ipc4_module_init_ext_object.header
+ */
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_SHIFT 0
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK BIT(0)
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID_SHIFT 1
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID_MASK GENMASK(15, 1)
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_ID(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_ID_SHIFT)
+
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_SHIFT 16
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_MASK GENMASK(31, 16)
+#define SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS_SHIFT)
+
+struct sof_ipc4_module_init_ext_object {
+ u32 header;
+ u32 data[];
+} __packed __aligned(4);
+
+enum sof_ipc4_mod_init_ext_obj_id {
+ SOF_IPC4_MOD_INIT_DATA_ID_INVALID = 0,
+ SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA,
+ SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA,
+};
+
+/* DP module memory configuration data object for ext_init object array */
+struct sof_ipc4_mod_init_ext_dp_memory_data {
+ u32 domain_id; /* userspace domain ID */
+ u32 stack_bytes; /* stack size in bytes, 0 means default size */
+ u32 heap_bytes; /* stack size in bytes, 0 means default size */
+} __packed __aligned(4);
+
/** @}*/
#endif
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index 9d3c54cb8223..7c03bdc951bb 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2026 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -233,7 +233,6 @@ struct tasdevice_priv {
bool playback_started;
bool isacpi;
bool isspi;
- bool is_user_space_calidata;
unsigned int global_addr;
int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index de6f6d25767c..869f97c9bf73 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -322,6 +322,7 @@
EM(rxrpc_call_put_kernel, "PUT kernel ") \
EM(rxrpc_call_put_poke, "PUT poke ") \
EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \
+ EM(rxrpc_call_put_recvmsg_peek_nowait, "PUT peek-nwt") \
EM(rxrpc_call_put_release_recvmsg_q, "PUT rls-rcmq") \
EM(rxrpc_call_put_release_sock, "PUT rls-sock") \
EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \
@@ -340,6 +341,9 @@
EM(rxrpc_call_see_input, "SEE input ") \
EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \
EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \
+ EM(rxrpc_call_see_recvmsg_requeue, "SEE recv-rqu") \
+ EM(rxrpc_call_see_recvmsg_requeue_first, "SEE recv-rqF") \
+ EM(rxrpc_call_see_recvmsg_requeue_move, "SEE recv-rqM") \
EM(rxrpc_call_see_release, "SEE release ") \
EM(rxrpc_call_see_userid_exists, "SEE u-exists") \
EM(rxrpc_call_see_waiting_call, "SEE q-conn ") \
diff --git a/include/uapi/linux/blkzoned.h b/include/uapi/linux/blkzoned.h
index e33f02703350..663836120966 100644
--- a/include/uapi/linux/blkzoned.h
+++ b/include/uapi/linux/blkzoned.h
@@ -81,7 +81,8 @@ enum blk_zone_cond {
BLK_ZONE_COND_FULL = 0xE,
BLK_ZONE_COND_OFFLINE = 0xF,
- BLK_ZONE_COND_ACTIVE = 0xFF,
+ BLK_ZONE_COND_ACTIVE = 0xFF, /* added in Linux 6.19 */
+#define BLK_ZONE_COND_ACTIVE BLK_ZONE_COND_ACTIVE
};
/**
@@ -100,7 +101,8 @@ enum blk_zone_report_flags {
BLK_ZONE_REP_CAPACITY = (1U << 0),
/* Input flags */
- BLK_ZONE_REP_CACHED = (1U << 31),
+ BLK_ZONE_REP_CACHED = (1U << 31), /* added in Linux 6.19 */
+#define BLK_ZONE_REP_CACHED BLK_ZONE_REP_CACHED
};
/**
diff --git a/include/uapi/linux/comedi.h b/include/uapi/linux/comedi.h
index 7314e5ee0a1e..798ec9a39e12 100644
--- a/include/uapi/linux/comedi.h
+++ b/include/uapi/linux/comedi.h
@@ -640,7 +640,7 @@ struct comedi_chaninfo {
/**
* struct comedi_rangeinfo - used to retrieve the range table for a channel
- * @range_type: Encodes subdevice index (bits 27:24), channel index
+ * @range_type: Encodes subdevice index (bits 31:24), channel index
* (bits 23:16) and range table length (bits 15:0).
* @range_ptr: Pointer to array of @struct comedi_krange to be filled
* in with the range table for the channel or subdevice.
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 8134f10e4e6c..8433bac48112 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2880,8 +2880,9 @@ enum nl80211_commands {
* index. If the userspace includes more RNR elements than number of
* MBSSID elements then these will be added in every EMA beacon.
*
- * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
- * disabled.
+ * @NL80211_ATTR_MLO_LINK_DISABLED: Unused. It was used to indicate that a link
+ * is disabled during association. However, the AP will send the
+ * information by including a TTLM in the association response.
*
* @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e.
* include BSSes that can only be used in restricted scenarios and/or
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index 9ce72fbd6f11..f4a7baadb44d 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -56,6 +56,9 @@
#define SOF_TKN_SCHED_LP_MODE 207
#define SOF_TKN_SCHED_MEM_USAGE 208
#define SOF_TKN_SCHED_USE_CHAIN_DMA 209
+#define SOF_TKN_SCHED_KCPS 210
+#define SOF_TKN_SCHED_DIRECTION 211
+#define SOF_TKN_SCHED_DIRECTION_VALID 212
/* volume */
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
@@ -107,6 +110,9 @@
#define SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME 417
#define SOF_TKN_COMP_SCHED_DOMAIN 418
+#define SOF_TKN_COMP_DOMAIN_ID 419
+#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420
+#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421
/* SSP */
#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 9fd9f6ab722c..2fa7d3601edb 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -598,9 +598,9 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
__releases(&acct->lock)
{
struct io_wq *wq = worker->wq;
- bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state);
do {
+ bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state);
struct io_wq_work *work;
/*
diff --git a/io_uring/rw.c b/io_uring/rw.c
index 70ca88cc1f54..28555bc85ba0 100644
--- a/io_uring/rw.c
+++ b/io_uring/rw.c
@@ -144,19 +144,22 @@ static inline int io_import_rw_buffer(int rw, struct io_kiocb *req,
return 0;
}
-static void io_rw_recycle(struct io_kiocb *req, unsigned int issue_flags)
+static bool io_rw_recycle(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_async_rw *rw = req->async_data;
if (unlikely(issue_flags & IO_URING_F_UNLOCKED))
- return;
+ return false;
io_alloc_cache_vec_kasan(&rw->vec);
if (rw->vec.nr > IO_VEC_CACHE_SOFT_CAP)
io_vec_free(&rw->vec);
- if (io_alloc_cache_put(&req->ctx->rw_cache, rw))
+ if (io_alloc_cache_put(&req->ctx->rw_cache, rw)) {
io_req_async_data_clear(req, 0);
+ return true;
+ }
+ return false;
}
static void io_req_rw_cleanup(struct io_kiocb *req, unsigned int issue_flags)
@@ -190,7 +193,11 @@ static void io_req_rw_cleanup(struct io_kiocb *req, unsigned int issue_flags)
*/
if (!(req->flags & (REQ_F_REISSUE | REQ_F_REFCOUNT))) {
req->flags &= ~REQ_F_NEED_CLEANUP;
- io_rw_recycle(req, issue_flags);
+ if (!io_rw_recycle(req, issue_flags)) {
+ struct io_async_rw *rw = req->async_data;
+
+ io_vec_free(&rw->vec);
+ }
}
}
diff --git a/io_uring/waitid.c b/io_uring/waitid.c
index 2d4cbd47c67c..d25d60aed6af 100644
--- a/io_uring/waitid.c
+++ b/io_uring/waitid.c
@@ -114,11 +114,11 @@ static void io_waitid_remove_wq(struct io_kiocb *req)
struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid);
struct wait_queue_head *head;
- head = READ_ONCE(iw->head);
+ head = smp_load_acquire(&iw->head);
if (head) {
struct io_waitid_async *iwa = req->async_data;
- iw->head = NULL;
+ smp_store_release(&iw->head, NULL);
spin_lock_irq(&head->lock);
list_del_init(&iwa->wo.child_wait.entry);
spin_unlock_irq(&head->lock);
@@ -246,7 +246,7 @@ static int io_waitid_wait(struct wait_queue_entry *wait, unsigned mode,
return 0;
list_del_init(&wait->entry);
- iw->head = NULL;
+ smp_store_release(&iw->head, NULL);
/* cancel is in progress */
if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index d8fd6f779f79..0e266979728b 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -91,6 +91,16 @@ static int __init early_cma(char *p)
}
early_param("cma", early_cma);
+/*
+ * cma_skip_dt_default_reserved_mem - This is called from the
+ * reserved_mem framework to detect if the default cma region is being
+ * set by the "cma=" kernel parameter.
+ */
+bool __init cma_skip_dt_default_reserved_mem(void)
+{
+ return size_cmdline != -1;
+}
+
#ifdef CONFIG_DMA_NUMA_CMA
static struct cma *dma_contiguous_numa_area[MAX_NUMNODES];
@@ -470,12 +480,6 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
struct cma *cma;
int err;
- if (size_cmdline != -1 && default_cma) {
- pr_info("Reserved memory: bypass %s node, using cmdline CMA params instead\n",
- rmem->name);
- return -EBUSY;
- }
-
if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
of_get_flat_dt_prop(node, "no-map", NULL))
return -EINVAL;
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 26392badc36b..2b2fbb709242 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -184,6 +184,12 @@ static __init struct gen_pool *__dma_atomic_pool_init(size_t pool_size,
return pool;
}
+#ifdef CONFIG_ZONE_DMA32
+#define has_managed_dma32 has_managed_zone(ZONE_DMA32)
+#else
+#define has_managed_dma32 false
+#endif
+
static int __init dma_atomic_pool_init(void)
{
int ret = 0;
@@ -199,17 +205,20 @@ static int __init dma_atomic_pool_init(void)
}
INIT_WORK(&atomic_pool_work, atomic_pool_work_fn);
- atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
+ /* All memory might be in the DMA zone(s) to begin with */
+ if (has_managed_zone(ZONE_NORMAL)) {
+ atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
GFP_KERNEL);
- if (!atomic_pool_kernel)
- ret = -ENOMEM;
+ if (!atomic_pool_kernel)
+ ret = -ENOMEM;
+ }
if (has_managed_dma()) {
atomic_pool_dma = __dma_atomic_pool_init(atomic_pool_size,
GFP_KERNEL | GFP_DMA);
if (!atomic_pool_dma)
ret = -ENOMEM;
}
- if (IS_ENABLED(CONFIG_ZONE_DMA32)) {
+ if (has_managed_dma32) {
atomic_pool_dma32 = __dma_atomic_pool_init(atomic_pool_size,
GFP_KERNEL | GFP_DMA32);
if (!atomic_pool_dma32)
@@ -224,11 +233,11 @@ postcore_initcall(dma_atomic_pool_init);
static inline struct gen_pool *dma_guess_pool(struct gen_pool *prev, gfp_t gfp)
{
if (prev == NULL) {
- if (IS_ENABLED(CONFIG_ZONE_DMA32) && (gfp & GFP_DMA32))
- return atomic_pool_dma32;
- if (atomic_pool_dma && (gfp & GFP_DMA))
- return atomic_pool_dma;
- return atomic_pool_kernel;
+ if (gfp & GFP_DMA)
+ return atomic_pool_dma ?: atomic_pool_dma32 ?: atomic_pool_kernel;
+ if (gfp & GFP_DMA32)
+ return atomic_pool_dma32 ?: atomic_pool_dma ?: atomic_pool_kernel;
+ return atomic_pool_kernel ?: atomic_pool_dma32 ?: atomic_pool_dma;
}
if (prev == atomic_pool_kernel)
return atomic_pool_dma32 ? atomic_pool_dma32 : atomic_pool_dma;
@@ -268,15 +277,20 @@ struct page *dma_alloc_from_pool(struct device *dev, size_t size,
{
struct gen_pool *pool = NULL;
struct page *page;
+ bool pool_found = false;
while ((pool = dma_guess_pool(pool, gfp))) {
+ pool_found = true;
page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
phys_addr_ok);
if (page)
return page;
}
- WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
+ if (pool_found)
+ WARN(!(gfp & __GFP_NOWARN), "DMA pool exhausted for %s\n", dev_name(dev));
+ else
+ WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
return NULL;
}
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index 1f6589578703..9d24b6e0c91f 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -246,7 +246,7 @@ get_perf_callchain(struct pt_regs *regs, bool kernel, bool user,
if (user && !crosstask) {
if (!user_mode(regs)) {
- if (current->flags & (PF_KTHREAD | PF_USER_WORKER))
+ if (!is_user_task(current))
goto exit_put;
regs = task_pt_regs(current);
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 5b5cb620499e..8cca80094624 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6997,6 +6997,15 @@ static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event,
if (data_page_nr(event->rb) != nr_pages)
return -EINVAL;
+ /*
+ * If this event doesn't have mmap_count, we're attempting to
+ * create an alias of another event's mmap(); this would mean
+ * both events will end up scribbling the same user_page;
+ * which makes no sense.
+ */
+ if (!refcount_read(&event->mmap_count))
+ return -EBUSY;
+
if (refcount_inc_not_zero(&event->rb->mmap_count)) {
/*
* Success -- managed to mmap() the same buffer
@@ -7451,7 +7460,7 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
if (user_mode(regs)) {
regs_user->abi = perf_reg_abi(current);
regs_user->regs = regs;
- } else if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+ } else if (is_user_task(current)) {
perf_get_regs_user(regs_user, regs);
} else {
regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
@@ -8091,7 +8100,7 @@ static u64 perf_virt_to_phys(u64 virt)
* Try IRQ-safe get_user_page_fast_only first.
* If failed, leave phys_addr as 0.
*/
- if (!(current->flags & (PF_KTHREAD | PF_USER_WORKER))) {
+ if (is_user_task(current)) {
struct page *p;
pagefault_disable();
@@ -8206,7 +8215,7 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
{
bool kernel = !event->attr.exclude_callchain_kernel;
bool user = !event->attr.exclude_callchain_user &&
- !(current->flags & (PF_KTHREAD | PF_USER_WORKER));
+ is_user_task(current);
/* Disallow cross-task user callchains. */
bool crosstask = event->ctx->task && event->ctx->task != current;
bool defer_user = IS_ENABLED(CONFIG_UNWIND_USER) && user &&
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index d4482b6e3cae..90d411a59f76 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -255,6 +255,14 @@ static struct page *kho_restore_page(phys_addr_t phys, bool is_folio)
if (is_folio && info.order)
prep_compound_page(page, info.order);
+ /* Always mark headpage's codetag as empty to avoid accounting mismatch */
+ clear_page_tag_ref(page);
+ if (!is_folio) {
+ /* Also do that for the non-compound tail pages */
+ for (unsigned int i = 1; i < nr_pages; i++)
+ clear_page_tag_ref(page + i);
+ }
+
adjust_managed_page_count(page, nr_pages);
return page;
}
@@ -1006,8 +1014,10 @@ int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation)
chunk->phys[idx++] = phys;
if (idx == ARRAY_SIZE(chunk->phys)) {
chunk = new_vmalloc_chunk(chunk);
- if (!chunk)
+ if (!chunk) {
+ err = -ENOMEM;
goto err_free;
+ }
idx = 0;
}
}
diff --git a/kernel/panic.c b/kernel/panic.c
index 0d52210a9e2b..0c20fcaae98a 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -131,7 +131,8 @@ static int proc_taint(const struct ctl_table *table, int write,
static int sysctl_panic_print_handler(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
- panic_print_deprecated();
+ if (write)
+ panic_print_deprecated();
return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
}
@@ -1014,7 +1015,6 @@ static int panic_print_set(const char *val, const struct kernel_param *kp)
static int panic_print_get(char *val, const struct kernel_param *kp)
{
- panic_print_deprecated();
return param_get_ulong(val, kp);
}
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index c509f2e7d69d..7bcde7114f1b 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1034,6 +1034,12 @@ static void update_dl_entity(struct sched_dl_entity *dl_se)
return;
}
+ /*
+ * When [4] D->A is followed by [1] A->B, dl_defer_running
+ * needs to be cleared, otherwise it will fail to properly
+ * start the zero-laxity timer.
+ */
+ dl_se->dl_defer_running = 0;
replenish_dl_new_period(dl_se, rq);
} else if (dl_server(dl_se) && dl_se->dl_defer) {
/*
@@ -1655,6 +1661,12 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* dl_server_active = 1;
* enqueue_dl_entity()
* update_dl_entity(WAKEUP)
+ * if (dl_time_before() || dl_entity_overflow())
+ * dl_defer_running = 0;
+ * replenish_dl_new_period();
+ * // fwd period
+ * dl_throttled = 1;
+ * dl_defer_armed = 1;
* if (!dl_defer_running)
* dl_defer_armed = 1;
* dl_throttled = 1;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index e71302282671..3eaeceda71b0 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -8828,16 +8828,6 @@ static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int
if ((wake_flags & WF_FORK) || pse->sched_delayed)
return;
- /*
- * If @p potentially is completing work required by current then
- * consider preemption.
- *
- * Reschedule if waker is no longer eligible. */
- if (in_task() && !entity_eligible(cfs_rq, se)) {
- preempt_action = PREEMPT_WAKEUP_RESCHED;
- goto preempt;
- }
-
/* Prefer picking wakee soon if appropriate. */
if (sched_feat(NEXT_BUDDY) &&
set_preempt_buddy(cfs_rq, wake_flags, pse, se)) {
@@ -8995,12 +8985,6 @@ idle:
goto again;
}
- /*
- * rq is about to be idle, check if we need to update the
- * lost_idle_time of clock_pelt
- */
- update_idle_rq_clock_pelt(rq);
-
return NULL;
}
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
index 980d92bab8ab..136a6584be79 100644
--- a/kernel/sched/features.h
+++ b/kernel/sched/features.h
@@ -29,7 +29,7 @@ SCHED_FEAT(PREEMPT_SHORT, true)
* wakeup-preemption), since its likely going to consume data we
* touched, increases cache locality.
*/
-SCHED_FEAT(NEXT_BUDDY, true)
+SCHED_FEAT(NEXT_BUDDY, false)
/*
* Allow completely ignoring cfs_rq->next; which can be set from various
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index c174afe1dd17..abf8f15d60c9 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -468,6 +468,12 @@ static void set_next_task_idle(struct rq *rq, struct task_struct *next, bool fir
scx_update_idle(rq, true, true);
schedstat_inc(rq->sched_goidle);
next->se.exec_start = rq_clock_task(rq);
+
+ /*
+ * rq is about to be idle, check if we need to update the
+ * lost_idle_time of clock_pelt
+ */
+ update_idle_rq_clock_pelt(rq);
}
struct task_struct *pick_task_idle(struct rq *rq, struct rq_flags *rf)
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index a1890a073196..df7194961658 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -252,7 +252,7 @@ enum wd_read_status {
static enum wd_read_status cs_watchdog_read(struct clocksource *cs, u64 *csnow, u64 *wdnow)
{
- int64_t md = 2 * watchdog->uncertainty_margin;
+ int64_t md = watchdog->uncertainty_margin;
unsigned int nretries, max_retries;
int64_t wd_delay, wd_seq_delay;
u64 wd_end, wd_end2;
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 3ec3daa4acab..91fa2003351c 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2735,7 +2735,7 @@ static int __do_adjtimex(struct tk_data *tkd, struct __kernel_timex *txc,
timekeeping_update_from_shadow(tkd, TK_CLOCK_WAS_SET);
result->clock_set = true;
} else {
- tk_update_leap_state_all(&tk_core);
+ tk_update_leap_state_all(tkd);
}
/* Update the multiplier immediately if frequency was set directly */
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index baec63134ab6..8bd4ec08fb36 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6115,10 +6115,10 @@ static int cmp_mod_entry(const void *key, const void *pivot)
unsigned long addr = (unsigned long)key;
const struct trace_mod_entry *ent = pivot;
- if (addr >= ent[0].mod_addr && addr < ent[1].mod_addr)
- return 0;
- else
- return addr - ent->mod_addr;
+ if (addr < ent[0].mod_addr)
+ return -1;
+
+ return addr >= ent[1].mod_addr;
}
/**
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 5e6e70540eef..c97bb2fda5c0 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -2057,6 +2057,15 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
hist_field->fn_num = HIST_FIELD_FN_RELDYNSTRING;
else
hist_field->fn_num = HIST_FIELD_FN_PSTRING;
+ } else if (field->filter_type == FILTER_STACKTRACE) {
+ flags |= HIST_FIELD_FL_STACKTRACE;
+
+ hist_field->size = MAX_FILTER_STR_VAL;
+ hist_field->type = kstrdup_const(field->type, GFP_KERNEL);
+ if (!hist_field->type)
+ goto free;
+
+ hist_field->fn_num = HIST_FIELD_FN_STACK;
} else {
hist_field->size = field->size;
hist_field->is_signed = field->is_signed;
diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c
index 4554c458b78c..45c187e77e21 100644
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -130,7 +130,9 @@ static int synth_event_define_fields(struct trace_event_call *call)
struct synth_event *event = call->data;
unsigned int i, size, n_u64;
char *name, *type;
+ int filter_type;
bool is_signed;
+ bool is_stack;
int ret = 0;
for (i = 0, n_u64 = 0; i < event->n_fields; i++) {
@@ -138,8 +140,12 @@ static int synth_event_define_fields(struct trace_event_call *call)
is_signed = event->fields[i]->is_signed;
type = event->fields[i]->type;
name = event->fields[i]->name;
+ is_stack = event->fields[i]->is_stack;
+
+ filter_type = is_stack ? FILTER_STACKTRACE : FILTER_OTHER;
+
ret = trace_define_field(call, type, name, offset, size,
- is_signed, FILTER_OTHER);
+ is_signed, filter_type);
if (ret)
break;
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index b1e9c9913309..1de6f1573621 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -901,7 +901,7 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
trace_seq_printf(s, "%ps", func);
if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long)) {
- print_function_args(s, entry->args, (unsigned long)func);
+ print_function_args(s, FGRAPH_ENTRY_ARGS(entry), (unsigned long)func);
trace_seq_putc(s, ';');
} else
trace_seq_puts(s, "();");
diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c
index fe9bf8db1922..e2784038bbed 100644
--- a/kernel/vmcore_info.c
+++ b/kernel/vmcore_info.c
@@ -36,7 +36,11 @@ struct hwerr_info {
time64_t timestamp;
};
-static struct hwerr_info hwerr_data[HWERR_RECOV_MAX];
+/*
+ * The hwerr_data[] array is declared with global scope so that it remains
+ * accessible to vmcoreinfo even when Link Time Optimization (LTO) is enabled.
+ */
+struct hwerr_info hwerr_data[HWERR_RECOV_MAX];
Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type,
void *data, size_t data_len)
diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c
index 84ecccddc771..012d5614efb9 100644
--- a/lib/flex_proportions.c
+++ b/lib/flex_proportions.c
@@ -64,13 +64,14 @@ void fprop_global_destroy(struct fprop_global *p)
bool fprop_new_period(struct fprop_global *p, int periods)
{
s64 events = percpu_counter_sum(&p->events);
+ unsigned long flags;
/*
* Don't do anything if there are no events.
*/
if (events <= 1)
return false;
- preempt_disable_nested();
+ local_irq_save(flags);
write_seqcount_begin(&p->sequence);
if (periods < 64)
events -= events >> periods;
@@ -78,7 +79,7 @@ bool fprop_new_period(struct fprop_global *p, int periods)
percpu_counter_add(&p->events, -events);
p->period += periods;
write_seqcount_end(&p->sequence);
- preempt_enable_nested();
+ local_irq_restore(flags);
return true;
}
diff --git a/lib/test_hmm.c b/lib/test_hmm.c
index 8af169d3873a..455a6862ae50 100644
--- a/lib/test_hmm.c
+++ b/lib/test_hmm.c
@@ -662,7 +662,9 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror *dmirror,
goto error;
}
- zone_device_folio_init(page_folio(dpage), order);
+ zone_device_folio_init(page_folio(dpage),
+ page_pgmap(folio_page(page_folio(dpage), 0)),
+ order);
dpage->zone_device_data = rpage;
return dpage;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index e0ab14020513..a1832da0f623 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5112,7 +5112,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
unsigned long last_addr_mask;
pte_t *src_pte, *dst_pte;
struct mmu_notifier_range range;
- bool shared_pmd = false;
+ struct mmu_gather tlb;
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, mm, old_addr,
old_end);
@@ -5122,6 +5122,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
* range.
*/
flush_cache_range(vma, range.start, range.end);
+ tlb_gather_mmu_vma(&tlb, vma);
mmu_notifier_invalidate_range_start(&range);
last_addr_mask = hugetlb_mask_last_page(h);
@@ -5138,8 +5139,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
if (huge_pte_none(huge_ptep_get(mm, old_addr, src_pte)))
continue;
- if (huge_pmd_unshare(mm, vma, old_addr, src_pte)) {
- shared_pmd = true;
+ if (huge_pmd_unshare(&tlb, vma, old_addr, src_pte)) {
old_addr |= last_addr_mask;
new_addr |= last_addr_mask;
continue;
@@ -5150,15 +5150,16 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma,
break;
move_huge_pte(vma, old_addr, new_addr, src_pte, dst_pte, sz);
+ tlb_remove_huge_tlb_entry(h, &tlb, src_pte, old_addr);
}
- if (shared_pmd)
- flush_hugetlb_tlb_range(vma, range.start, range.end);
- else
- flush_hugetlb_tlb_range(vma, old_end - len, old_end);
+ tlb_flush_mmu_tlbonly(&tlb);
+ huge_pmd_unshare_flush(&tlb, vma);
+
mmu_notifier_invalidate_range_end(&range);
i_mmap_unlock_write(mapping);
hugetlb_vma_unlock_write(vma);
+ tlb_finish_mmu(&tlb);
return len + old_addr - old_end;
}
@@ -5177,7 +5178,6 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long sz = huge_page_size(h);
bool adjust_reservation;
unsigned long last_addr_mask;
- bool force_flush = false;
WARN_ON(!is_vm_hugetlb_page(vma));
BUG_ON(start & ~huge_page_mask(h));
@@ -5200,10 +5200,8 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
}
ptl = huge_pte_lock(h, mm, ptep);
- if (huge_pmd_unshare(mm, vma, address, ptep)) {
+ if (huge_pmd_unshare(tlb, vma, address, ptep)) {
spin_unlock(ptl);
- tlb_flush_pmd_range(tlb, address & PUD_MASK, PUD_SIZE);
- force_flush = true;
address |= last_addr_mask;
continue;
}
@@ -5319,21 +5317,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
}
tlb_end_vma(tlb, vma);
- /*
- * If we unshared PMDs, the TLB flush was not recorded in mmu_gather. We
- * could defer the flush until now, since by holding i_mmap_rwsem we
- * guaranteed that the last reference would not be dropped. But we must
- * do the flushing before we return, as otherwise i_mmap_rwsem will be
- * dropped and the last reference to the shared PMDs page might be
- * dropped as well.
- *
- * In theory we could defer the freeing of the PMD pages as well, but
- * huge_pmd_unshare() relies on the exact page_count for the PMD page to
- * detect sharing, so we cannot defer the release of the page either.
- * Instead, do flush now.
- */
- if (force_flush)
- tlb_flush_mmu_tlbonly(tlb);
+ huge_pmd_unshare_flush(tlb, vma);
}
void __hugetlb_zap_begin(struct vm_area_struct *vma,
@@ -6432,11 +6416,11 @@ long hugetlb_change_protection(struct vm_area_struct *vma,
pte_t pte;
struct hstate *h = hstate_vma(vma);
long pages = 0, psize = huge_page_size(h);
- bool shared_pmd = false;
struct mmu_notifier_range range;
unsigned long last_addr_mask;
bool uffd_wp = cp_flags & MM_CP_UFFD_WP;
bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE;
+ struct mmu_gather tlb;
/*
* In the case of shared PMDs, the area to flush could be beyond
@@ -6449,6 +6433,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma,
BUG_ON(address >= end);
flush_cache_range(vma, range.start, range.end);
+ tlb_gather_mmu_vma(&tlb, vma);
mmu_notifier_invalidate_range_start(&range);
hugetlb_vma_lock_write(vma);
@@ -6475,7 +6460,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma,
}
}
ptl = huge_pte_lock(h, mm, ptep);
- if (huge_pmd_unshare(mm, vma, address, ptep)) {
+ if (huge_pmd_unshare(&tlb, vma, address, ptep)) {
/*
* When uffd-wp is enabled on the vma, unshare
* shouldn't happen at all. Warn about it if it
@@ -6484,7 +6469,6 @@ long hugetlb_change_protection(struct vm_area_struct *vma,
WARN_ON_ONCE(uffd_wp || uffd_wp_resolve);
pages++;
spin_unlock(ptl);
- shared_pmd = true;
address |= last_addr_mask;
continue;
}
@@ -6545,23 +6529,16 @@ long hugetlb_change_protection(struct vm_area_struct *vma,
pte = huge_pte_clear_uffd_wp(pte);
huge_ptep_modify_prot_commit(vma, address, ptep, old_pte, pte);
pages++;
+ tlb_remove_huge_tlb_entry(h, &tlb, ptep, address);
}
next:
spin_unlock(ptl);
cond_resched();
}
- /*
- * Must flush TLB before releasing i_mmap_rwsem: x86's huge_pmd_unshare
- * may have cleared our pud entry and done put_page on the page table:
- * once we release i_mmap_rwsem, another task can do the final put_page
- * and that page table be reused and filled with junk. If we actually
- * did unshare a page of pmds, flush the range corresponding to the pud.
- */
- if (shared_pmd)
- flush_hugetlb_tlb_range(vma, range.start, range.end);
- else
- flush_hugetlb_tlb_range(vma, start, end);
+
+ tlb_flush_mmu_tlbonly(&tlb);
+ huge_pmd_unshare_flush(&tlb, vma);
/*
* No need to call mmu_notifier_arch_invalidate_secondary_tlbs() we are
* downgrading page table protection not changing it to point to a new
@@ -6572,6 +6549,7 @@ next:
i_mmap_unlock_write(vma->vm_file->f_mapping);
hugetlb_vma_unlock_write(vma);
mmu_notifier_invalidate_range_end(&range);
+ tlb_finish_mmu(&tlb);
return pages > 0 ? (pages << h->order) : pages;
}
@@ -6928,18 +6906,27 @@ out:
return pte;
}
-/*
- * unmap huge page backed by shared pte.
+/**
+ * huge_pmd_unshare - Unmap a pmd table if it is shared by multiple users
+ * @tlb: the current mmu_gather.
+ * @vma: the vma covering the pmd table.
+ * @addr: the address we are trying to unshare.
+ * @ptep: pointer into the (pmd) page table.
*
- * Called with page table lock held.
+ * Called with the page table lock held, the i_mmap_rwsem held in write mode
+ * and the hugetlb vma lock held in write mode.
*
- * returns: 1 successfully unmapped a shared pte page
- * 0 the underlying pte page is not shared, or it is the last user
+ * Note: The caller must call huge_pmd_unshare_flush() before dropping the
+ * i_mmap_rwsem.
+ *
+ * Returns: 1 if it was a shared PMD table and it got unmapped, or 0 if it
+ * was not a shared PMD table.
*/
-int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
+int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
{
unsigned long sz = huge_page_size(hstate_vma(vma));
+ struct mm_struct *mm = vma->vm_mm;
pgd_t *pgd = pgd_offset(mm, addr);
p4d_t *p4d = p4d_offset(pgd, addr);
pud_t *pud = pud_offset(p4d, addr);
@@ -6951,18 +6938,36 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
i_mmap_assert_write_locked(vma->vm_file->f_mapping);
hugetlb_vma_assert_locked(vma);
pud_clear(pud);
- /*
- * Once our caller drops the rmap lock, some other process might be
- * using this page table as a normal, non-hugetlb page table.
- * Wait for pending gup_fast() in other threads to finish before letting
- * that happen.
- */
- tlb_remove_table_sync_one();
- ptdesc_pmd_pts_dec(virt_to_ptdesc(ptep));
+
+ tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr);
+
mm_dec_nr_pmds(mm);
return 1;
}
+/*
+ * huge_pmd_unshare_flush - Complete a sequence of huge_pmd_unshare() calls
+ * @tlb: the current mmu_gather.
+ * @vma: the vma covering the pmd table.
+ *
+ * Perform necessary TLB flushes or IPI broadcasts to synchronize PMD table
+ * unsharing with concurrent page table walkers.
+ *
+ * This function must be called after a sequence of huge_pmd_unshare()
+ * calls while still holding the i_mmap_rwsem.
+ */
+void huge_pmd_unshare_flush(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+ /*
+ * We must synchronize page table unsharing such that nobody will
+ * try reusing a previously-shared page table while it might still
+ * be in use by previous sharers (TLB, GUP_fast).
+ */
+ i_mmap_assert_write_locked(vma->vm_file->f_mapping);
+
+ tlb_flush_unshared_tables(tlb);
+}
+
#else /* !CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING */
pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
@@ -6971,12 +6976,16 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
return NULL;
}
-int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
+int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
{
return 0;
}
+void huge_pmd_unshare_flush(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+}
+
void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
unsigned long *start, unsigned long *end)
{
@@ -7243,6 +7252,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
unsigned long sz = huge_page_size(h);
struct mm_struct *mm = vma->vm_mm;
struct mmu_notifier_range range;
+ struct mmu_gather tlb;
unsigned long address;
spinlock_t *ptl;
pte_t *ptep;
@@ -7254,6 +7264,8 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
return;
flush_cache_range(vma, start, end);
+ tlb_gather_mmu_vma(&tlb, vma);
+
/*
* No need to call adjust_range_if_pmd_sharing_possible(), because
* we have already done the PUD_SIZE alignment.
@@ -7272,10 +7284,10 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
if (!ptep)
continue;
ptl = huge_pte_lock(h, mm, ptep);
- huge_pmd_unshare(mm, vma, address, ptep);
+ huge_pmd_unshare(&tlb, vma, address, ptep);
spin_unlock(ptl);
}
- flush_hugetlb_tlb_range(vma, start, end);
+ huge_pmd_unshare_flush(&tlb, vma);
if (take_locks) {
i_mmap_unlock_write(vma->vm_file->f_mapping);
hugetlb_vma_unlock_write(vma);
@@ -7285,6 +7297,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
* Documentation/mm/mmu_notifier.rst.
*/
mmu_notifier_invalidate_range_end(&range);
+ tlb_finish_mmu(&tlb);
}
/*
diff --git a/mm/init-mm.c b/mm/init-mm.c
index 4600e7605cab..c5556bb9d5f0 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -44,7 +44,10 @@ struct mm_struct init_mm = {
.mm_lock_seq = SEQCNT_ZERO(init_mm.mm_lock_seq),
#endif
.user_ns = &init_user_ns,
- .cpu_bitmap = CPU_BITS_NONE,
+#ifdef CONFIG_SCHED_MM_CID
+ .mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(init_mm.mm_cid.lock),
+#endif
+ .flexible_array = MM_STRUCT_FLEXIBLE_ARRAY_INIT,
INIT_MM_CONTEXT(init_mm)
};
diff --git a/mm/internal.h b/mm/internal.h
index e430da900430..f35dbcf99a86 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -538,16 +538,8 @@ extern unsigned long highest_memmap_pfn;
bool folio_isolate_lru(struct folio *folio);
void folio_putback_lru(struct folio *folio);
extern void reclaim_throttle(pg_data_t *pgdat, enum vmscan_throttle_state reason);
-#ifdef CONFIG_NUMA
int user_proactive_reclaim(char *buf,
struct mem_cgroup *memcg, pg_data_t *pgdat);
-#else
-static inline int user_proactive_reclaim(char *buf,
- struct mem_cgroup *memcg, pg_data_t *pgdat)
-{
- return 0;
-}
-#endif
/*
* in mm/rmap.c:
diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index ed489a14dddf..b7d05c2a6d93 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -606,4 +606,25 @@ void __kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms,
__kasan_unpoison_vmalloc(addr, size, flags | KASAN_VMALLOC_KEEP_TAG);
}
}
+
+void __kasan_vrealloc(const void *addr, unsigned long old_size,
+ unsigned long new_size)
+{
+ if (new_size < old_size) {
+ kasan_poison_last_granule(addr, new_size);
+
+ new_size = round_up(new_size, KASAN_GRANULE_SIZE);
+ old_size = round_up(old_size, KASAN_GRANULE_SIZE);
+ if (new_size < old_size)
+ __kasan_poison_vmalloc(addr + new_size,
+ old_size - new_size);
+ } else if (new_size > old_size) {
+ old_size = round_down(old_size, KASAN_GRANULE_SIZE);
+ __kasan_unpoison_vmalloc(addr + old_size,
+ new_size - old_size,
+ KASAN_VMALLOC_PROT_NORMAL |
+ KASAN_VMALLOC_VM_ALLOC |
+ KASAN_VMALLOC_KEEP_TAG);
+ }
+}
#endif
diff --git a/mm/kfence/core.c b/mm/kfence/core.c
index 577a1699c553..4f79ec720752 100644
--- a/mm/kfence/core.c
+++ b/mm/kfence/core.c
@@ -596,7 +596,7 @@ static void rcu_guarded_free(struct rcu_head *h)
static unsigned long kfence_init_pool(void)
{
unsigned long addr, start_pfn;
- int i;
+ int i, rand;
if (!arch_kfence_init_pool())
return (unsigned long)__kfence_pool;
@@ -647,13 +647,27 @@ static unsigned long kfence_init_pool(void)
INIT_LIST_HEAD(&meta->list);
raw_spin_lock_init(&meta->lock);
meta->state = KFENCE_OBJECT_UNUSED;
- meta->addr = addr; /* Initialize for validation in metadata_to_pageaddr(). */
- list_add_tail(&meta->list, &kfence_freelist);
+ /* Use addr to randomize the freelist. */
+ meta->addr = i;
/* Protect the right redzone. */
- if (unlikely(!kfence_protect(addr + PAGE_SIZE)))
+ if (unlikely(!kfence_protect(addr + 2 * i * PAGE_SIZE + PAGE_SIZE)))
goto reset_slab;
+ }
+
+ for (i = CONFIG_KFENCE_NUM_OBJECTS; i > 0; i--) {
+ rand = get_random_u32_below(i);
+ swap(kfence_metadata_init[i - 1].addr, kfence_metadata_init[rand].addr);
+ }
+ for (i = 0; i < CONFIG_KFENCE_NUM_OBJECTS; i++) {
+ struct kfence_metadata *meta_1 = &kfence_metadata_init[i];
+ struct kfence_metadata *meta_2 = &kfence_metadata_init[meta_1->addr];
+
+ list_add_tail(&meta_2->list, &kfence_freelist);
+ }
+ for (i = 0; i < CONFIG_KFENCE_NUM_OBJECTS; i++) {
+ kfence_metadata_init[i].addr = addr;
addr += 2 * PAGE_SIZE;
}
@@ -666,6 +680,7 @@ static unsigned long kfence_init_pool(void)
return 0;
reset_slab:
+ addr += 2 * i * PAGE_SIZE;
for (i = 0; i < KFENCE_POOL_SIZE / PAGE_SIZE; i++) {
struct page *page;
@@ -823,6 +838,9 @@ static struct notifier_block kfence_check_canary_notifier = {
static struct delayed_work kfence_timer;
#ifdef CONFIG_KFENCE_STATIC_KEYS
+/* Wait queue to wake up allocation-gate timer task. */
+static DECLARE_WAIT_QUEUE_HEAD(allocation_wait);
+
static int kfence_reboot_callback(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -832,7 +850,12 @@ static int kfence_reboot_callback(struct notifier_block *nb,
*/
WRITE_ONCE(kfence_enabled, false);
/* Cancel any pending timer work */
- cancel_delayed_work_sync(&kfence_timer);
+ cancel_delayed_work(&kfence_timer);
+ /*
+ * Wake up any blocked toggle_allocation_gate() so it can complete
+ * early while the system is still able to handle IPIs.
+ */
+ wake_up(&allocation_wait);
return NOTIFY_OK;
}
@@ -842,9 +865,6 @@ static struct notifier_block kfence_reboot_notifier = {
.priority = INT_MAX, /* Run early to stop timers ASAP */
};
-/* Wait queue to wake up allocation-gate timer task. */
-static DECLARE_WAIT_QUEUE_HEAD(allocation_wait);
-
static void wake_up_kfence_timer(struct irq_work *work)
{
wake_up(&allocation_wait);
@@ -873,7 +893,9 @@ static void toggle_allocation_gate(struct work_struct *work)
/* Enable static key, and await allocation to happen. */
static_branch_enable(&kfence_allocation_key);
- wait_event_idle(allocation_wait, atomic_read(&kfence_allocation_gate) > 0);
+ wait_event_idle(allocation_wait,
+ atomic_read(&kfence_allocation_gate) > 0 ||
+ !READ_ONCE(kfence_enabled));
/* Disable static key and reset timer. */
static_branch_disable(&kfence_allocation_key);
diff --git a/mm/memfd.c b/mm/memfd.c
index ab5312aff14b..f032c6052926 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -456,7 +456,7 @@ err_name:
return ERR_PTR(error);
}
-static struct file *alloc_file(const char *name, unsigned int flags)
+struct file *memfd_alloc_file(const char *name, unsigned int flags)
{
unsigned int *file_seals;
struct file *file;
@@ -520,5 +520,5 @@ SYSCALL_DEFINE2(memfd_create,
return PTR_ERR(name);
fd_flags = (flags & MFD_CLOEXEC) ? O_CLOEXEC : 0;
- return FD_ADD(fd_flags, alloc_file(name, flags));
+ return FD_ADD(fd_flags, memfd_alloc_file(name, flags));
}
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index 4f6ba63b4310..a34fccc23b6a 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -78,6 +78,7 @@
#include <linux/liveupdate.h>
#include <linux/shmem_fs.h>
#include <linux/vmalloc.h>
+#include <linux/memfd.h>
#include "internal.h"
static int memfd_luo_preserve_folios(struct file *file,
@@ -443,11 +444,11 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
if (!ser)
return -EINVAL;
- file = shmem_file_setup("", 0, VM_NORESERVE);
-
+ file = memfd_alloc_file("", 0);
if (IS_ERR(file)) {
pr_err("failed to setup file: %pe\n", file);
- return PTR_ERR(file);
+ err = PTR_ERR(file);
+ goto free_ser;
}
vfs_setpos(file, ser->pos, MAX_LFS_FILESIZE);
@@ -473,7 +474,8 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
put_file:
fput(file);
-
+free_ser:
+ kho_restore_free(ser);
return err;
}
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index c80c2907da33..cf0d526e6d41 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -692,6 +692,8 @@ static int check_hwpoisoned_entry(pte_t pte, unsigned long addr, short shift,
unsigned long poisoned_pfn, struct to_kill *tk)
{
unsigned long pfn = 0;
+ unsigned long hwpoison_vaddr;
+ unsigned long mask;
if (pte_present(pte)) {
pfn = pte_pfn(pte);
@@ -702,10 +704,12 @@ static int check_hwpoisoned_entry(pte_t pte, unsigned long addr, short shift,
pfn = softleaf_to_pfn(entry);
}
- if (!pfn || pfn != poisoned_pfn)
+ mask = ~((1UL << (shift - PAGE_SHIFT)) - 1);
+ if (!pfn || pfn != (poisoned_pfn & mask))
return 0;
- set_to_kill(tk, addr, shift);
+ hwpoison_vaddr = addr + ((poisoned_pfn - pfn) << PAGE_SHIFT);
+ set_to_kill(tk, hwpoison_vaddr, shift);
return 1;
}
@@ -1883,12 +1887,22 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
return count;
}
-static int folio_set_hugetlb_hwpoison(struct folio *folio, struct page *page)
+#define MF_HUGETLB_FREED 0 /* freed hugepage */
+#define MF_HUGETLB_IN_USED 1 /* in-use hugepage */
+#define MF_HUGETLB_NON_HUGEPAGE 2 /* not a hugepage */
+#define MF_HUGETLB_FOLIO_PRE_POISONED 3 /* folio already poisoned */
+#define MF_HUGETLB_PAGE_PRE_POISONED 4 /* exact page already poisoned */
+#define MF_HUGETLB_RETRY 5 /* hugepage is busy, retry */
+/*
+ * Set hugetlb folio as hwpoisoned, update folio private raw hwpoison list
+ * to keep track of the poisoned pages.
+ */
+static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)
{
struct llist_head *head;
struct raw_hwp_page *raw_hwp;
struct raw_hwp_page *p;
- int ret = folio_test_set_hwpoison(folio) ? -EHWPOISON : 0;
+ int ret = folio_test_set_hwpoison(folio) ? MF_HUGETLB_FOLIO_PRE_POISONED : 0;
/*
* Once the hwpoison hugepage has lost reliable raw error info,
@@ -1896,20 +1910,17 @@ static int folio_set_hugetlb_hwpoison(struct folio *folio, struct page *page)
* so skip to add additional raw error info.
*/
if (folio_test_hugetlb_raw_hwp_unreliable(folio))
- return -EHWPOISON;
+ return MF_HUGETLB_FOLIO_PRE_POISONED;
head = raw_hwp_list_head(folio);
llist_for_each_entry(p, head->first, node) {
if (p->page == page)
- return -EHWPOISON;
+ return MF_HUGETLB_PAGE_PRE_POISONED;
}
raw_hwp = kmalloc(sizeof(struct raw_hwp_page), GFP_ATOMIC);
if (raw_hwp) {
raw_hwp->page = page;
llist_add(&raw_hwp->node, head);
- /* the first error event will be counted in action_result(). */
- if (ret)
- num_poisoned_pages_inc(page_to_pfn(page));
} else {
/*
* Failed to save raw error info. We no longer trace all
@@ -1957,42 +1968,39 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
/*
* Called from hugetlb code with hugetlb_lock held.
- *
- * Return values:
- * 0 - free hugepage
- * 1 - in-use hugepage
- * 2 - not a hugepage
- * -EBUSY - the hugepage is busy (try to retry)
- * -EHWPOISON - the hugepage is already hwpoisoned
*/
int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared)
{
struct page *page = pfn_to_page(pfn);
struct folio *folio = page_folio(page);
- int ret = 2; /* fallback to normal page handling */
bool count_increased = false;
+ int ret, rc;
- if (!folio_test_hugetlb(folio))
+ if (!folio_test_hugetlb(folio)) {
+ ret = MF_HUGETLB_NON_HUGEPAGE;
goto out;
-
- if (flags & MF_COUNT_INCREASED) {
- ret = 1;
+ } else if (flags & MF_COUNT_INCREASED) {
+ ret = MF_HUGETLB_IN_USED;
count_increased = true;
} else if (folio_test_hugetlb_freed(folio)) {
- ret = 0;
+ ret = MF_HUGETLB_FREED;
} else if (folio_test_hugetlb_migratable(folio)) {
- ret = folio_try_get(folio);
- if (ret)
+ if (folio_try_get(folio)) {
+ ret = MF_HUGETLB_IN_USED;
count_increased = true;
+ } else {
+ ret = MF_HUGETLB_FREED;
+ }
} else {
- ret = -EBUSY;
+ ret = MF_HUGETLB_RETRY;
if (!(flags & MF_NO_RETRY))
goto out;
}
- if (folio_set_hugetlb_hwpoison(folio, page)) {
- ret = -EHWPOISON;
+ rc = hugetlb_update_hwpoison(folio, page);
+ if (rc >= MF_HUGETLB_FOLIO_PRE_POISONED) {
+ ret = rc;
goto out;
}
@@ -2017,10 +2025,16 @@ out:
* with basic operations like hugepage allocation/free/demotion.
* So some of prechecks for hwpoison (pinning, and testing/setting
* PageHWPoison) should be done in single hugetlb_lock range.
+ * Returns:
+ * 0 - not hugetlb, or recovered
+ * -EBUSY - not recovered
+ * -EOPNOTSUPP - hwpoison_filter'ed
+ * -EHWPOISON - folio or exact page already poisoned
+ * -EFAULT - kill_accessing_process finds current->mm null
*/
static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb)
{
- int res;
+ int res, rv;
struct page *p = pfn_to_page(pfn);
struct folio *folio;
unsigned long page_flags;
@@ -2029,22 +2043,29 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb
*hugetlb = 1;
retry:
res = get_huge_page_for_hwpoison(pfn, flags, &migratable_cleared);
- if (res == 2) { /* fallback to normal page handling */
+ switch (res) {
+ case MF_HUGETLB_NON_HUGEPAGE: /* fallback to normal page handling */
*hugetlb = 0;
return 0;
- } else if (res == -EHWPOISON) {
- if (flags & MF_ACTION_REQUIRED) {
- folio = page_folio(p);
- res = kill_accessing_process(current, folio_pfn(folio), flags);
- }
- action_result(pfn, MF_MSG_ALREADY_POISONED, MF_FAILED);
- return res;
- } else if (res == -EBUSY) {
+ case MF_HUGETLB_RETRY:
if (!(flags & MF_NO_RETRY)) {
flags |= MF_NO_RETRY;
goto retry;
}
return action_result(pfn, MF_MSG_GET_HWPOISON, MF_IGNORED);
+ case MF_HUGETLB_FOLIO_PRE_POISONED:
+ case MF_HUGETLB_PAGE_PRE_POISONED:
+ rv = -EHWPOISON;
+ if (flags & MF_ACTION_REQUIRED)
+ rv = kill_accessing_process(current, pfn, flags);
+ if (res == MF_HUGETLB_PAGE_PRE_POISONED)
+ action_result(pfn, MF_MSG_ALREADY_POISONED, MF_FAILED);
+ else
+ action_result(pfn, MF_MSG_HUGE, MF_FAILED);
+ return rv;
+ default:
+ WARN_ON((res != MF_HUGETLB_FREED) && (res != MF_HUGETLB_IN_USED));
+ break;
}
folio = page_folio(p);
@@ -2055,7 +2076,7 @@ retry:
if (migratable_cleared)
folio_set_hugetlb_migratable(folio);
folio_unlock(folio);
- if (res == 1)
+ if (res == MF_HUGETLB_IN_USED)
folio_put(folio);
return -EOPNOTSUPP;
}
@@ -2064,7 +2085,7 @@ retry:
* Handling free hugepage. The possible race with hugepage allocation
* or demotion can be prevented by PageHWPoison flag.
*/
- if (res == 0) {
+ if (res == MF_HUGETLB_FREED) {
folio_unlock(folio);
if (__page_handle_poison(p) > 0) {
page_ref_inc(p);
diff --git a/mm/memory.c b/mm/memory.c
index 2a55edc48a65..da360a6eb8a4 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1465,7 +1465,11 @@ copy_p4d_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma,
static bool
vma_needs_copy(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma)
{
- if (src_vma->vm_flags & VM_COPY_ON_FORK)
+ /*
+ * We check against dst_vma as while sane VMA flags will have been
+ * copied, VM_UFFD_WP may be set only on dst_vma.
+ */
+ if (dst_vma->vm_flags & VM_COPY_ON_FORK)
return true;
/*
* The presence of an anon_vma indicates an anonymous VMA has page
@@ -1963,10 +1967,9 @@ static inline unsigned long zap_pud_range(struct mmu_gather *tlb,
do {
next = pud_addr_end(addr, end);
if (pud_trans_huge(*pud)) {
- if (next - addr != HPAGE_PUD_SIZE) {
- mmap_assert_locked(tlb->mm);
+ if (next - addr != HPAGE_PUD_SIZE)
split_huge_pud(vma, pud, addr);
- } else if (zap_huge_pud(tlb, vma, pud, addr))
+ else if (zap_huge_pud(tlb, vma, pud, addr))
goto next;
/* fall through */
}
diff --git a/mm/memremap.c b/mm/memremap.c
index 63c6ab4fdf08..ac7be07e3361 100644
--- a/mm/memremap.c
+++ b/mm/memremap.c
@@ -477,10 +477,43 @@ void free_zone_device_folio(struct folio *folio)
}
}
-void zone_device_page_init(struct page *page, unsigned int order)
+void zone_device_page_init(struct page *page, struct dev_pagemap *pgmap,
+ unsigned int order)
{
+ struct page *new_page = page;
+ unsigned int i;
+
VM_WARN_ON_ONCE(order > MAX_ORDER_NR_PAGES);
+ for (i = 0; i < (1UL << order); ++i, ++new_page) {
+ struct folio *new_folio = (struct folio *)new_page;
+
+ /*
+ * new_page could have been part of previous higher order folio
+ * which encodes the order, in page + 1, in the flags bits. We
+ * blindly clear bits which could have set my order field here,
+ * including page head.
+ */
+ new_page->flags.f &= ~0xffUL; /* Clear possible order, page head */
+
+#ifdef NR_PAGES_IN_LARGE_FOLIO
+ /*
+ * This pointer math looks odd, but new_page could have been
+ * part of a previous higher order folio, which sets _nr_pages
+ * in page + 1 (new_page). Therefore, we use pointer casting to
+ * correctly locate the _nr_pages bits within new_page which
+ * could have modified by previous higher order folio.
+ */
+ ((struct folio *)(new_page - 1))->_nr_pages = 0;
+#endif
+
+ new_folio->mapping = NULL;
+ new_folio->pgmap = pgmap; /* Also clear compound head */
+ new_folio->share = 0; /* fsdax only, unused for device private */
+ VM_WARN_ON_FOLIO(folio_ref_count(new_folio), new_folio);
+ VM_WARN_ON_FOLIO(!folio_is_zone_device(new_folio), new_folio);
+ }
+
/*
* Drivers shouldn't be allocating pages after calling
* memunmap_pages().
diff --git a/mm/migrate.c b/mm/migrate.c
index 5169f9717f60..4688b9e38cd2 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1458,6 +1458,7 @@ static int unmap_and_move_huge_page(new_folio_t get_new_folio,
int page_was_mapped = 0;
struct anon_vma *anon_vma = NULL;
struct address_space *mapping = NULL;
+ enum ttu_flags ttu = 0;
if (folio_ref_count(src) == 1) {
/* page was freed from under us. So we are done. */
@@ -1498,8 +1499,6 @@ static int unmap_and_move_huge_page(new_folio_t get_new_folio,
goto put_anon;
if (folio_mapped(src)) {
- enum ttu_flags ttu = 0;
-
if (!folio_test_anon(src)) {
/*
* In shared mappings, try_to_unmap could potentially
@@ -1516,16 +1515,17 @@ static int unmap_and_move_huge_page(new_folio_t get_new_folio,
try_to_migrate(src, ttu);
page_was_mapped = 1;
-
- if (ttu & TTU_RMAP_LOCKED)
- i_mmap_unlock_write(mapping);
}
if (!folio_mapped(src))
rc = move_to_new_folio(dst, src, mode);
if (page_was_mapped)
- remove_migration_ptes(src, !rc ? dst : src, 0);
+ remove_migration_ptes(src, !rc ? dst : src,
+ ttu ? RMP_LOCKED : 0);
+
+ if (ttu & TTU_RMAP_LOCKED)
+ i_mmap_unlock_write(mapping);
unlock_put_anon:
folio_unlock(dst);
diff --git a/mm/mm_init.c b/mm/mm_init.c
index fc2a6f1e518f..2a809cd8e7fa 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -2059,7 +2059,7 @@ static unsigned long __init deferred_init_pages(struct zone *zone,
*/
static unsigned long __init
deferred_init_memmap_chunk(unsigned long start_pfn, unsigned long end_pfn,
- struct zone *zone)
+ struct zone *zone, bool can_resched)
{
int nid = zone_to_nid(zone);
unsigned long nr_pages = 0;
@@ -2085,10 +2085,10 @@ deferred_init_memmap_chunk(unsigned long start_pfn, unsigned long end_pfn,
spfn = chunk_end;
- if (irqs_disabled())
- touch_nmi_watchdog();
- else
+ if (can_resched)
cond_resched();
+ else
+ touch_nmi_watchdog();
}
}
@@ -2101,7 +2101,7 @@ deferred_init_memmap_job(unsigned long start_pfn, unsigned long end_pfn,
{
struct zone *zone = arg;
- deferred_init_memmap_chunk(start_pfn, end_pfn, zone);
+ deferred_init_memmap_chunk(start_pfn, end_pfn, zone, true);
}
static unsigned int __init
@@ -2216,7 +2216,7 @@ bool __init deferred_grow_zone(struct zone *zone, unsigned int order)
for (spfn = first_deferred_pfn, epfn = SECTION_ALIGN_UP(spfn + 1);
nr_pages < nr_pages_needed && spfn < zone_end_pfn(zone);
spfn = epfn, epfn += PAGES_PER_SECTION) {
- nr_pages += deferred_init_memmap_chunk(spfn, epfn, zone);
+ nr_pages += deferred_init_memmap_chunk(spfn, epfn, zone, false);
}
/*
diff --git a/mm/mmu_gather.c b/mm/mmu_gather.c
index 247e3f9db6c7..7468ec388455 100644
--- a/mm/mmu_gather.c
+++ b/mm/mmu_gather.c
@@ -10,6 +10,7 @@
#include <linux/swap.h>
#include <linux/rmap.h>
#include <linux/pgalloc.h>
+#include <linux/hugetlb.h>
#include <asm/tlb.h>
@@ -426,6 +427,7 @@ static void __tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
#endif
tlb->vma_pfn = 0;
+ tlb->fully_unshared_tables = 0;
__tlb_reset_range(tlb);
inc_tlb_flush_pending(tlb->mm);
}
@@ -460,6 +462,31 @@ void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm)
}
/**
+ * tlb_gather_mmu_vma - initialize an mmu_gather structure for operating on a
+ * single VMA
+ * @tlb: the mmu_gather structure to initialize
+ * @vma: the vm_area_struct
+ *
+ * Called to initialize an (on-stack) mmu_gather structure for operating on
+ * a single VMA. In contrast to tlb_gather_mmu(), calling this function will
+ * not require another call to tlb_start_vma(). In contrast to tlb_start_vma(),
+ * this function will *not* call flush_cache_range().
+ *
+ * For hugetlb VMAs, this function will also initialize the mmu_gather
+ * page_size accordingly, not requiring a separate call to
+ * tlb_change_page_size().
+ *
+ */
+void tlb_gather_mmu_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+ tlb_gather_mmu(tlb, vma->vm_mm);
+ tlb_update_vma_flags(tlb, vma);
+ if (is_vm_hugetlb_page(vma))
+ /* All entries have the same size. */
+ tlb_change_page_size(tlb, huge_page_size(hstate_vma(vma)));
+}
+
+/**
* tlb_finish_mmu - finish an mmu_gather structure
* @tlb: the mmu_gather structure to finish
*
@@ -469,6 +496,12 @@ void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm)
void tlb_finish_mmu(struct mmu_gather *tlb)
{
/*
+ * We expect an earlier huge_pmd_unshare_flush() call to sort this out,
+ * due to complicated locking requirements with page table unsharing.
+ */
+ VM_WARN_ON_ONCE(tlb->fully_unshared_tables);
+
+ /*
* If there are parallel threads are doing PTE changes on same range
* under non-exclusive lock (e.g., mmap_lock read-side) but defer TLB
* flush by batching, one thread may end up seeing inconsistent PTEs
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index f65c4edf199d..cbf758e27aa2 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -7457,20 +7457,16 @@ bool put_page_back_buddy(struct page *page)
}
#endif
-#ifdef CONFIG_ZONE_DMA
-bool has_managed_dma(void)
+bool has_managed_zone(enum zone_type zone)
{
struct pglist_data *pgdat;
for_each_online_pgdat(pgdat) {
- struct zone *zone = &pgdat->node_zones[ZONE_DMA];
-
- if (managed_zone(zone))
+ if (managed_zone(&pgdat->node_zones[zone]))
return true;
}
return false;
}
-#endif /* CONFIG_ZONE_DMA */
#ifdef CONFIG_UNACCEPTED_MEMORY
diff --git a/mm/rmap.c b/mm/rmap.c
index f955f02d570e..7b9879ef442d 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -76,7 +76,7 @@
#include <linux/mm_inline.h>
#include <linux/oom.h>
-#include <asm/tlbflush.h>
+#include <asm/tlb.h>
#define CREATE_TRACE_POINTS
#include <trace/events/migrate.h>
@@ -2008,26 +2008,25 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
* if unsuccessful.
*/
if (!anon) {
+ struct mmu_gather tlb;
+
VM_BUG_ON(!(flags & TTU_RMAP_LOCKED));
if (!hugetlb_vma_trylock_write(vma))
goto walk_abort;
- if (huge_pmd_unshare(mm, vma, address, pvmw.pte)) {
+
+ tlb_gather_mmu_vma(&tlb, vma);
+ if (huge_pmd_unshare(&tlb, vma, address, pvmw.pte)) {
hugetlb_vma_unlock_write(vma);
- flush_tlb_range(vma,
- range.start, range.end);
+ huge_pmd_unshare_flush(&tlb, vma);
+ tlb_finish_mmu(&tlb);
/*
- * The ref count of the PMD page was
- * dropped which is part of the way map
- * counting is done for shared PMDs.
- * Return 'true' here. When there is
- * no other sharing, huge_pmd_unshare
- * returns false and we will unmap the
- * actual page and drop map count
- * to zero.
+ * The PMD table was unmapped,
+ * consequently unmapping the folio.
*/
goto walk_done;
}
hugetlb_vma_unlock_write(vma);
+ tlb_finish_mmu(&tlb);
}
pteval = huge_ptep_clear_flush(vma, address, pvmw.pte);
if (pte_dirty(pteval))
@@ -2404,31 +2403,29 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
* fail if unsuccessful.
*/
if (!anon) {
+ struct mmu_gather tlb;
+
VM_BUG_ON(!(flags & TTU_RMAP_LOCKED));
if (!hugetlb_vma_trylock_write(vma)) {
page_vma_mapped_walk_done(&pvmw);
ret = false;
break;
}
- if (huge_pmd_unshare(mm, vma, address, pvmw.pte)) {
- hugetlb_vma_unlock_write(vma);
- flush_tlb_range(vma,
- range.start, range.end);
+ tlb_gather_mmu_vma(&tlb, vma);
+ if (huge_pmd_unshare(&tlb, vma, address, pvmw.pte)) {
+ hugetlb_vma_unlock_write(vma);
+ huge_pmd_unshare_flush(&tlb, vma);
+ tlb_finish_mmu(&tlb);
/*
- * The ref count of the PMD page was
- * dropped which is part of the way map
- * counting is done for shared PMDs.
- * Return 'true' here. When there is
- * no other sharing, huge_pmd_unshare
- * returns false and we will unmap the
- * actual page and drop map count
- * to zero.
+ * The PMD table was unmapped,
+ * consequently unmapping the folio.
*/
page_vma_mapped_walk_done(&pvmw);
break;
}
hugetlb_vma_unlock_write(vma);
+ tlb_finish_mmu(&tlb);
}
/* Nuke the hugetlb page table entry */
pteval = huge_ptep_clear_flush(vma, address, pvmw.pte);
diff --git a/mm/shmem.c b/mm/shmem.c
index ec6c01378e9d..6c3485d24d66 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -962,17 +962,29 @@ static void shmem_delete_from_page_cache(struct folio *folio, void *radswap)
* being freed).
*/
static long shmem_free_swap(struct address_space *mapping,
- pgoff_t index, void *radswap)
+ pgoff_t index, pgoff_t end, void *radswap)
{
- int order = xa_get_order(&mapping->i_pages, index);
- void *old;
+ XA_STATE(xas, &mapping->i_pages, index);
+ unsigned int nr_pages = 0;
+ pgoff_t base;
+ void *entry;
- old = xa_cmpxchg_irq(&mapping->i_pages, index, radswap, NULL, 0);
- if (old != radswap)
- return 0;
- free_swap_and_cache_nr(radix_to_swp_entry(radswap), 1 << order);
+ xas_lock_irq(&xas);
+ entry = xas_load(&xas);
+ if (entry == radswap) {
+ nr_pages = 1 << xas_get_order(&xas);
+ base = round_down(xas.xa_index, nr_pages);
+ if (base < index || base + nr_pages - 1 > end)
+ nr_pages = 0;
+ else
+ xas_store(&xas, NULL);
+ }
+ xas_unlock_irq(&xas);
+
+ if (nr_pages)
+ free_swap_and_cache_nr(radix_to_swp_entry(radswap), nr_pages);
- return 1 << order;
+ return nr_pages;
}
/*
@@ -1124,8 +1136,8 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
if (xa_is_value(folio)) {
if (unfalloc)
continue;
- nr_swaps_freed += shmem_free_swap(mapping,
- indices[i], folio);
+ nr_swaps_freed += shmem_free_swap(mapping, indices[i],
+ end - 1, folio);
continue;
}
@@ -1191,12 +1203,23 @@ whole_folios:
folio = fbatch.folios[i];
if (xa_is_value(folio)) {
+ int order;
long swaps_freed;
if (unfalloc)
continue;
- swaps_freed = shmem_free_swap(mapping, indices[i], folio);
+ swaps_freed = shmem_free_swap(mapping, indices[i],
+ end - 1, folio);
if (!swaps_freed) {
+ /*
+ * If found a large swap entry cross the end border,
+ * skip it as the truncate_inode_partial_folio above
+ * should have at least zerod its content once.
+ */
+ order = shmem_confirm_swap(mapping, indices[i],
+ radix_to_swp_entry(folio));
+ if (order > 0 && indices[i] + (1 << order) > end)
+ continue;
/* Swap was replaced by page: retry */
index = indices[i];
break;
diff --git a/mm/slub.c b/mm/slub.c
index 861592ac5425..f77b7407c51b 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -5694,8 +5694,12 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node)
if (unlikely(!size))
return ZERO_SIZE_PTR;
- if (IS_ENABLED(CONFIG_PREEMPT_RT) && (in_nmi() || in_hardirq()))
- /* kmalloc_nolock() in PREEMPT_RT is not supported from irq */
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && !preemptible())
+ /*
+ * kmalloc_nolock() in PREEMPT_RT is not supported from
+ * non-preemptible context because local_lock becomes a
+ * sleeping lock on RT.
+ */
return NULL;
retry:
if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
diff --git a/mm/swap.h b/mm/swap.h
index d034c13d8dd2..1bd466da3039 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -198,7 +198,7 @@ int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug);
void __swap_writepage(struct folio *folio, struct swap_iocb **swap_plug);
/* linux/mm/swap_state.c */
-extern struct address_space swap_space __ro_after_init;
+extern struct address_space swap_space __read_mostly;
static inline struct address_space *swap_address_space(swp_entry_t entry)
{
return &swap_space;
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 5f97c6ae70a2..44d228982521 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -37,8 +37,7 @@ static const struct address_space_operations swap_aops = {
#endif
};
-/* Set swap_space as read only as swap cache is handled by swap table */
-struct address_space swap_space __ro_after_init = {
+struct address_space swap_space __read_mostly = {
.a_ops = &swap_aops,
};
diff --git a/mm/vma.c b/mm/vma.c
index dc92f3dd8514..7a908a964d18 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -37,6 +37,8 @@ struct mmap_state {
bool check_ksm_early :1;
/* If we map new, hold the file rmap lock on mapping. */
bool hold_file_rmap_lock :1;
+ /* If .mmap_prepare changed the file, we don't need to pin. */
+ bool file_doesnt_need_get :1;
};
#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, vm_flags_, file_) \
@@ -2450,7 +2452,9 @@ static int __mmap_new_file_vma(struct mmap_state *map,
struct vma_iterator *vmi = map->vmi;
int error;
- vma->vm_file = get_file(map->file);
+ vma->vm_file = map->file;
+ if (!map->file_doesnt_need_get)
+ get_file(map->file);
if (!map->file->f_op->mmap)
return 0;
@@ -2638,7 +2642,10 @@ static int call_mmap_prepare(struct mmap_state *map,
/* Update fields permitted to be changed. */
map->pgoff = desc->pgoff;
- map->file = desc->vm_file;
+ if (desc->vm_file != map->file) {
+ map->file_doesnt_need_get = true;
+ map->file = desc->vm_file;
+ }
map->vm_flags = desc->vm_flags;
map->page_prot = desc->page_prot;
/* User-defined fields. */
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 628f96e83b11..e286c2d2068c 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4322,7 +4322,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
if (want_init_on_free() || want_init_on_alloc(flags))
memset((void *)p + size, 0, old_size - size);
vm->requested_size = size;
- kasan_poison_vmalloc(p + size, old_size - size);
+ kasan_vrealloc(p, old_size, size);
return (void *)p;
}
@@ -4330,16 +4330,13 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
* We already have the bytes available in the allocation; use them.
*/
if (size <= alloced_size) {
- kasan_unpoison_vmalloc(p + old_size, size - old_size,
- KASAN_VMALLOC_PROT_NORMAL |
- KASAN_VMALLOC_VM_ALLOC |
- KASAN_VMALLOC_KEEP_TAG);
/*
* No need to zero memory here, as unused memory will have
* already been zeroed at initial allocation time or during
* realloc shrink time.
*/
vm->requested_size = size;
+ kasan_vrealloc(p, old_size, size);
return (void *)p;
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 670fe9fae5ba..614ccf39fe3f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -7707,6 +7707,17 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
return ret;
}
+#else
+
+static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask,
+ unsigned long nr_pages,
+ struct scan_control *sc)
+{
+ return 0;
+}
+
+#endif
+
enum {
MEMORY_RECLAIM_SWAPPINESS = 0,
MEMORY_RECLAIM_SWAPPINESS_MAX,
@@ -7814,8 +7825,6 @@ int user_proactive_reclaim(char *buf,
return 0;
}
-#endif
-
/**
* check_move_unevictable_folios - Move evictable folios to appropriate zone
* lru list
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 5be9b8c91949..0e46f9e08b10 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1966,6 +1966,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
}
mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_err);
+ mgmt_pending_free(cmd);
return;
}
@@ -1984,6 +1985,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
sock_put(match.sk);
hci_update_eir_sync(hdev);
+ mgmt_pending_free(cmd);
}
static int set_ssp_sync(struct hci_dev *hdev, void *data)
@@ -6438,6 +6440,7 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
hci_dev_clear_flag(hdev, HCI_ADVERTISING);
settings_rsp(cmd, &match);
+ mgmt_pending_free(cmd);
new_settings(hdev, match.sk);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index e355a15bf5ab..1405f1061a54 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -274,7 +274,7 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
int ret;
net = dev_net(skb->dev);
-#ifdef HAVE_JUMP_LABEL
+#ifdef CONFIG_JUMP_LABEL
if (!static_key_false(&nf_hooks_needed[NFPROTO_BRIDGE][NF_BR_PRE_ROUTING]))
goto frame_finish;
#endif
diff --git a/net/core/filter.c b/net/core/filter.c
index 616e0520a0bb..bcd73d9bd764 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3353,6 +3353,7 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
shinfo->gso_type &= ~SKB_GSO_TCPV4;
shinfo->gso_type |= SKB_GSO_TCPV6;
}
+ shinfo->gso_type |= SKB_GSO_DODGY;
}
bpf_skb_change_protocol(skb, ETH_P_IPV6);
@@ -3383,6 +3384,7 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
shinfo->gso_type &= ~SKB_GSO_TCPV6;
shinfo->gso_type |= SKB_GSO_TCPV4;
}
+ shinfo->gso_type |= SKB_GSO_DODGY;
}
bpf_skb_change_protocol(skb, ETH_P_IP);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index a56133902c0d..61746c2b95f6 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1312,14 +1312,15 @@ void skb_dump(const char *level, const struct sk_buff *skb, bool full_pkt)
has_mac = skb_mac_header_was_set(skb);
has_trans = skb_transport_header_was_set(skb);
- printk("%sskb len=%u headroom=%u headlen=%u tailroom=%u\n"
- "mac=(%d,%d) mac_len=%u net=(%d,%d) trans=%d\n"
+ printk("%sskb len=%u data_len=%u headroom=%u headlen=%u tailroom=%u\n"
+ "end-tail=%u mac=(%d,%d) mac_len=%u net=(%d,%d) trans=%d\n"
"shinfo(txflags=%u nr_frags=%u gso(size=%hu type=%u segs=%hu))\n"
"csum(0x%x start=%u offset=%u ip_summed=%u complete_sw=%u valid=%u level=%u)\n"
"hash(0x%x sw=%u l4=%u) proto=0x%04x pkttype=%u iif=%d\n"
"priority=0x%x mark=0x%x alloc_cpu=%u vlan_all=0x%x\n"
"encapsulation=%d inner(proto=0x%04x, mac=%u, net=%u, trans=%u)\n",
- level, skb->len, headroom, skb_headlen(skb), tailroom,
+ level, skb->len, skb->data_len, headroom, skb_headlen(skb),
+ tailroom, skb->end - skb->tail,
has_mac ? skb->mac_header : -1,
has_mac ? skb_mac_header_len(skb) : -1,
skb->mac_len,
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 99ede37698ac..35ce3941fae3 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -158,7 +158,7 @@ unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max)
bridge_num = find_next_zero_bit(&dsa_fwd_offloading_bridges,
DSA_MAX_NUM_OFFLOADING_BRIDGES,
1);
- if (bridge_num >= max)
+ if (bridge_num > max)
return 0;
set_bit(bridge_num, &dsa_fwd_offloading_bridges);
diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c
index 3970b6b7ace5..ab8f309f8925 100644
--- a/net/ipv4/fou_core.c
+++ b/net/ipv4/fou_core.c
@@ -215,6 +215,9 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
return gue_control_message(skb, guehdr);
proto_ctype = guehdr->proto_ctype;
+ if (unlikely(!proto_ctype))
+ goto drop;
+
__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
skb_reset_transport_header(skb);
diff --git a/net/ipv4/fou_nl.c b/net/ipv4/fou_nl.c
index 7a99639204b1..309d5ba983d0 100644
--- a/net/ipv4/fou_nl.c
+++ b/net/ipv4/fou_nl.c
@@ -15,7 +15,7 @@
const struct nla_policy fou_nl_policy[FOU_ATTR_IFINDEX + 1] = {
[FOU_ATTR_PORT] = { .type = NLA_BE16, },
[FOU_ATTR_AF] = { .type = NLA_U8, },
- [FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
+ [FOU_ATTR_IPPROTO] = NLA_POLICY_MIN(NLA_U8, 1),
[FOU_ATTR_TYPE] = { .type = NLA_U8, },
[FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, },
[FOU_ATTR_LOCAL_V4] = { .type = NLA_U32, },
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index fdda18b1abda..942a948f1a31 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -107,7 +107,8 @@ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
struct tcphdr *th = tcp_hdr(skb);
- if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
+ if ((skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size) &&
+ !(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY))
return __tcp4_gso_segment_list(skb, features);
skb->ip_summed = CHECKSUM_NONE;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 19d0b5b09ffa..589456bd8b5f 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -514,7 +514,8 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST) {
/* Detect modified geometry and pass those to skb_segment. */
- if (skb_pagelen(gso_skb) - sizeof(*uh) == skb_shinfo(gso_skb)->gso_size)
+ if ((skb_pagelen(gso_skb) - sizeof(*uh) == skb_shinfo(gso_skb)->gso_size) &&
+ !(skb_shinfo(gso_skb)->gso_type & SKB_GSO_DODGY))
return __udp_gso_segment_list(gso_skb, features, is_ipv6);
ret = __skb_linearize(gso_skb);
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 5d2f90babaa5..9d37e7711bc2 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -965,7 +965,9 @@ static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
fl6.daddr = ipv6_hdr(skb)->saddr;
if (saddr)
fl6.saddr = *saddr;
- fl6.flowi6_oif = icmp6_iif(skb);
+ fl6.flowi6_oif = ipv6_addr_loopback(&fl6.daddr) ?
+ skb->dev->ifindex :
+ icmp6_iif(skb);
fl6.fl6_icmp_type = type;
fl6.flowi6_mark = mark;
fl6.flowi6_uid = sock_net_uid(net, NULL);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 59d17b6f06bf..f6a5d8c73af9 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1555,8 +1555,8 @@ skip_routeinfo:
memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
mtu = ntohl(n);
- if (in6_dev->ra_mtu != mtu) {
- in6_dev->ra_mtu = mtu;
+ if (READ_ONCE(in6_dev->ra_mtu) != mtu) {
+ WRITE_ONCE(in6_dev->ra_mtu, mtu);
send_ifinfo_notify = true;
}
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index effeba58630b..5670d32c27f8 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -170,7 +170,8 @@ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
struct tcphdr *th = tcp_hdr(skb);
- if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
+ if ((skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size) &&
+ !(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY))
return __tcp6_gso_segment_list(skb, features);
skb->ip_summed = CHECKSUM_NONE;
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 687c1366a4d0..f9b0f666600f 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1086,8 +1086,10 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
tunnel = session->tunnel;
/* Check protocol version */
- if (version != tunnel->version)
+ if (version != tunnel->version) {
+ l2tp_session_put(session);
goto invalid;
+ }
if (version == L2TP_HDR_VER_3 &&
l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) {
@@ -1414,8 +1416,6 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
{
struct l2tp_tunnel *tunnel = container_of(work, struct l2tp_tunnel,
del_work);
- struct sock *sk = tunnel->sock;
- struct socket *sock = sk->sk_socket;
l2tp_tunnel_closeall(tunnel);
@@ -1423,6 +1423,8 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
* the sk API to release it here.
*/
if (tunnel->fd < 0) {
+ struct socket *sock = tunnel->sock->sk_socket;
+
if (sock) {
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9d9313eee59f..bd573f8e61fb 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -451,8 +451,6 @@ struct ieee80211_mgd_assoc_data {
struct ieee80211_conn_settings conn;
u16 status;
-
- bool disabled;
} link[IEEE80211_MLD_MAX_NUM_LINKS];
u8 ap_addr[ETH_ALEN] __aligned(2);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7b0aa24c1f97..515384ca2f8f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -350,6 +350,8 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
/* we hold the RTNL here so can safely walk the list */
list_for_each_entry(nsdata, &local->interfaces, list) {
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
+ struct ieee80211_link_data *link;
+
/*
* Only OCB and monitor mode may coexist
*/
@@ -376,8 +378,10 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
* will not add another interface while any channel
* switch is active.
*/
- if (nsdata->vif.bss_conf.csa_active)
- return -EBUSY;
+ for_each_link_data(nsdata, link) {
+ if (link->conf->csa_active)
+ return -EBUSY;
+ }
/*
* The remaining checks are only performed for interfaces
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index d5da7ccea66e..04c8809173d7 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -987,7 +987,8 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata)
if (ieee80211_sdata_running(sdata)) {
list_for_each_entry(key, &sdata->key_list, list) {
- increment_tailroom_need_count(sdata);
+ if (!(key->flags & KEY_FLAG_TAINTED))
+ increment_tailroom_need_count(sdata);
ieee80211_key_enable_hw_accel(key);
}
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index ad53dedd929c..73f57b9e0ebf 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -8,7 +8,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*/
#include <linux/delay.h>
@@ -6161,6 +6161,100 @@ static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,
return true;
}
+static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
+{
+ if (bm_size == 1)
+ return *data;
+
+ return get_unaligned_le16(data);
+}
+
+static int
+ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_ttlm_elem *ttlm,
+ struct ieee80211_adv_ttlm_info *ttlm_info)
+{
+ /* The element size was already validated in
+ * ieee80211_tid_to_link_map_size_ok()
+ */
+ u8 control, link_map_presence, map_size, tid;
+ u8 *pos;
+
+ memset(ttlm_info, 0, sizeof(*ttlm_info));
+ pos = (void *)ttlm->optional;
+ control = ttlm->control;
+
+ if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) !=
+ IEEE80211_TTLM_DIRECTION_BOTH) {
+ sdata_info(sdata, "Invalid advertised T2L map direction\n");
+ return -EINVAL;
+ }
+
+ if (!(control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP)) {
+ link_map_presence = *pos;
+ pos++;
+ }
+
+ if (control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) {
+ ttlm_info->switch_time = get_unaligned_le16(pos);
+
+ /* Since ttlm_info->switch_time == 0 means no switch time, bump
+ * it by 1.
+ */
+ if (!ttlm_info->switch_time)
+ ttlm_info->switch_time = 1;
+
+ pos += 2;
+ }
+
+ if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) {
+ ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16;
+ pos += 3;
+ }
+
+ if (control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) {
+ ttlm_info->map = 0xffff;
+ return 0;
+ }
+
+ if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
+ map_size = 1;
+ else
+ map_size = 2;
+
+ /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall
+ * not advertise a TID-to-link mapping that does not map all TIDs to the
+ * same link set, reject frame if not all links have mapping
+ */
+ if (link_map_presence != 0xff) {
+ sdata_info(sdata,
+ "Invalid advertised T2L mapping presence indicator\n");
+ return -EINVAL;
+ }
+
+ ttlm_info->map = ieee80211_get_ttlm(map_size, pos);
+ if (!ttlm_info->map) {
+ sdata_info(sdata,
+ "Invalid advertised T2L map for TID 0\n");
+ return -EINVAL;
+ }
+
+ pos += map_size;
+
+ for (tid = 1; tid < 8; tid++) {
+ u16 map = ieee80211_get_ttlm(map_size, pos);
+
+ if (map != ttlm_info->map) {
+ sdata_info(sdata, "Invalid advertised T2L map for tid %d\n",
+ tid);
+ return -EINVAL;
+ }
+
+ pos += map_size;
+ }
+ return 0;
+}
+
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
struct ieee802_11_elems *elems,
@@ -6192,8 +6286,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
continue;
valid_links |= BIT(link_id);
- if (assoc_data->link[link_id].disabled)
- dormant_links |= BIT(link_id);
if (link_id != assoc_data->assoc_link_id) {
err = ieee80211_sta_allocate_link(sta, link_id);
@@ -6202,6 +6294,33 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
}
}
+ /*
+ * We do not support setting a negotiated TTLM during
+ * association. As such, we can assume that if there is a TTLM,
+ * then it is the currently active advertised TTLM.
+ * In that case, there must be exactly one TTLM that does not
+ * have a switch time set. This mapping should also leave us
+ * with at least one usable link.
+ */
+ if (elems->ttlm_num > 1) {
+ sdata_info(sdata,
+ "More than one advertised TTLM in association response\n");
+ goto out_err;
+ } else if (elems->ttlm_num == 1) {
+ if (ieee80211_parse_adv_t2l(sdata, elems->ttlm[0],
+ &sdata->u.mgd.ttlm_info) ||
+ sdata->u.mgd.ttlm_info.switch_time != 0 ||
+ !(valid_links & sdata->u.mgd.ttlm_info.map)) {
+ sdata_info(sdata,
+ "Invalid advertised TTLM in association response\n");
+ goto out_err;
+ }
+
+ sdata->u.mgd.ttlm_info.active = true;
+ dormant_links =
+ valid_links & ~sdata->u.mgd.ttlm_info.map;
+ }
+
ieee80211_vif_set_links(sdata, valid_links, dormant_links);
}
@@ -6992,95 +7111,6 @@ static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
sdata->u.mgd.ttlm_info.switch_time = 0;
}
-static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
-{
- if (bm_size == 1)
- return *data;
- else
- return get_unaligned_le16(data);
-}
-
-static int
-ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
- const struct ieee80211_ttlm_elem *ttlm,
- struct ieee80211_adv_ttlm_info *ttlm_info)
-{
- /* The element size was already validated in
- * ieee80211_tid_to_link_map_size_ok()
- */
- u8 control, link_map_presence, map_size, tid;
- u8 *pos;
-
- memset(ttlm_info, 0, sizeof(*ttlm_info));
- pos = (void *)ttlm->optional;
- control = ttlm->control;
-
- if ((control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) ||
- !(control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT))
- return 0;
-
- if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) !=
- IEEE80211_TTLM_DIRECTION_BOTH) {
- sdata_info(sdata, "Invalid advertised T2L map direction\n");
- return -EINVAL;
- }
-
- link_map_presence = *pos;
- pos++;
-
- ttlm_info->switch_time = get_unaligned_le16(pos);
-
- /* Since ttlm_info->switch_time == 0 means no switch time, bump it
- * by 1.
- */
- if (!ttlm_info->switch_time)
- ttlm_info->switch_time = 1;
-
- pos += 2;
-
- if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) {
- ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16;
- pos += 3;
- }
-
- if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
- map_size = 1;
- else
- map_size = 2;
-
- /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall
- * not advertise a TID-to-link mapping that does not map all TIDs to the
- * same link set, reject frame if not all links have mapping
- */
- if (link_map_presence != 0xff) {
- sdata_info(sdata,
- "Invalid advertised T2L mapping presence indicator\n");
- return -EINVAL;
- }
-
- ttlm_info->map = ieee80211_get_ttlm(map_size, pos);
- if (!ttlm_info->map) {
- sdata_info(sdata,
- "Invalid advertised T2L map for TID 0\n");
- return -EINVAL;
- }
-
- pos += map_size;
-
- for (tid = 1; tid < 8; tid++) {
- u16 map = ieee80211_get_ttlm(map_size, pos);
-
- if (map != ttlm_info->map) {
- sdata_info(sdata, "Invalid advertised T2L map for tid %d\n",
- tid);
- return -EINVAL;
- }
-
- pos += map_size;
- }
- return 0;
-}
-
static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
u64 beacon_ts)
@@ -9737,7 +9767,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
req, true, i,
&assoc_data->link[i].conn);
assoc_data->link[i].bss = link_cbss;
- assoc_data->link[i].disabled = req->links[i].disabled;
if (!bss->uapsd_supported)
uapsd_supported = false;
@@ -10719,8 +10748,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata,
&data->link[link_id].conn);
data->link[link_id].bss = link_cbss;
- data->link[link_id].disabled =
- req->add_links[link_id].disabled;
data->link[link_id].elems =
(u8 *)req->add_links[link_id].elems;
data->link[link_id].elems_len =
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 5ef315ed3b0f..4823c8d45639 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -347,8 +347,13 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
mgmt->da))
return;
} else {
- /* Beacons are expected only with broadcast address */
- if (!is_broadcast_ether_addr(mgmt->da))
+ /*
+ * Non-S1G beacons are expected only with broadcast address.
+ * S1G beacons only carry the SA so no DA check is required
+ * nor possible.
+ */
+ if (!ieee80211_is_s1g_beacon(mgmt->frame_control) &&
+ !is_broadcast_ether_addr(mgmt->da))
return;
}
diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
index 57570a44e418..b26675054b0d 100644
--- a/net/mptcp/pm_kernel.c
+++ b/net/mptcp/pm_kernel.c
@@ -1294,16 +1294,26 @@ static void __reset_counters(struct pm_nl_pernet *pernet)
int mptcp_pm_nl_flush_addrs_doit(struct sk_buff *skb, struct genl_info *info)
{
struct pm_nl_pernet *pernet = genl_info_pm_nl(info);
- LIST_HEAD(free_list);
+ struct list_head free_list;
spin_lock_bh(&pernet->lock);
- list_splice_init(&pernet->endp_list, &free_list);
+ free_list = pernet->endp_list;
+ INIT_LIST_HEAD_RCU(&pernet->endp_list);
__reset_counters(pernet);
pernet->next_id = 1;
bitmap_zero(pernet->id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1);
spin_unlock_bh(&pernet->lock);
- mptcp_nl_flush_addrs_list(sock_net(skb->sk), &free_list);
+
+ if (free_list.next == &pernet->endp_list)
+ return 0;
+
synchronize_rcu();
+
+ /* Adjust the pointers to free_list instead of pernet->endp_list */
+ free_list.prev->next = &free_list;
+ free_list.next->prev = &free_list;
+
+ mptcp_nl_flush_addrs_list(sock_net(skb->sk), &free_list);
__flush_addrs(&free_list);
return 0;
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f505b780f713..8d3233667418 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -821,11 +821,8 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
static bool __mptcp_subflow_error_report(struct sock *sk, struct sock *ssk)
{
- int err = sock_error(ssk);
int ssk_state;
-
- if (!err)
- return false;
+ int err;
/* only propagate errors on fallen-back sockets or
* on MPC connect
@@ -833,6 +830,10 @@ static bool __mptcp_subflow_error_report(struct sock *sk, struct sock *ssk)
if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(mptcp_sk(sk)))
return false;
+ err = sock_error(ssk);
+ if (!err)
+ return false;
+
/* We need to propagate only transition to CLOSE state.
* Orphaned socket will see such state change via
* subflow_sched_work_if_closed() and that path will properly
@@ -2598,8 +2599,8 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
struct mptcp_sock *msk = mptcp_sk(sk);
struct sk_buff *skb;
- /* The first subflow can already be closed and still in the list */
- if (subflow->close_event_done)
+ /* The first subflow can already be closed or disconnected */
+ if (subflow->close_event_done || READ_ONCE(subflow->local_id) < 0)
return;
subflow->close_event_done = true;
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index b94cb2ffbaf8..9cc29ae85b06 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -752,7 +752,7 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
unsigned char *dptr;
ax25_cb *ax25s;
int ret;
- struct sk_buff *skbn;
+ struct sk_buff *nskb, *oskb;
/*
* Reject malformed packets early. Check that it contains at least 2
@@ -811,14 +811,16 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
/* We are going to change the netrom headers so we should get our
own skb, we also did not know until now how much header space
we had to reserve... - RXQ */
- if ((skbn=skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC)) == NULL) {
+ nskb = skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC);
+
+ if (!nskb) {
nr_node_unlock(nr_node);
nr_node_put(nr_node);
dev_put(dev);
return 0;
}
- kfree_skb(skb);
- skb=skbn;
+ oskb = skb;
+ skb = nskb;
skb->data[14]--;
dptr = skb_push(skb, 1);
@@ -837,6 +839,9 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nr_node_unlock(nr_node);
nr_node_put(nr_node);
+ if (ret)
+ kfree_skb(oskb);
+
return ret;
}
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 82f023f37754..f50e5bab35d8 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -1147,14 +1147,14 @@ int nfc_register_device(struct nfc_dev *dev)
EXPORT_SYMBOL(nfc_register_device);
/**
- * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ * nfc_unregister_rfkill - unregister a nfc device in the rfkill subsystem
*
* @dev: The nfc device to unregister
*/
-void nfc_unregister_device(struct nfc_dev *dev)
+void nfc_unregister_rfkill(struct nfc_dev *dev)
{
- int rc;
struct rfkill *rfk = NULL;
+ int rc;
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
@@ -1175,7 +1175,16 @@ void nfc_unregister_device(struct nfc_dev *dev)
rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
+}
+EXPORT_SYMBOL(nfc_unregister_rfkill);
+/**
+ * nfc_remove_device - remove a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to remove
+ */
+void nfc_remove_device(struct nfc_dev *dev)
+{
if (dev->ops->check_presence) {
timer_delete_sync(&dev->check_pres_timer);
cancel_work_sync(&dev->check_pres_work);
@@ -1188,6 +1197,18 @@ void nfc_unregister_device(struct nfc_dev *dev)
device_del(&dev->dev);
mutex_unlock(&nfc_devlist_mutex);
}
+EXPORT_SYMBOL(nfc_remove_device);
+
+/**
+ * nfc_unregister_device - unregister a nfc device in the nfc subsystem
+ *
+ * @dev: The nfc device to unregister
+ */
+void nfc_unregister_device(struct nfc_dev *dev)
+{
+ nfc_unregister_rfkill(dev);
+ nfc_remove_device(dev);
+}
EXPORT_SYMBOL(nfc_unregister_device);
static int __init nfc_init(void)
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index e2680a3bef79..b652323bc2c1 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -778,8 +778,23 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
if (likely(frag_len > 0))
skb_put_data(pdu, msg_ptr, frag_len);
+ spin_lock(&local->tx_queue.lock);
+
+ if (list_empty(&local->list)) {
+ spin_unlock(&local->tx_queue.lock);
+
+ kfree_skb(pdu);
+
+ len -= remaining_len;
+ if (len == 0)
+ len = -ENXIO;
+ break;
+ }
+
/* No need to check for the peer RW for UI frames */
- skb_queue_tail(&local->tx_queue, pdu);
+ __skb_queue_tail(&local->tx_queue, pdu);
+
+ spin_unlock(&local->tx_queue.lock);
remaining_len -= frag_len;
msg_ptr += frag_len;
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index beeb3b4d28ca..444a3774c8e8 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -316,7 +316,9 @@ static struct nfc_llcp_local *nfc_llcp_remove_local(struct nfc_dev *dev)
spin_lock(&llcp_devices_lock);
list_for_each_entry_safe(local, tmp, &llcp_devices, list)
if (local->dev == dev) {
- list_del(&local->list);
+ spin_lock(&local->tx_queue.lock);
+ list_del_init(&local->list);
+ spin_unlock(&local->tx_queue.lock);
spin_unlock(&llcp_devices_lock);
return local;
}
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index fc921cd2cdff..e419e020a70a 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -1303,6 +1303,8 @@ void nci_unregister_device(struct nci_dev *ndev)
{
struct nci_conn_info *conn_info, *n;
+ nfc_unregister_rfkill(ndev->nfc_dev);
+
/* This set_bit is not protected with specialized barrier,
* However, it is fine because the mutex_lock(&ndev->req_lock);
* in nci_close_device() will help to emit one.
@@ -1320,7 +1322,7 @@ void nci_unregister_device(struct nci_dev *ndev)
/* conn_info is allocated with devm_kzalloc */
}
- nfc_unregister_device(ndev->nfc_dev);
+ nfc_remove_device(ndev->nfc_dev);
}
EXPORT_SYMBOL(nci_unregister_device);
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 6bbbc16ab778..f0ce8ce1dce0 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -310,22 +310,23 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
*/
int ovs_vport_get_upcall_stats(struct vport *vport, struct sk_buff *skb)
{
+ u64 tx_success = 0, tx_fail = 0;
struct nlattr *nla;
int i;
- __u64 tx_success = 0;
- __u64 tx_fail = 0;
-
for_each_possible_cpu(i) {
const struct vport_upcall_stats_percpu *stats;
+ u64 n_success, n_fail;
unsigned int start;
stats = per_cpu_ptr(vport->upcall_stats, i);
do {
start = u64_stats_fetch_begin(&stats->syncp);
- tx_success += u64_stats_read(&stats->n_success);
- tx_fail += u64_stats_read(&stats->n_fail);
+ n_success = u64_stats_read(&stats->n_success);
+ n_fail = u64_stats_read(&stats->n_fail);
} while (u64_stats_fetch_retry(&stats->syncp, start));
+ tx_success += n_success;
+ tx_fail += n_fail;
}
nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_UPCALL_STATS);
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 5b7342d43486..36d6ca0d1089 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -387,7 +387,7 @@ struct rxrpc_peer {
struct rb_root service_conns; /* Service connections */
struct list_head keepalive_link; /* Link in net->peer_keepalive[] */
unsigned long app_data; /* Application data (e.g. afs_server) */
- time64_t last_tx_at; /* Last time packet sent here */
+ unsigned int last_tx_at; /* Last time packet sent here (time64_t LSW) */
seqlock_t service_conn_lock;
spinlock_t lock; /* access lock */
int debug_id; /* debug ID for printks */
@@ -1379,6 +1379,13 @@ void rxrpc_peer_keepalive_worker(struct work_struct *);
void rxrpc_input_probe_for_pmtud(struct rxrpc_connection *conn, rxrpc_serial_t acked_serial,
bool sendmsg_fail);
+/* Update the last transmission time on a peer for keepalive purposes. */
+static inline void rxrpc_peer_mark_tx(struct rxrpc_peer *peer)
+{
+ /* To avoid tearing on 32-bit systems, we only keep the LSW. */
+ WRITE_ONCE(peer->last_tx_at, ktime_get_seconds());
+}
+
/*
* peer_object.c
*/
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 232b6986da83..98ad9b51ca2c 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -194,7 +194,7 @@ void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
}
ret = kernel_sendmsg(conn->local->socket, &msg, iov, ioc, len);
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
if (ret < 0)
trace_rxrpc_tx_fail(chan->call_debug_id, serial, ret,
rxrpc_tx_point_call_final_resend);
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 8b5903b6e481..d70db367e358 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -275,7 +275,7 @@ static void rxrpc_send_ack_packet(struct rxrpc_call *call, int nr_kv, size_t len
rxrpc_local_dont_fragment(conn->local, why == rxrpc_propose_ack_ping_for_mtu_probe);
ret = do_udp_sendmsg(conn->local->socket, &msg, len);
- call->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(call->peer);
if (ret < 0) {
trace_rxrpc_tx_fail(call->debug_id, serial, ret,
rxrpc_tx_point_call_ack);
@@ -411,7 +411,7 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call)
iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, sizeof(pkt));
ret = do_udp_sendmsg(conn->local->socket, &msg, sizeof(pkt));
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
if (ret < 0)
trace_rxrpc_tx_fail(call->debug_id, serial, ret,
rxrpc_tx_point_call_abort);
@@ -698,7 +698,7 @@ void rxrpc_send_data_packet(struct rxrpc_call *call, struct rxrpc_send_data_req
ret = 0;
trace_rxrpc_tx_data(call, txb->seq, txb->serial, txb->flags,
rxrpc_txdata_inject_loss);
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
goto done;
}
}
@@ -711,7 +711,7 @@ void rxrpc_send_data_packet(struct rxrpc_call *call, struct rxrpc_send_data_req
*/
rxrpc_inc_stat(call->rxnet, stat_tx_data_send);
ret = do_udp_sendmsg(conn->local->socket, &msg, len);
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
if (ret == -EMSGSIZE) {
rxrpc_inc_stat(call->rxnet, stat_tx_data_send_msgsize);
@@ -797,7 +797,7 @@ void rxrpc_send_conn_abort(struct rxrpc_connection *conn)
trace_rxrpc_tx_packet(conn->debug_id, &whdr, rxrpc_tx_point_conn_abort);
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
}
/*
@@ -917,7 +917,7 @@ void rxrpc_send_keepalive(struct rxrpc_peer *peer)
trace_rxrpc_tx_packet(peer->debug_id, &whdr,
rxrpc_tx_point_version_keepalive);
- peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(peer);
_leave("");
}
@@ -973,7 +973,7 @@ void rxrpc_send_response(struct rxrpc_connection *conn, struct sk_buff *response
if (ret < 0)
goto fail;
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
return;
fail:
diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c
index 7f4729234957..9d02448ac062 100644
--- a/net/rxrpc/peer_event.c
+++ b/net/rxrpc/peer_event.c
@@ -238,6 +238,21 @@ static void rxrpc_distribute_error(struct rxrpc_peer *peer, struct sk_buff *skb,
}
/*
+ * Reconstruct the last transmission time. The difference calculated should be
+ * valid provided no more than ~68 years elapsed since the last transmission.
+ */
+static time64_t rxrpc_peer_get_tx_mark(const struct rxrpc_peer *peer, time64_t base)
+{
+ s32 last_tx_at = READ_ONCE(peer->last_tx_at);
+ s32 base_lsw = base;
+ s32 diff = last_tx_at - base_lsw;
+
+ diff = clamp(diff, -RXRPC_KEEPALIVE_TIME, RXRPC_KEEPALIVE_TIME);
+
+ return diff + base;
+}
+
+/*
* Perform keep-alive pings.
*/
static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
@@ -265,7 +280,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
spin_unlock_bh(&rxnet->peer_hash_lock);
if (use) {
- keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME;
+ keepalive_at = rxrpc_peer_get_tx_mark(peer, base) + RXRPC_KEEPALIVE_TIME;
slot = keepalive_at - base;
_debug("%02x peer %u t=%d {%pISp}",
cursor, peer->debug_id, slot, &peer->srx.transport);
diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c
index d803562ca0ac..59292f7f9205 100644
--- a/net/rxrpc/proc.c
+++ b/net/rxrpc/proc.c
@@ -296,13 +296,13 @@ static int rxrpc_peer_seq_show(struct seq_file *seq, void *v)
now = ktime_get_seconds();
seq_printf(seq,
- "UDP %-47.47s %-47.47s %3u %4u %5u %6llus %8d %8d\n",
+ "UDP %-47.47s %-47.47s %3u %4u %5u %6ds %8d %8d\n",
lbuff,
rbuff,
refcount_read(&peer->ref),
peer->cong_ssthresh,
peer->max_data,
- now - peer->last_tx_at,
+ (s32)now - (s32)READ_ONCE(peer->last_tx_at),
READ_ONCE(peer->recent_srtt_us),
READ_ONCE(peer->recent_rto_us));
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index 7fa7e77f6bb9..e1f7513a46db 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -518,7 +518,8 @@ try_again:
if (rxrpc_call_has_failed(call))
goto call_failed;
- if (!skb_queue_empty(&call->recvmsg_queue))
+ if (!(flags & MSG_PEEK) &&
+ !skb_queue_empty(&call->recvmsg_queue))
rxrpc_notify_socket(call);
goto not_yet_complete;
@@ -549,11 +550,21 @@ error_unlock_call:
error_requeue_call:
if (!(flags & MSG_PEEK)) {
spin_lock_irq(&rx->recvmsg_lock);
- list_add(&call->recvmsg_link, &rx->recvmsg_q);
- spin_unlock_irq(&rx->recvmsg_lock);
+ if (list_empty(&call->recvmsg_link)) {
+ list_add(&call->recvmsg_link, &rx->recvmsg_q);
+ rxrpc_see_call(call, rxrpc_call_see_recvmsg_requeue);
+ spin_unlock_irq(&rx->recvmsg_lock);
+ } else if (list_is_first(&call->recvmsg_link, &rx->recvmsg_q)) {
+ spin_unlock_irq(&rx->recvmsg_lock);
+ rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_first);
+ } else {
+ list_move(&call->recvmsg_link, &rx->recvmsg_q);
+ spin_unlock_irq(&rx->recvmsg_lock);
+ rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_move);
+ }
trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_requeue, 0);
} else {
- rxrpc_put_call(call, rxrpc_call_put_recvmsg);
+ rxrpc_put_call(call, rxrpc_call_put_recvmsg_peek_nowait);
}
error_no_call:
release_sock(&rx->sk);
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index dce5a3d8a964..43cbf9efd89f 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -678,7 +678,7 @@ static int rxgk_issue_challenge(struct rxrpc_connection *conn)
ret = do_udp_sendmsg(conn->local->socket, &msg, len);
if (ret > 0)
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
__free_page(page);
if (ret < 0) {
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index 3657c0661cdc..a756855a0a62 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -694,7 +694,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
return -EAGAIN;
}
- conn->peer->last_tx_at = ktime_get_seconds();
+ rxrpc_peer_mark_tx(conn->peer);
trace_rxrpc_tx_packet(conn->debug_id, &whdr,
rxrpc_tx_point_rxkad_challenge);
_leave(" = 0");
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 1dfdda6c2d4c..8e8f6af731d5 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -821,6 +821,7 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
/* could be stupid policy setup or mtu config
* so lets be conservative.. */
if ((action == TC_ACT_SHOT) || exceed_mtu) {
+drop:
qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats));
return TC_ACT_SHOT;
}
@@ -829,6 +830,8 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
skb_push(skb, skb->dev->hard_header_len);
ife_meta = ife_encode(skb, metalen);
+ if (!ife_meta)
+ goto drop;
spin_lock(&ife->tcf_lock);
@@ -844,8 +847,7 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
if (err < 0) {
/* too corrupt to keep around if overwritten */
spin_unlock(&ife->tcf_lock);
- qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats));
- return TC_ACT_SHOT;
+ goto drop;
}
skboff += err;
}
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 9d59090bbe93..e7778413e72f 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -373,7 +373,7 @@ static void qfq_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl)
/* Deschedule class and remove it from its parent aggregate. */
static void qfq_deact_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl)
{
- if (cl->qdisc->q.qlen > 0) /* class is active */
+ if (cl_is_active(cl)) /* class is active */
qfq_deactivate_class(q, cl);
qfq_rm_from_agg(q, cl);
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 8badec6d82a2..6e4bdaa876ed 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -178,6 +178,11 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
if (m->dev == dev)
return -ELOOP;
+ if (sch->parent != TC_H_ROOT) {
+ NL_SET_ERR_MSG_MOD(extack, "teql can only be used as root");
+ return -EOPNOTSUPP;
+ }
+
q->m = m;
skb_queue_head_init(&q->q);
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 3755ba079d07..7b823d759141 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -603,6 +603,11 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net,
sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT,
SCTP_PEER_INIT(initchunk));
+ /* SCTP-AUTH: generate the association shared keys so that
+ * we can potentially sign the COOKIE-ECHO.
+ */
+ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_SHKEY, SCTP_NULL());
+
/* Reset init error count upon receipt of INIT-ACK. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());
@@ -617,11 +622,6 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_COOKIE_ECHOED));
- /* SCTP-AUTH: generate the association shared keys so that
- * we can potentially sign the COOKIE-ECHO.
- */
- sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_SHKEY, SCTP_NULL());
-
/* 5.1 C) "A" shall then send the State Cookie received in the
* INIT ACK chunk in a COOKIE ECHO chunk, ...
*/
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index dcc8a1d5851e..d3e26025ef58 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -28,6 +28,7 @@
static void virtio_transport_cancel_close_work(struct vsock_sock *vsk,
bool cancel_timeout);
+static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs);
static const struct virtio_transport *
virtio_transport_get_ops(struct vsock_sock *vsk)
@@ -499,9 +500,7 @@ u32 virtio_transport_get_credit(struct virtio_vsock_sock *vvs, u32 credit)
return 0;
spin_lock_bh(&vvs->tx_lock);
- ret = vvs->peer_buf_alloc - (vvs->tx_cnt - vvs->peer_fwd_cnt);
- if (ret > credit)
- ret = credit;
+ ret = min_t(u32, credit, virtio_transport_has_space(vvs));
vvs->tx_cnt += ret;
vvs->bytes_unsent += ret;
spin_unlock_bh(&vvs->tx_lock);
@@ -822,6 +821,15 @@ virtio_transport_seqpacket_dequeue(struct vsock_sock *vsk,
}
EXPORT_SYMBOL_GPL(virtio_transport_seqpacket_dequeue);
+static u32 virtio_transport_tx_buf_size(struct virtio_vsock_sock *vvs)
+{
+ /* The peer advertises its receive buffer via peer_buf_alloc, but we
+ * cap it to our local buf_alloc so a remote peer cannot force us to
+ * queue more data than our own buffer configuration allows.
+ */
+ return min(vvs->peer_buf_alloc, vvs->buf_alloc);
+}
+
int
virtio_transport_seqpacket_enqueue(struct vsock_sock *vsk,
struct msghdr *msg,
@@ -831,7 +839,7 @@ virtio_transport_seqpacket_enqueue(struct vsock_sock *vsk,
spin_lock_bh(&vvs->tx_lock);
- if (len > vvs->peer_buf_alloc) {
+ if (len > virtio_transport_tx_buf_size(vvs)) {
spin_unlock_bh(&vvs->tx_lock);
return -EMSGSIZE;
}
@@ -877,12 +885,16 @@ u32 virtio_transport_seqpacket_has_data(struct vsock_sock *vsk)
}
EXPORT_SYMBOL_GPL(virtio_transport_seqpacket_has_data);
-static s64 virtio_transport_has_space(struct vsock_sock *vsk)
+static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs)
{
- struct virtio_vsock_sock *vvs = vsk->trans;
s64 bytes;
- bytes = (s64)vvs->peer_buf_alloc - (vvs->tx_cnt - vvs->peer_fwd_cnt);
+ /* Use s64 arithmetic so if the peer shrinks peer_buf_alloc while
+ * we have bytes in flight (tx_cnt - peer_fwd_cnt), the subtraction
+ * does not underflow.
+ */
+ bytes = (s64)virtio_transport_tx_buf_size(vvs) -
+ (vvs->tx_cnt - vvs->peer_fwd_cnt);
if (bytes < 0)
bytes = 0;
@@ -895,7 +907,7 @@ s64 virtio_transport_stream_has_space(struct vsock_sock *vsk)
s64 bytes;
spin_lock_bh(&vvs->tx_lock);
- bytes = virtio_transport_has_space(vsk);
+ bytes = virtio_transport_has_space(vvs);
spin_unlock_bh(&vvs->tx_lock);
return bytes;
@@ -1359,9 +1371,11 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk,
/* Try to copy small packets into the buffer of last packet queued,
* to avoid wasting memory queueing the entire buffer with a small
- * payload.
+ * payload. Skip non-linear (e.g. zerocopy) skbs; these carry payload
+ * in skb_shinfo.
*/
- if (len <= GOOD_COPY_LEN && !skb_queue_empty(&vvs->rx_queue)) {
+ if (len <= GOOD_COPY_LEN && !skb_queue_empty(&vvs->rx_queue) &&
+ !skb_is_nonlinear(skb)) {
struct virtio_vsock_hdr *last_hdr;
struct sk_buff *last_skb;
@@ -1490,7 +1504,7 @@ static bool virtio_transport_space_update(struct sock *sk,
spin_lock_bh(&vvs->tx_lock);
vvs->peer_buf_alloc = le32_to_cpu(hdr->buf_alloc);
vvs->peer_fwd_cnt = le32_to_cpu(hdr->fwd_cnt);
- space_available = virtio_transport_has_space(vsk);
+ space_available = virtio_transport_has_space(vvs);
spin_unlock_bh(&vvs->tx_lock);
return space_available;
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c961cd42a832..03efd45c007f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -12241,9 +12241,6 @@ static int nl80211_process_links(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
}
-
- links[link_id].disabled =
- nla_get_flag(attrs[NL80211_ATTR_MLO_LINK_DISABLED]);
}
return 0;
@@ -12423,13 +12420,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
goto free;
}
- if (req.links[req.link_id].disabled) {
- GENL_SET_ERR_MSG(info,
- "cannot have assoc link disabled");
- err = -EINVAL;
- goto free;
- }
-
if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS])
req.ext_mld_capa_ops =
nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]);
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 27e8a2f52f04..4f581aed45b7 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1561,12 +1561,14 @@ static u32 cfg80211_calculate_bitrate_he(struct rate_info *rate)
tmp = result;
tmp *= SCALE;
do_div(tmp, mcs_divisors[rate->mcs]);
- result = tmp;
/* and take NSS, DCM into account */
- result = (result * rate->nss) / 8;
+ tmp *= rate->nss;
+ do_div(tmp, 8);
if (rate->he_dcm)
- result /= 2;
+ do_div(tmp, 2);
+
+ result = tmp;
return result / 10000;
}
diff --git a/rust/Makefile b/rust/Makefile
index 5d357dce1704..4dcc2eff51cb 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -383,6 +383,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \
-fno-inline-functions-called-once -fsanitize=bounds-strict \
-fstrict-flex-arrays=% -fmin-function-alignment=% \
-fzero-init-padding-bits=% -mno-fdpic \
+ -fdiagnostics-show-context -fdiagnostics-show-context=% \
--param=% --param asan-% -fno-isolate-erroneous-paths-dereference
# Derived from `scripts/Makefile.clang`.
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 56f3c180e8f6..be76f11aecb7 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -23,13 +23,22 @@ use core::{
/// An adapter for the registration of auxiliary drivers.
pub struct Adapter<T: Driver>(T);
-// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// SAFETY:
+// - `bindings::auxiliary_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct auxiliary_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+ type DriverType = bindings::auxiliary_driver;
+ type DriverData = T;
+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
// a preceding call to `register` has been successful.
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
- type RegType = bindings::auxiliary_driver;
-
unsafe fn register(
- adrv: &Opaque<Self::RegType>,
+ adrv: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
@@ -41,14 +50,14 @@ unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
(*adrv.get()).id_table = T::ID_TABLE.as_ptr();
}
- // SAFETY: `adrv` is guaranteed to be a valid `RegType`.
+ // SAFETY: `adrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr())
})
}
- unsafe fn unregister(adrv: &Opaque<Self::RegType>) {
- // SAFETY: `adrv` is guaranteed to be a valid `RegType`.
+ unsafe fn unregister(adrv: &Opaque<Self::DriverType>) {
+ // SAFETY: `adrv` is guaranteed to be a valid `DriverType`.
unsafe { bindings::auxiliary_driver_unregister(adrv.get()) }
}
}
@@ -87,7 +96,9 @@ impl<T: Driver + 'static> Adapter<T> {
// SAFETY: `remove_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
// and stored a `Pin<KBox<T>>`.
- drop(unsafe { adev.as_ref().drvdata_obtain::<T>() });
+ let data = unsafe { adev.as_ref().drvdata_borrow::<T>() };
+
+ T::unbind(adev, data);
}
}
@@ -187,6 +198,20 @@ pub trait Driver {
///
/// Called when an auxiliary device is matches a corresponding driver.
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
+
+ /// Auxiliary driver unbind.
+ ///
+ /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
+ /// is optional.
+ ///
+ /// This callback serves as a place for drivers to perform teardown operations that require a
+ /// `&Device<Core>` or `&Device<Bound>` reference. For instance, drivers may try to perform I/O
+ /// operations to gracefully tear down the device.
+ ///
+ /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
+ fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
+ let _ = (dev, this);
+ }
}
/// The auxiliary device representation.
diff --git a/rust/kernel/bits.rs b/rust/kernel/bits.rs
index 553d50265883..2daead125626 100644
--- a/rust/kernel/bits.rs
+++ b/rust/kernel/bits.rs
@@ -27,7 +27,8 @@ macro_rules! impl_bit_fn {
///
/// This version is the default and should be used if `n` is known at
/// compile time.
- #[inline]
+ // Always inline to optimize out error path of `build_assert`.
+ #[inline(always)]
pub const fn [<bit_ $ty>](n: u32) -> $ty {
build_assert!(n < <$ty>::BITS);
(1 as $ty) << n
@@ -75,7 +76,8 @@ macro_rules! impl_genmask_fn {
/// This version is the default and should be used if the range is known
/// at compile time.
$(#[$genmask_ex])*
- #[inline]
+ // Always inline to optimize out error path of `build_assert`.
+ #[inline(always)]
pub const fn [<genmask_ $ty>](range: RangeInclusive<u32>) -> $ty {
let start = *range.start();
let end = *range.end();
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 71b200df0f40..031720bf5d8c 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -232,30 +232,32 @@ impl Device<CoreInternal> {
///
/// # Safety
///
- /// - Must only be called once after a preceding call to [`Device::set_drvdata`].
/// - The type `T` must match the type of the `ForeignOwnable` previously stored by
/// [`Device::set_drvdata`].
- pub unsafe fn drvdata_obtain<T: 'static>(&self) -> Pin<KBox<T>> {
+ pub(crate) unsafe fn drvdata_obtain<T: 'static>(&self) -> Option<Pin<KBox<T>>> {
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) };
// SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
unsafe { bindings::dev_set_drvdata(self.as_raw(), core::ptr::null_mut()) };
+ if ptr.is_null() {
+ return None;
+ }
+
// SAFETY:
- // - By the safety requirements of this function, `ptr` comes from a previous call to
- // `into_foreign()`.
+ // - If `ptr` is not NULL, it comes from a previous call to `into_foreign()`.
// - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()`
// in `into_foreign()`.
- unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) }
+ Some(unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) })
}
/// Borrow the driver's private data bound to this [`Device`].
///
/// # Safety
///
- /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before
- /// [`Device::drvdata_obtain`].
+ /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before the
+ /// device is fully unbound.
/// - The type `T` must match the type of the `ForeignOwnable` previously stored by
/// [`Device::set_drvdata`].
pub unsafe fn drvdata_borrow<T: 'static>(&self) -> Pin<&T> {
@@ -271,7 +273,7 @@ impl Device<Bound> {
/// # Safety
///
/// - Must only be called after a preceding call to [`Device::set_drvdata`] and before
- /// [`Device::drvdata_obtain`].
+ /// the device is fully unbound.
/// - The type `T` must match the type of the `ForeignOwnable` previously stored by
/// [`Device::set_drvdata`].
unsafe fn drvdata_unchecked<T: 'static>(&self) -> Pin<&T> {
@@ -320,7 +322,7 @@ impl Device<Bound> {
// SAFETY:
// - The above check of `dev_get_drvdata()` guarantees that we are called after
- // `set_drvdata()` and before `drvdata_obtain()`.
+ // `set_drvdata()`.
// - We've just checked that the type of the driver's private data is in fact `T`.
Ok(unsafe { self.drvdata_unchecked() })
}
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 649d06468f41..bee3ae21a27b 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -99,23 +99,43 @@ use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule
use core::pin::Pin;
use pin_init::{pin_data, pinned_drop, PinInit};
+/// Trait describing the layout of a specific device driver.
+///
+/// This trait describes the layout of a specific driver structure, such as `struct pci_driver` or
+/// `struct platform_driver`.
+///
+/// # Safety
+///
+/// Implementors must guarantee that:
+/// - `DriverType` is `repr(C)`,
+/// - `DriverData` is the type of the driver's device private data.
+/// - `DriverType` embeds a valid `struct device_driver` at byte offset `DEVICE_DRIVER_OFFSET`.
+pub unsafe trait DriverLayout {
+ /// The specific driver type embedding a `struct device_driver`.
+ type DriverType: Default;
+
+ /// The type of the driver's device private data.
+ type DriverData;
+
+ /// Byte offset of the embedded `struct device_driver` within `DriverType`.
+ ///
+ /// This must correspond exactly to the location of the embedded `struct device_driver` field.
+ const DEVICE_DRIVER_OFFSET: usize;
+}
+
/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform,
/// Amba, etc.) to provide the corresponding subsystem specific implementation to register /
-/// unregister a driver of the particular type (`RegType`).
+/// unregister a driver of the particular type (`DriverType`).
///
-/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call
+/// For instance, the PCI subsystem would set `DriverType` to `bindings::pci_driver` and call
/// `bindings::__pci_register_driver` from `RegistrationOps::register` and
/// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`.
///
/// # Safety
///
-/// A call to [`RegistrationOps::unregister`] for a given instance of `RegType` is only valid if a
-/// preceding call to [`RegistrationOps::register`] has been successful.
-pub unsafe trait RegistrationOps {
- /// The type that holds information about the registration. This is typically a struct defined
- /// by the C portion of the kernel.
- type RegType: Default;
-
+/// A call to [`RegistrationOps::unregister`] for a given instance of `DriverType` is only valid if
+/// a preceding call to [`RegistrationOps::register`] has been successful.
+pub unsafe trait RegistrationOps: DriverLayout {
/// Registers a driver.
///
/// # Safety
@@ -123,7 +143,7 @@ pub unsafe trait RegistrationOps {
/// On success, `reg` must remain pinned and valid until the matching call to
/// [`RegistrationOps::unregister`].
unsafe fn register(
- reg: &Opaque<Self::RegType>,
+ reg: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result;
@@ -134,7 +154,7 @@ pub unsafe trait RegistrationOps {
///
/// Must only be called after a preceding successful call to [`RegistrationOps::register`] for
/// the same `reg`.
- unsafe fn unregister(reg: &Opaque<Self::RegType>);
+ unsafe fn unregister(reg: &Opaque<Self::DriverType>);
}
/// A [`Registration`] is a generic type that represents the registration of some driver type (e.g.
@@ -146,7 +166,7 @@ pub unsafe trait RegistrationOps {
#[pin_data(PinnedDrop)]
pub struct Registration<T: RegistrationOps> {
#[pin]
- reg: Opaque<T::RegType>,
+ reg: Opaque<T::DriverType>,
}
// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to
@@ -157,17 +177,51 @@ unsafe impl<T: RegistrationOps> Sync for Registration<T> {}
// any thread, so `Registration` is `Send`.
unsafe impl<T: RegistrationOps> Send for Registration<T> {}
-impl<T: RegistrationOps> Registration<T> {
+impl<T: RegistrationOps + 'static> Registration<T> {
+ extern "C" fn post_unbind_callback(dev: *mut bindings::device) {
+ // SAFETY: The driver core only ever calls the post unbind callback with a valid pointer to
+ // a `struct device`.
+ //
+ // INVARIANT: `dev` is valid for the duration of the `post_unbind_callback()`.
+ let dev = unsafe { &*dev.cast::<device::Device<device::CoreInternal>>() };
+
+ // `remove()` and all devres callbacks have been completed at this point, hence drop the
+ // driver's device private data.
+ //
+ // SAFETY: By the safety requirements of the `Driver` trait, `T::DriverData` is the
+ // driver's device private data type.
+ drop(unsafe { dev.drvdata_obtain::<T::DriverData>() });
+ }
+
+ /// Attach generic `struct device_driver` callbacks.
+ fn callbacks_attach(drv: &Opaque<T::DriverType>) {
+ let ptr = drv.get().cast::<u8>();
+
+ // SAFETY:
+ // - `drv.get()` yields a valid pointer to `Self::DriverType`.
+ // - Adding `DEVICE_DRIVER_OFFSET` yields the address of the embedded `struct device_driver`
+ // as guaranteed by the safety requirements of the `Driver` trait.
+ let base = unsafe { ptr.add(T::DEVICE_DRIVER_OFFSET) };
+
+ // CAST: `base` points to the offset of the embedded `struct device_driver`.
+ let base = base.cast::<bindings::device_driver>();
+
+ // SAFETY: It is safe to set the fields of `struct device_driver` on initialization.
+ unsafe { (*base).p_cb.post_unbind_rust = Some(Self::post_unbind_callback) };
+ }
+
/// Creates a new instance of the registration object.
pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
- reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| {
+ reg <- Opaque::try_ffi_init(|ptr: *mut T::DriverType| {
// SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write.
- unsafe { ptr.write(T::RegType::default()) };
+ unsafe { ptr.write(T::DriverType::default()) };
// SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write, and it has
// just been initialised above, so it's also valid for read.
- let drv = unsafe { &*(ptr as *const Opaque<T::RegType>) };
+ let drv = unsafe { &*(ptr as *const Opaque<T::DriverType>) };
+
+ Self::callbacks_attach(drv);
// SAFETY: `drv` is guaranteed to be pinned until `T::unregister`.
unsafe { T::register(drv, name, module) }
diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs
index 84d634201d90..1e8725eb44ed 100644
--- a/rust/kernel/fmt.rs
+++ b/rust/kernel/fmt.rs
@@ -6,7 +6,7 @@
pub use core::fmt::{Arguments, Debug, Error, Formatter, Result, Write};
-/// Internal adapter used to route allow implementations of formatting traits for foreign types.
+/// Internal adapter used to route and allow implementations of formatting traits for foreign types.
///
/// It is inserted automatically by the [`fmt!`] macro and is not meant to be used directly.
///
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 491e6cc25cf4..39b0a9a207fd 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -92,13 +92,22 @@ macro_rules! i2c_device_table {
/// An adapter for the registration of I2C drivers.
pub struct Adapter<T: Driver>(T);
-// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// SAFETY:
+// - `bindings::i2c_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct i2c_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+ type DriverType = bindings::i2c_driver;
+ type DriverData = T;
+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
// a preceding call to `register` has been successful.
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
- type RegType = bindings::i2c_driver;
-
unsafe fn register(
- idrv: &Opaque<Self::RegType>,
+ idrv: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
@@ -133,12 +142,12 @@ unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
(*idrv.get()).driver.acpi_match_table = acpi_table;
}
- // SAFETY: `idrv` is guaranteed to be a valid `RegType`.
+ // SAFETY: `idrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) })
}
- unsafe fn unregister(idrv: &Opaque<Self::RegType>) {
- // SAFETY: `idrv` is guaranteed to be a valid `RegType`.
+ unsafe fn unregister(idrv: &Opaque<Self::DriverType>) {
+ // SAFETY: `idrv` is guaranteed to be a valid `DriverType`.
unsafe { bindings::i2c_del_driver(idrv.get()) }
}
}
@@ -169,9 +178,9 @@ impl<T: Driver + 'static> Adapter<T> {
// SAFETY: `remove_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called
// and stored a `Pin<KBox<T>>`.
- let data = unsafe { idev.as_ref().drvdata_obtain::<T>() };
+ let data = unsafe { idev.as_ref().drvdata_borrow::<T>() };
- T::unbind(idev, data.as_ref());
+ T::unbind(idev, data);
}
extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
@@ -181,9 +190,9 @@ impl<T: Driver + 'static> Adapter<T> {
// SAFETY: `shutdown_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
// and stored a `Pin<KBox<T>>`.
- let data = unsafe { idev.as_ref().drvdata_obtain::<T>() };
+ let data = unsafe { idev.as_ref().drvdata_borrow::<T>() };
- T::shutdown(idev, data.as_ref());
+ T::shutdown(idev, data);
}
/// The [`i2c::IdTable`] of the corresponding driver.
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 98e8b84e68d1..b64b11f75a35 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -142,7 +142,8 @@ macro_rules! define_read {
/// Bound checks are performed on compile time, hence if the offset is not known at compile
/// time, the build will fail.
$(#[$attr])*
- #[inline]
+ // Always inline to optimize out error path of `io_addr_assert`.
+ #[inline(always)]
pub fn $name(&self, offset: usize) -> $type_name {
let addr = self.io_addr_assert::<$type_name>(offset);
@@ -171,7 +172,8 @@ macro_rules! define_write {
/// Bound checks are performed on compile time, hence if the offset is not known at compile
/// time, the build will fail.
$(#[$attr])*
- #[inline]
+ // Always inline to optimize out error path of `io_addr_assert`.
+ #[inline(always)]
pub fn $name(&self, value: $type_name, offset: usize) {
let addr = self.io_addr_assert::<$type_name>(offset);
@@ -239,7 +241,8 @@ impl<const SIZE: usize> Io<SIZE> {
self.addr().checked_add(offset).ok_or(EINVAL)
}
- #[inline]
+ // Always inline to optimize out error path of `build_assert`.
+ #[inline(always)]
fn io_addr_assert<U>(&self, offset: usize) -> usize {
build_assert!(Self::offset_valid::<U>(offset, SIZE));
diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs
index 56cfde97ce87..b7ac9faf141d 100644
--- a/rust/kernel/io/resource.rs
+++ b/rust/kernel/io/resource.rs
@@ -226,6 +226,8 @@ impl Flags {
/// Resource represents a memory region that must be ioremaped using `ioremap_np`.
pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED);
+ // Always inline to optimize out error path of `build_assert`.
+ #[inline(always)]
const fn new(value: u32) -> Self {
crate::build_assert!(value as u64 <= c_ulong::MAX as u64);
Flags(value as c_ulong)
diff --git a/rust/kernel/irq/flags.rs b/rust/kernel/irq/flags.rs
index adfde96ec47c..d26e25af06ee 100644
--- a/rust/kernel/irq/flags.rs
+++ b/rust/kernel/irq/flags.rs
@@ -96,6 +96,8 @@ impl Flags {
self.0
}
+ // Always inline to optimize out error path of `build_assert`.
+ #[inline(always)]
const fn new(value: u32) -> Self {
build_assert!(value as u64 <= c_ulong::MAX as u64);
Self(value as c_ulong)
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index f870080af8ac..fa81acbdc8c2 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -40,11 +40,11 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
fits_within!(value, T, num_bits)
}
-/// An integer value that requires only the `N` less significant bits of the wrapped type to be
+/// An integer value that requires only the `N` least significant bits of the wrapped type to be
/// encoded.
///
/// This limits the number of usable bits in the wrapped integer type, and thus the stored value to
-/// a narrower range, which provides guarantees that can be useful when working with in e.g.
+/// a narrower range, which provides guarantees that can be useful when working within e.g.
/// bitfields.
///
/// # Invariants
@@ -56,7 +56,7 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
/// # Examples
///
/// The preferred way to create values is through constants and the [`Bounded::new`] family of
-/// constructors, as they trigger a build error if the type invariants cannot be withheld.
+/// constructors, as they trigger a build error if the type invariants cannot be upheld.
///
/// ```
/// use kernel::num::Bounded;
@@ -82,7 +82,7 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
/// ```
/// use kernel::num::Bounded;
///
-/// // This succeeds because `15` can be represented with 4 unsigned bits.
+/// // This succeeds because `15` can be represented with 4 unsigned bits.
/// assert!(Bounded::<u8, 4>::try_new(15).is_some());
///
/// // This fails because `16` cannot be represented with 4 unsigned bits.
@@ -221,7 +221,7 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
/// let v: Option<Bounded<u16, 8>> = 128u32.try_into_bounded();
/// assert_eq!(v.as_deref().copied(), Some(128));
///
-/// // Fails because `128` doesn't fits into 6 bits.
+/// // Fails because `128` doesn't fit into 6 bits.
/// let v: Option<Bounded<u16, 6>> = 128u32.try_into_bounded();
/// assert_eq!(v, None);
/// ```
@@ -259,9 +259,9 @@ macro_rules! impl_const_new {
assert!(fits_within!(VALUE, $type, N));
}
- // INVARIANT: `fits_within` confirmed that `VALUE` can be represented within
+ // SAFETY: `fits_within` confirmed that `VALUE` can be represented within
// `N` bits.
- Self::__new(VALUE)
+ unsafe { Self::__new(VALUE) }
}
}
)*
@@ -282,9 +282,10 @@ where
/// All instances of [`Bounded`] must be created through this method as it enforces most of the
/// type invariants.
///
- /// The caller remains responsible for checking, either statically or dynamically, that `value`
- /// can be represented as a `T` using at most `N` bits.
- const fn __new(value: T) -> Self {
+ /// # Safety
+ ///
+ /// The caller must ensure that `value` can be represented within `N` bits.
+ const unsafe fn __new(value: T) -> Self {
// Enforce the type invariants.
const {
// `N` cannot be zero.
@@ -293,6 +294,7 @@ where
assert!(N <= T::BITS);
}
+ // INVARIANT: The caller ensures `value` fits within `N` bits.
Self(value)
}
@@ -328,8 +330,8 @@ where
/// ```
pub fn try_new(value: T) -> Option<Self> {
fits_within(value, N).then(|| {
- // INVARIANT: `fits_within` confirmed that `value` can be represented within `N` bits.
- Self::__new(value)
+ // SAFETY: `fits_within` confirmed that `value` can be represented within `N` bits.
+ unsafe { Self::__new(value) }
})
}
@@ -363,6 +365,7 @@ where
/// assert_eq!(Bounded::<u8, 1>::from_expr(1).get(), 1);
/// assert_eq!(Bounded::<u16, 8>::from_expr(0xff).get(), 0xff);
/// ```
+ // Always inline to optimize out error path of `build_assert`.
#[inline(always)]
pub fn from_expr(expr: T) -> Self {
crate::build_assert!(
@@ -370,8 +373,8 @@ where
"Requested value larger than maximal representable value."
);
- // INVARIANT: `fits_within` confirmed that `expr` can be represented within `N` bits.
- Self::__new(expr)
+ // SAFETY: `fits_within` confirmed that `expr` can be represented within `N` bits.
+ unsafe { Self::__new(expr) }
}
/// Returns the wrapped value as the backing type.
@@ -410,9 +413,9 @@ where
);
}
- // INVARIANT: The value did fit within `N` bits, so it will all the more fit within
+ // SAFETY: The value did fit within `N` bits, so it will all the more fit within
// the larger `M` bits.
- Bounded::__new(self.0)
+ unsafe { Bounded::__new(self.0) }
}
/// Attempts to shrink the number of bits usable for `self`.
@@ -466,9 +469,9 @@ where
// `U` and `T` have the same sign, hence this conversion cannot fail.
let value = unsafe { U::try_from(self.get()).unwrap_unchecked() };
- // INVARIANT: Although the backing type has changed, the value is still represented within
+ // SAFETY: Although the backing type has changed, the value is still represented within
// `N` bits, and with the same signedness.
- Bounded::__new(value)
+ unsafe { Bounded::__new(value) }
}
}
@@ -501,7 +504,7 @@ where
/// let v: Option<Bounded<u16, 8>> = 128u32.try_into_bounded();
/// assert_eq!(v.as_deref().copied(), Some(128));
///
-/// // Fails because `128` doesn't fits into 6 bits.
+/// // Fails because `128` doesn't fit into 6 bits.
/// let v: Option<Bounded<u16, 6>> = 128u32.try_into_bounded();
/// assert_eq!(v, None);
/// ```
@@ -944,9 +947,9 @@ macro_rules! impl_from_primitive {
Self: AtLeastXBits<{ <$type as Integer>::BITS as usize }>,
{
fn from(value: $type) -> Self {
- // INVARIANT: The trait bound on `Self` guarantees that `N` bits is
+ // SAFETY: The trait bound on `Self` guarantees that `N` bits is
// enough to hold any value of the source type.
- Self::__new(T::from(value))
+ unsafe { Self::__new(T::from(value)) }
}
}
)*
@@ -1051,8 +1054,8 @@ where
T: Integer + From<bool>,
{
fn from(value: bool) -> Self {
- // INVARIANT: A boolean can be represented using a single bit, and thus fits within any
+ // SAFETY: A boolean can be represented using a single bit, and thus fits within any
// integer type for any `N` > 0.
- Self::__new(T::from(value))
+ unsafe { Self::__new(T::from(value)) }
}
}
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 82e128431f08..bea76ca9c3da 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -50,13 +50,22 @@ pub use self::irq::{
/// An adapter for the registration of PCI drivers.
pub struct Adapter<T: Driver>(T);
-// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// SAFETY:
+// - `bindings::pci_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct pci_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+ type DriverType = bindings::pci_driver;
+ type DriverData = T;
+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
// a preceding call to `register` has been successful.
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
- type RegType = bindings::pci_driver;
-
unsafe fn register(
- pdrv: &Opaque<Self::RegType>,
+ pdrv: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
@@ -68,14 +77,14 @@ unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
(*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
}
- // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr())
})
}
- unsafe fn unregister(pdrv: &Opaque<Self::RegType>) {
- // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
+ // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
unsafe { bindings::pci_unregister_driver(pdrv.get()) }
}
}
@@ -114,9 +123,9 @@ impl<T: Driver + 'static> Adapter<T> {
// SAFETY: `remove_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
// and stored a `Pin<KBox<T>>`.
- let data = unsafe { pdev.as_ref().drvdata_obtain::<T>() };
+ let data = unsafe { pdev.as_ref().drvdata_borrow::<T>() };
- T::unbind(pdev, data.as_ref());
+ T::unbind(pdev, data);
}
}
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index ed889f079cab..35a5813ffb33 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -26,13 +26,22 @@ use core::{
/// An adapter for the registration of platform drivers.
pub struct Adapter<T: Driver>(T);
-// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// SAFETY:
+// - `bindings::platform_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct platform_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+ type DriverType = bindings::platform_driver;
+ type DriverData = T;
+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
// a preceding call to `register` has been successful.
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
- type RegType = bindings::platform_driver;
-
unsafe fn register(
- pdrv: &Opaque<Self::RegType>,
+ pdrv: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
@@ -55,12 +64,12 @@ unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
(*pdrv.get()).driver.acpi_match_table = acpi_table;
}
- // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) })
}
- unsafe fn unregister(pdrv: &Opaque<Self::RegType>) {
- // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
+ // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
unsafe { bindings::platform_driver_unregister(pdrv.get()) };
}
}
@@ -92,9 +101,9 @@ impl<T: Driver + 'static> Adapter<T> {
// SAFETY: `remove_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
// and stored a `Pin<KBox<T>>`.
- let data = unsafe { pdev.as_ref().drvdata_obtain::<T>() };
+ let data = unsafe { pdev.as_ref().drvdata_borrow::<T>() };
- T::unbind(pdev, data.as_ref());
+ T::unbind(pdev, data);
}
}
diff --git a/rust/kernel/rbtree.rs b/rust/kernel/rbtree.rs
index 4729eb56827a..312cecab72e7 100644
--- a/rust/kernel/rbtree.rs
+++ b/rust/kernel/rbtree.rs
@@ -985,7 +985,7 @@ impl<'a, K, V> CursorMut<'a, K, V> {
self.peek(Direction::Prev)
}
- /// Access the previous node without moving the cursor.
+ /// Access the next node without moving the cursor.
pub fn peek_next(&self) -> Option<(&K, &V)> {
self.peek(Direction::Next)
}
@@ -1130,7 +1130,7 @@ pub struct IterMut<'a, K, V> {
}
// SAFETY: The [`IterMut`] has exclusive access to both `K` and `V`, so it is sufficient to require them to be `Send`.
-// The iterator only gives out immutable references to the keys, but since the iterator has excusive access to those same
+// The iterator only gives out immutable references to the keys, but since the iterator has exclusive access to those same
// keys, `Send` is sufficient. `Sync` would be okay, but it is more restrictive to the user.
unsafe impl<'a, K: Send, V: Send> Send for IterMut<'a, K, V> {}
diff --git a/rust/kernel/sync/atomic/predefine.rs b/rust/kernel/sync/atomic/predefine.rs
index 45a17985cda4..0fca1ba3c2db 100644
--- a/rust/kernel/sync/atomic/predefine.rs
+++ b/rust/kernel/sync/atomic/predefine.rs
@@ -35,12 +35,23 @@ unsafe impl super::AtomicAdd<i64> for i64 {
// as `isize` and `usize`, and `isize` and `usize` are always bi-directional transmutable to
// `isize_atomic_repr`, which also always implements `AtomicImpl`.
#[allow(non_camel_case_types)]
+#[cfg(not(testlib))]
#[cfg(not(CONFIG_64BIT))]
type isize_atomic_repr = i32;
#[allow(non_camel_case_types)]
+#[cfg(not(testlib))]
#[cfg(CONFIG_64BIT)]
type isize_atomic_repr = i64;
+#[allow(non_camel_case_types)]
+#[cfg(testlib)]
+#[cfg(target_pointer_width = "32")]
+type isize_atomic_repr = i32;
+#[allow(non_camel_case_types)]
+#[cfg(testlib)]
+#[cfg(target_pointer_width = "64")]
+type isize_atomic_repr = i64;
+
// Ensure size and alignment requirements are checked.
static_assert!(size_of::<isize>() == size_of::<isize_atomic_repr>());
static_assert!(align_of::<isize>() == align_of::<isize_atomic_repr>());
diff --git a/rust/kernel/sync/refcount.rs b/rust/kernel/sync/refcount.rs
index 19236a5bccde..6c7ae8b05a0b 100644
--- a/rust/kernel/sync/refcount.rs
+++ b/rust/kernel/sync/refcount.rs
@@ -23,7 +23,8 @@ impl Refcount {
/// Construct a new [`Refcount`] from an initial value.
///
/// The initial value should be non-saturated.
- #[inline]
+ // Always inline to optimize out error path of `build_assert`.
+ #[inline(always)]
pub fn new(value: i32) -> Self {
build_assert!(value >= 0, "initial value saturated");
// SAFETY: There are no safety requirements for this FFI call.
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index d10b65e9fb6a..67ce5c85c619 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -27,13 +27,22 @@ use core::{
/// An adapter for the registration of USB drivers.
pub struct Adapter<T: Driver>(T);
-// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// SAFETY:
+// - `bindings::usb_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct usb_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+ type DriverType = bindings::usb_driver;
+ type DriverData = T;
+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
// a preceding call to `register` has been successful.
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
- type RegType = bindings::usb_driver;
-
unsafe fn register(
- udrv: &Opaque<Self::RegType>,
+ udrv: &Opaque<Self::DriverType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
@@ -45,14 +54,14 @@ unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
(*udrv.get()).id_table = T::ID_TABLE.as_ptr();
}
- // SAFETY: `udrv` is guaranteed to be a valid `RegType`.
+ // SAFETY: `udrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
bindings::usb_register_driver(udrv.get(), module.0, name.as_char_ptr())
})
}
- unsafe fn unregister(udrv: &Opaque<Self::RegType>) {
- // SAFETY: `udrv` is guaranteed to be a valid `RegType`.
+ unsafe fn unregister(udrv: &Opaque<Self::DriverType>) {
+ // SAFETY: `udrv` is guaranteed to be a valid `DriverType`.
unsafe { bindings::usb_deregister(udrv.get()) };
}
}
@@ -94,9 +103,9 @@ impl<T: Driver + 'static> Adapter<T> {
// SAFETY: `disconnect_callback` is only ever called after a successful call to
// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
// and stored a `Pin<KBox<T>>`.
- let data = unsafe { dev.drvdata_obtain::<T>() };
+ let data = unsafe { dev.drvdata_borrow::<T>() };
- T::disconnect(intf, data.as_ref());
+ T::disconnect(intf, data);
}
}
diff --git a/rust/macros/fmt.rs b/rust/macros/fmt.rs
index 2f4b9f6e2211..8354abd54502 100644
--- a/rust/macros/fmt.rs
+++ b/rust/macros/fmt.rs
@@ -67,7 +67,7 @@ pub(crate) fn fmt(input: TokenStream) -> TokenStream {
}
(None, acc)
})();
- args.extend(quote_spanned!(first_span => #lhs #adapter(&#rhs)));
+ args.extend(quote_spanned!(first_span => #lhs #adapter(&(#rhs))));
}
};
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index b38002151871..33f66e86418a 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -59,7 +59,7 @@ use proc_macro::TokenStream;
///
/// # Examples
///
-/// ```
+/// ```ignore
/// use kernel::prelude::*;
///
/// module!{
diff --git a/rust/proc-macro2/lib.rs b/rust/proc-macro2/lib.rs
index 7b78d065d51c..5d408943fa0d 100644
--- a/rust/proc-macro2/lib.rs
+++ b/rust/proc-macro2/lib.rs
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
+// When fixdep scans this, it will find this string `CONFIG_RUSTC_VERSION_TEXT`
+// and thus add a dependency on `include/config/RUSTC_VERSION_TEXT`, which is
+// touched by Kconfig when the version string from the compiler changes.
+
//! [![github]](https://github.com/dtolnay/proc-macro2)&ensp;[![crates-io]](https://crates.io/crates/proc-macro2)&ensp;[![docs-rs]](crate)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 5037f4715d74..32e209bc7985 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -166,11 +166,13 @@ else ifeq ($(KBUILD_CHECKSRC),2)
cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $<
endif
+ifeq ($(KBUILD_EXTMOD),)
ifneq ($(KBUILD_EXTRA_WARN),)
cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(KDOCFLAGS) \
$(if $(findstring 2, $(KBUILD_EXTRA_WARN)), -Wall) \
$<
endif
+endif
# Compile C sources (.c)
# ---------------------------------------------------------------------------
@@ -356,7 +358,7 @@ $(obj)/%.o: $(obj)/%.rs FORCE
quiet_cmd_rustc_rsi_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_rsi_rs = \
$(rust_common_cmd) -Zunpretty=expanded $< >$@; \
- command -v $(RUSTFMT) >/dev/null && $(RUSTFMT) $@
+ command -v $(RUSTFMT) >/dev/null && $(RUSTFMT) --config-path $(srctree)/.rustfmt.toml $@
$(obj)/%.rsi: $(obj)/%.rs FORCE
+$(call if_changed_dep,rustc_rsi_rs)
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index cd788cac9d91..276c3134a563 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -113,7 +113,8 @@ vmlinux: vmlinux.unstripped FORCE
# what kmod expects to parse.
quiet_cmd_modules_builtin_modinfo = GEN $@
cmd_modules_builtin_modinfo = $(cmd_objcopy); \
- sed -i 's/\x00\+$$/\x00/g' $@
+ sed -i 's/\x00\+$$/\x00/g' $@; \
+ chmod -x $@
OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
diff --git a/scripts/check-function-names.sh b/scripts/check-function-names.sh
index 410042591cfc..08071133e5a5 100755
--- a/scripts/check-function-names.sh
+++ b/scripts/check-function-names.sh
@@ -13,7 +13,7 @@ if [ ! -f "$objfile" ]; then
exit 1
fi
-bad_symbols=$(nm "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' | grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
+bad_symbols=$(${NM:-nm} "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' | grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
if [ -n "$bad_symbols" ]; then
echo "$bad_symbols" | while read -r sym; do
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index 147d0cc94068..766c2d91cd81 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -61,7 +61,6 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
display_name,
deps,
cfg=[],
- edition="2021",
):
append_crate(
display_name,
@@ -69,13 +68,37 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
deps,
cfg,
is_workspace_member=False,
- edition=edition,
+ # Miguel Ojeda writes:
+ #
+ # > ... in principle even the sysroot crates may have different
+ # > editions.
+ # >
+ # > For instance, in the move to 2024, it seems all happened at once
+ # > in 1.87.0 in these upstream commits:
+ # >
+ # > 0e071c2c6a58 ("Migrate core to Rust 2024")
+ # > f505d4e8e380 ("Migrate alloc to Rust 2024")
+ # > 0b2489c226c3 ("Migrate proc_macro to Rust 2024")
+ # > 993359e70112 ("Migrate std to Rust 2024")
+ # >
+ # > But in the previous move to 2021, `std` moved in 1.59.0, while
+ # > the others in 1.60.0:
+ # >
+ # > b656384d8398 ("Update stdlib to the 2021 edition")
+ # > 06a1c14d52a8 ("Switch all libraries to the 2021 edition")
+ #
+ # Link: https://lore.kernel.org/all/CANiq72kd9bHdKaAm=8xCUhSHMy2csyVed69bOc4dXyFAW4sfuw@mail.gmail.com/
+ #
+ # At the time of writing all rust versions we support build the
+ # sysroot crates with the same edition. We may need to relax this
+ # assumption if future edition moves span multiple rust versions.
+ edition=core_edition,
)
# NB: sysroot crates reexport items from one another so setting up our transitive dependencies
# here is important for ensuring that rust-analyzer can resolve symbols. The sources of truth
# for this dependency graph are `(sysroot_src / crate / "Cargo.toml" for crate in crates)`.
- append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []), edition=core_edition)
+ append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []))
append_sysroot_crate("alloc", ["core"])
append_sysroot_crate("std", ["alloc", "core"])
append_sysroot_crate("proc_macro", ["core", "std"])
@@ -83,7 +106,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
append_crate(
"compiler_builtins",
srctree / "rust" / "compiler_builtins.rs",
- [],
+ ["core"],
)
append_crate(
@@ -96,14 +119,15 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
append_crate(
"quote",
srctree / "rust" / "quote" / "lib.rs",
- ["alloc", "proc_macro", "proc_macro2"],
+ ["core", "alloc", "std", "proc_macro", "proc_macro2"],
cfg=crates_cfgs["quote"],
+ edition="2018",
)
append_crate(
"syn",
srctree / "rust" / "syn" / "lib.rs",
- ["proc_macro", "proc_macro2", "quote"],
+ ["std", "proc_macro", "proc_macro2", "quote"],
cfg=crates_cfgs["syn"],
)
@@ -123,7 +147,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
append_crate(
"pin_init_internal",
srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs",
- [],
+ ["std", "proc_macro"],
cfg=["kernel"],
is_proc_macro=True,
)
@@ -131,7 +155,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
append_crate(
"pin_init",
srctree / "rust" / "pin-init" / "src" / "lib.rs",
- ["core", "pin_init_internal", "macros"],
+ ["core", "compiler_builtins", "pin_init_internal", "macros"],
cfg=["kernel"],
)
@@ -190,7 +214,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
append_crate(
name,
path,
- ["core", "kernel"],
+ ["core", "kernel", "pin_init"],
cfg=cfg,
)
@@ -213,9 +237,6 @@ def main():
level=logging.INFO if args.verbose else logging.WARNING
)
- # Making sure that the `sysroot` and `sysroot_src` belong to the same toolchain.
- assert args.sysroot in args.sysroot_src.parents
-
rust_project = {
"crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs, args.core_edition),
"sysroot": str(args.sysroot),
diff --git a/scripts/kconfig/nconf-cfg.sh b/scripts/kconfig/nconf-cfg.sh
index a20290b1a37d..4d08453f9bdb 100755
--- a/scripts/kconfig/nconf-cfg.sh
+++ b/scripts/kconfig/nconf-cfg.sh
@@ -6,8 +6,9 @@ set -eu
cflags=$1
libs=$2
-PKG="ncursesw menuw panelw"
-PKG2="ncurses menu panel"
+# Keep library order for static linking (HOSTCC='cc -static')
+PKG="menuw panelw ncursesw"
+PKG2="menu panel ncurses"
if [ -n "$(command -v ${HOSTPKG_CONFIG})" ]; then
if ${HOSTPKG_CONFIG} --exists $PKG; then
@@ -28,19 +29,19 @@ fi
# find ncurses by pkg-config.)
if [ -f /usr/include/ncursesw/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncursesw > ${cflags}
- echo -lncursesw -lmenuw -lpanelw > ${libs}
+ echo -lmenuw -lpanelw -lncursesw > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncurses > ${cflags}
- echo -lncurses -lmenu -lpanel > ${libs}
+ echo -lmenu -lpanel -lncurses > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses.h ]; then
echo -D_GNU_SOURCE > ${cflags}
- echo -lncurses -lmenu -lpanel > ${libs}
+ echo -lmenu -lpanel -lncurses > ${libs}
exit 0
fi
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index 882272120c9e..a73515a82272 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -555,13 +555,11 @@ copy_orig_objects() {
local file_dir="$(dirname "$file")"
local orig_file="$ORIG_DIR/$rel_file"
local orig_dir="$(dirname "$orig_file")"
- local cmd_file="$file_dir/.$(basename "$file").cmd"
[[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file"
mkdir -p "$orig_dir"
cp -f "$file" "$orig_dir"
- [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$orig_dir"
done
xtrace_restore
@@ -740,15 +738,17 @@ build_patch_module() {
local orig_dir="$(dirname "$orig_file")"
local kmod_file="$KMOD_DIR/$rel_file"
local kmod_dir="$(dirname "$kmod_file")"
- local cmd_file="$orig_dir/.$(basename "$file").cmd"
+ local cmd_file="$kmod_dir/.$(basename "$file").cmd"
mkdir -p "$kmod_dir"
cp -f "$file" "$kmod_dir"
- [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$kmod_dir"
# Tell kbuild this is a prebuilt object
cp -f "$file" "${kmod_file}_shipped"
+ # Make modpost happy
+ touch "$cmd_file"
+
echo -n " $rel_file" >> "$makefile"
done
diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec
index 98f206cb7c60..0f1c8de1bd95 100644
--- a/scripts/package/kernel.spec
+++ b/scripts/package/kernel.spec
@@ -2,6 +2,8 @@
%{!?_arch: %define _arch dummy}
%{!?make: %define make make}
%define makeflags %{?_smp_mflags} ARCH=%{ARCH}
+%define __spec_install_post /usr/lib/rpm/brp-compress || :
+%define debug_package %{nil}
Name: kernel
Summary: The Linux Kernel
@@ -46,34 +48,12 @@ against the %{version} kernel package.
%endif
%if %{with_debuginfo}
-# list of debuginfo-related options taken from distribution kernel.spec
-# files
-%undefine _include_minidebuginfo
-%undefine _find_debuginfo_dwz_opts
-%undefine _unique_build_ids
-%undefine _unique_debug_names
-%undefine _unique_debug_srcs
-%undefine _debugsource_packages
-%undefine _debuginfo_subpackages
-%global _find_debuginfo_opts -r
-%global _missing_build_ids_terminate_build 1
-%global _no_recompute_build_ids 1
-%{debug_package}
+%package debuginfo
+Summary: Debug information package for the Linux kernel
+%description debuginfo
+This package provides debug information for the kernel image and modules from the
+%{version} package.
%endif
-# some (but not all) versions of rpmbuild emit %%debug_package with
-# %%install. since we've already emitted it manually, that would cause
-# a package redefinition error. ensure that doesn't happen
-%define debug_package %{nil}
-
-# later, we make all modules executable so that find-debuginfo.sh strips
-# them up. but they don't actually need to be executable, so remove the
-# executable bit, taking care to do it _after_ find-debuginfo.sh has run
-%define __spec_install_post \
- %{?__debug_package:%{__debug_install_post}} \
- %{__arch_install_post} \
- %{__os_install_post} \
- find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \\\
- | xargs --no-run-if-empty chmod u-x
%prep
%setup -q -n linux
@@ -87,7 +67,7 @@ patch -p1 < %{SOURCE2}
mkdir -p %{buildroot}/lib/modules/%{KERNELRELEASE}
cp $(%{make} %{makeflags} -s image_name) %{buildroot}/lib/modules/%{KERNELRELEASE}/vmlinuz
# DEPMOD=true makes depmod no-op. We do not package depmod-generated files.
-%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} DEPMOD=true modules_install
+%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} INSTALL_MOD_STRIP=1 DEPMOD=true modules_install
%{make} %{makeflags} INSTALL_HDR_PATH=%{buildroot}/usr headers_install
cp System.map %{buildroot}/lib/modules/%{KERNELRELEASE}
cp .config %{buildroot}/lib/modules/%{KERNELRELEASE}/config
@@ -118,22 +98,31 @@ ln -fns /usr/src/kernels/%{KERNELRELEASE} %{buildroot}/lib/modules/%{KERNELRELEA
echo "%exclude /lib/modules/%{KERNELRELEASE}/build"
} > %{buildroot}/kernel.list
-# make modules executable so that find-debuginfo.sh strips them. this
-# will be undone later in %%__spec_install_post
-find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \
- | xargs --no-run-if-empty chmod u+x
-
%if %{with_debuginfo}
# copying vmlinux directly to the debug directory means it will not get
# stripped (but its source paths will still be collected + fixed up)
mkdir -p %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
cp vmlinux %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
+
+echo /usr/lib/debug/lib/modules/%{KERNELRELEASE}/vmlinux > %{buildroot}/debuginfo.list
+
+while read -r mod; do
+ mod="${mod%.o}.ko"
+ dbg="%{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}/kernel/${mod}"
+ buildid=$("${READELF}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
+ link="%{buildroot}/usr/lib/debug/.build-id/${buildid}.debug"
+
+ mkdir -p "${dbg%/*}" "${link%/*}"
+ "${OBJCOPY}" --only-keep-debug "${mod}" "${dbg}"
+ ln -sf --relative "${dbg}" "${link}"
+
+ echo "${dbg#%{buildroot}}" >> %{buildroot}/debuginfo.list
+ echo "${link#%{buildroot}}" >> %{buildroot}/debuginfo.list
+done < modules.order
%endif
%clean
rm -rf %{buildroot}
-rm -f debugfiles.list debuglinks.list debugsourcefiles.list debugsources.list \
- elfbins.list
%post
if [ -x /usr/bin/kernel-install ]; then
@@ -172,3 +161,9 @@ fi
/usr/src/kernels/%{KERNELRELEASE}
/lib/modules/%{KERNELRELEASE}/build
%endif
+
+%if %{with_debuginfo}
+%files -f %{buildroot}/debuginfo.list debuginfo
+%defattr (-, root, root)
+%exclude /debuginfo.list
+%endif
diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs
index be0561049660..6fd9f5c84e2e 100644
--- a/scripts/rustdoc_test_gen.rs
+++ b/scripts/rustdoc_test_gen.rs
@@ -206,7 +206,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
/// The anchor where the test code body starts.
#[allow(unused)]
- static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 1;
+ static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 2;
{{
#![allow(unreachable_pub, clippy::disallowed_names)]
{body}
diff --git a/scripts/tracepoint-update.c b/scripts/tracepoint-update.c
index 90046aedc97b..5cf43c0aac89 100644
--- a/scripts/tracepoint-update.c
+++ b/scripts/tracepoint-update.c
@@ -49,6 +49,8 @@ static int add_string(const char *str, const char ***vals, int *count)
array = realloc(array, sizeof(char *) * size);
if (!array) {
fprintf(stderr, "Failed memory allocation\n");
+ free(*vals);
+ *vals = NULL;
return -1;
}
*vals = array;
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a7ea4a1c3bed..6340823f8b53 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -465,7 +465,7 @@ out:
}
/**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - execute a TPM2_Unseal command
*
* @chip: TPM chip to use
* @payload: the key data in clear and encrypted form
@@ -498,7 +498,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
if (rc)
goto out;
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index dea2c661b353..e4d7288d1e1e 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -18,16 +18,6 @@ config SND_ARMAACI
select SND_PCM
select SND_AC97_CODEC
-config SND_PXA2XX_AC97
- tristate "AC97 driver for the Intel PXA2xx chip"
- depends on ARCH_PXA
- select SND_AC97_CODEC
- select SND_PXA2XX_LIB
- select SND_PXA2XX_LIB_AC97
- help
- Say Y or M if you want to support any AC97 codec attached to
- the PXA2xx AC97 interface.
-
endif # SND_ARM
config SND_PXA2XX_LIB
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 899edb4bb278..99325a66cf77 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -9,6 +9,3 @@ snd-aaci-y := aaci.o
obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
-
-obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
-snd-pxa2xx-ac97-y := pxa2xx-ac97.o
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 64510318091f..1e114dbcf93c 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -13,10 +13,9 @@
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/io.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/soc/pxa/cpu.h>
#include <sound/pxa2xx-lib.h>
@@ -31,6 +30,7 @@ static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
static int reset_gpio;
+struct gpio_desc *rst_gpio;
static void __iomem *ac97_reg_base;
/*
@@ -321,7 +321,6 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
{
int ret;
int irq;
- pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
ac97_reg_base = devm_platform_ioremap_resource(dev, 0);
if (IS_ERR(ac97_reg_base)) {
@@ -329,32 +328,15 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
return PTR_ERR(ac97_reg_base);
}
- if (pdata) {
- switch (pdata->reset_gpio) {
- case 95:
- case 113:
- reset_gpio = pdata->reset_gpio;
- break;
- case 0:
- reset_gpio = 113;
- break;
- case -1:
- break;
- default:
- dev_err(&dev->dev, "Invalid reset GPIO %d\n",
- pdata->reset_gpio);
- }
- } else if (!pdata && dev->dev.of_node) {
- pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
- pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node,
- "reset-gpios", 0);
- if (pdata->reset_gpio == -ENOENT)
- pdata->reset_gpio = -1;
- else if (pdata->reset_gpio < 0)
- return pdata->reset_gpio;
- reset_gpio = pdata->reset_gpio;
+ if (dev->dev.of_node) {
+ /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */
+ rst_gpio = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH);
+ ret = PTR_ERR(rst_gpio);
+ if (ret == -ENOENT)
+ reset_gpio = -1;
+ else if (ret)
+ return ret;
+ reset_gpio = desc_to_gpio(rst_gpio);
} else {
if (cpu_is_pxa27x())
reset_gpio = 113;
@@ -367,13 +349,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
* here so that it is an output driven high when switching from
* AC97_nRESET alt function to generic gpio.
*/
- ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH,
- "pxa27x ac97 reset");
- if (ret < 0) {
- pr_err("%s: gpio_request_one() failed: %d\n",
- __func__, ret);
- goto err_conf;
- }
+ gpiod_set_consumer_name(rst_gpio, "pxa27x ac97 reset");
pxa27x_configure_ac97reset(reset_gpio, false);
ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
@@ -424,8 +400,6 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
void pxa2xx_ac97_hw_remove(struct platform_device *dev)
{
- if (cpu_is_pxa27x())
- gpio_free(reset_gpio);
writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
free_irq(platform_get_irq(dev, 0), NULL);
if (ac97conf_clk) {
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
deleted file mode 100644
index 77b11616a7ee..000000000000
--- a/sound/arm/pxa2xx-ac97.c
+++ /dev/null
@@ -1,286 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
- *
- * Author: Nicolas Pitre
- * Created: Dec 02, 2004
- * Copyright: MontaVista Software Inc.
- */
-
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/initval.h>
-#include <sound/pxa2xx-lib.h>
-#include <sound/dmaengine_pcm.h>
-
-#include <linux/platform_data/asoc-pxa.h>
-
-static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
-{
- if (!pxa2xx_ac97_try_cold_reset())
- pxa2xx_ac97_try_warm_reset();
-
- pxa2xx_ac97_finish_reset();
-}
-
-static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- int ret;
-
- ret = pxa2xx_ac97_read(ac97->num, reg);
- if (ret < 0)
- return 0;
- else
- return (unsigned short)(ret & 0xffff);
-}
-
-static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
- unsigned short reg, unsigned short val)
-{
- pxa2xx_ac97_write(ac97->num, reg, val);
-}
-
-static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_legacy_read,
- .write = pxa2xx_ac97_legacy_write,
- .reset = pxa2xx_ac97_legacy_reset,
-};
-
-static struct snd_pcm *pxa2xx_ac97_pcm;
-static struct snd_ac97 *pxa2xx_ac97_ac97;
-
-static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- pxa2xx_audio_ops_t *platform_ops;
- int ret, i;
-
- ret = pxa2xx_pcm_open(substream);
- if (ret)
- return ret;
-
- runtime->hw.channels_min = 2;
- runtime->hw.channels_max = 2;
-
- i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
- runtime->hw.rates = pxa2xx_ac97_ac97->rates[i];
- snd_pcm_limit_hw_rates(runtime);
-
- platform_ops = substream->pcm->card->dev->platform_data;
- if (platform_ops && platform_ops->startup) {
- ret = platform_ops->startup(substream, platform_ops->priv);
- if (ret < 0)
- pxa2xx_pcm_close(substream);
- }
-
- return ret;
-}
-
-static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream)
-{
- pxa2xx_audio_ops_t *platform_ops;
-
- platform_ops = substream->pcm->card->dev->platform_data;
- if (platform_ops && platform_ops->shutdown)
- platform_ops->shutdown(substream, platform_ops->priv);
-
- return 0;
-}
-
-static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- int ret;
-
- ret = pxa2xx_pcm_prepare(substream);
- if (ret < 0)
- return ret;
-
- return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
-}
-
-static int pxa2xx_ac97_do_suspend(struct snd_card *card)
-{
- pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
- snd_ac97_suspend(pxa2xx_ac97_ac97);
- if (platform_ops && platform_ops->suspend)
- platform_ops->suspend(platform_ops->priv);
-
- return pxa2xx_ac97_hw_suspend();
-}
-
-static int pxa2xx_ac97_do_resume(struct snd_card *card)
-{
- pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
- int rc;
-
- rc = pxa2xx_ac97_hw_resume();
- if (rc)
- return rc;
-
- if (platform_ops && platform_ops->resume)
- platform_ops->resume(platform_ops->priv);
- snd_ac97_resume(pxa2xx_ac97_ac97);
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
-
- return 0;
-}
-
-static int pxa2xx_ac97_suspend(struct device *dev)
-{
- struct snd_card *card = dev_get_drvdata(dev);
- int ret = 0;
-
- if (card)
- ret = pxa2xx_ac97_do_suspend(card);
-
- return ret;
-}
-
-static int pxa2xx_ac97_resume(struct device *dev)
-{
- struct snd_card *card = dev_get_drvdata(dev);
- int ret = 0;
-
- if (card)
- ret = pxa2xx_ac97_do_resume(card);
-
- return ret;
-}
-
-static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
-
-static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
- .open = pxa2xx_ac97_pcm_open,
- .close = pxa2xx_ac97_pcm_close,
- .hw_params = pxa2xx_pcm_hw_params,
- .prepare = pxa2xx_ac97_pcm_prepare,
- .trigger = pxa2xx_pcm_trigger,
- .pointer = pxa2xx_pcm_pointer,
-};
-
-
-static int pxa2xx_ac97_pcm_new(struct snd_card *card)
-{
- struct snd_pcm *pcm;
- int ret;
-
- ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm);
- if (ret)
- goto out;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- goto out;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm);
- if (ret)
- goto out;
-
- pxa2xx_ac97_pcm = pcm;
- ret = 0;
-
- out:
- return ret;
-}
-
-static int pxa2xx_ac97_probe(struct platform_device *dev)
-{
- struct snd_card *card;
- struct snd_ac97_bus *ac97_bus;
- struct snd_ac97_template ac97_template;
- int ret;
- pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
-
- if (dev->id >= 0) {
- dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
- ret = -ENXIO;
- goto err_dev;
- }
-
- ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0, &card);
- if (ret < 0)
- goto err;
-
- strscpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
-
- ret = pxa2xx_ac97_pcm_new(card);
- if (ret)
- goto err;
-
- ret = pxa2xx_ac97_hw_probe(dev);
- if (ret)
- goto err;
-
- ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
- if (ret)
- goto err_remove;
- memset(&ac97_template, 0, sizeof(ac97_template));
- ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
- if (ret)
- goto err_remove;
-
- snprintf(card->shortname, sizeof(card->shortname),
- "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
- snprintf(card->longname, sizeof(card->longname),
- "%s (%s)", dev->dev.driver->name, card->mixername);
-
- if (pdata && pdata->codec_pdata[0])
- snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
- ret = snd_card_register(card);
- if (ret == 0) {
- platform_set_drvdata(dev, card);
- return 0;
- }
-
-err_remove:
- pxa2xx_ac97_hw_remove(dev);
-err:
- if (card)
- snd_card_free(card);
-err_dev:
- return ret;
-}
-
-static void pxa2xx_ac97_remove(struct platform_device *dev)
-{
- struct snd_card *card = platform_get_drvdata(dev);
-
- if (card) {
- snd_card_free(card);
- pxa2xx_ac97_hw_remove(dev);
- }
-}
-
-static struct platform_driver pxa2xx_ac97_driver = {
- .probe = pxa2xx_ac97_probe,
- .remove = pxa2xx_ac97_remove,
- .driver = {
- .name = "pxa2xx-ac97",
- .pm = &pxa2xx_ac97_pm_ops,
- },
-};
-
-module_platform_driver(pxa2xx_ac97_driver);
-
-MODULE_AUTHOR("Nicolas Pitre");
-MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c
index 96e6d82dc69e..b22f93424c62 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda.c
@@ -2,7 +2,7 @@
//
// TAS2781 HDA Shared Lib for I2C&SPI driver
//
-// Copyright 2025 Texas Instruments, Inc.
+// Copyright 2025 - 2026 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
@@ -159,7 +159,6 @@ static void tas2781_apply_calib(struct tasdevice_priv *p)
r->tlimit_reg = cali_reg[4];
}
- p->is_user_space_calidata = true;
cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
}
@@ -216,6 +215,12 @@ int tas2781_save_calibration(struct tas2781_hda *hda)
status = -ENOMEM;
continue;
}
+ /*
+ * Set to an invalid value before the calibrated data
+ * is stored into it, for the default value is 0, which
+ * means the first device.
+ */
+ data[0] = 0xff;
/* Get variable contents into buffer */
status = efi.get_variable(efi_name[i], &efi_guid,
&attr, &cali_data->total_sz, data);
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index 49c80babb500..74c3cf1e45e1 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -393,19 +393,6 @@ static int tas2563_save_calibration(struct tas2781_hda *h)
r->pow_reg = TAS2563_CAL_POWER;
r->tlimit_reg = TAS2563_CAL_TLIM;
- /*
- * TAS2781_FMWLIB supports two solutions of calibrated data. One is
- * from the driver itself: driver reads the calibrated files directly
- * during probe; The other from user space: during init of audio hal,
- * the audio hal will pass the calibrated data via kcontrol interface.
- * Driver will store this data in "struct calidata" for use. For hda
- * device, calibrated data are usunally saved into UEFI. So Hda side
- * codec driver use the mixture of these two solutions, driver reads
- * the data from UEFI, then store this data in "struct calidata" for
- * use.
- */
- p->is_user_space_calidata = true;
-
return 0;
}
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
index 1e8e3d61291a..85324c7c796d 100644
--- a/sound/hda/controllers/intel.c
+++ b/sound/hda/controllers/intel.c
@@ -2551,11 +2551,12 @@ static const struct pci_device_id azx_ids[] = {
/* Wildcat Lake */
{ PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
/* Nova Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_NVL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
{ PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
/* Apollolake (Broxton-P) */
{ PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Gemini-Lake */
- { PCI_DEVICE_DATA(INTEL, HDA_GML, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GLK, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Haswell */
{ PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
{ PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c
index 0c25e87408de..f0a44fd111f3 100644
--- a/sound/hda/core/intel-dsp-config.c
+++ b/sound/hda/core/intel-dsp-config.c
@@ -154,7 +154,7 @@ static const struct config_entry config_table[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
{
.flags = FLAG_SOF,
- .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GLK,
.dmi_table = (const struct dmi_system_id []) {
{
.ident = "Google Chromebooks",
@@ -167,7 +167,7 @@ static const struct config_entry config_table[] = {
},
{
.flags = FLAG_SOF,
- .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GLK,
.codec_hid = &essx_83x6,
},
#endif
@@ -582,6 +582,10 @@ static const struct config_entry config_table[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOVALAKE)
{
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_NVL,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = PCI_DEVICE_ID_INTEL_HDA_NVL_S,
},
#endif
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 36e0d443ba0e..edfdcbf734fe 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -127,6 +127,7 @@ source "sound/soc/renesas/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/sdca/Kconfig"
+source "sound/soc/sophgo/Kconfig"
source "sound/soc/spacemit/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sprd/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8c0480e6484e..21d8406767fc 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += sdca/
obj-$(CONFIG_SND_SOC) += sof/
+obj-$(CONFIG_SND_SOC) += sophgo/
obj-$(CONFIG_SND_SOC) += spacemit/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sprd/
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index fd35a03aadcb..f7c3010624df 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -8,7 +8,6 @@ config SND_SOC_AMD_ACP
config SND_SOC_AMD_CZ_DA7219MX98357_MACH
tristate "AMD CZ support for DA7219, RT5682 and MAX9835"
- select CLK_FIXED_FCH
select SND_SOC_DA7219
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
@@ -45,7 +44,6 @@ config SND_SOC_AMD_ACP3x
config SND_SOC_AMD_RV_RT5682_MACH
tristate "AMD RV support for RT5682"
- select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
select SND_SOC_CROS_EC_CODEC
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index c2a60bc80ee6..977e4f2a7a70 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -15,7 +15,13 @@ config SND_SOC_AMD_ACP_COMMON
config SND_SOC_ACPI_AMD_MATCH
tristate
- select SND_SOC_ACPI if ACPI
+ select SND_SOC_ACPI_AMD_SDCA_QUIRKS if SND_SOC_SDCA
+ select SND_SOC_ACPI if ACPI
+
+config SND_SOC_ACPI_AMD_SDCA_QUIRKS
+ tristate
+ depends on ACPI
+ depends on SND_SOC_SDCA
if SND_SOC_AMD_ACP_COMMON
@@ -104,7 +110,6 @@ config SND_AMD_ASOC_ACP70
config SND_SOC_AMD_MACH_COMMON
tristate
depends on X86 && PCI && I2C
- select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
select SND_SOC_RT1019
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 08220b9a3802..81d23aded348 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -27,6 +27,7 @@ snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
+snd-soc-acpi-amd-sdca-quirks-y += soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
+obj-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += snd-soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
index fae94b9edd5a..9cb55d592c3c 100644
--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -358,8 +358,6 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
int sdw_be_num = 0, dmic_num = 0;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
- struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;
- struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;
struct snd_soc_aux_dev *soc_aux;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
@@ -380,12 +378,14 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
num_confs = num_ends;
/* One per DAI link, worst case is a DAI link for every endpoint */
- soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
+ struct asoc_sdw_dailink *soc_dais __free(kfree) =
+ kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
if (!soc_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
- soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
+ struct asoc_sdw_endpoint *soc_ends __free(kfree) =
+ kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
if (!soc_ends)
return -ENOMEM;
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
index 5677ae63fca9..da815b3f6389 100644
--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -270,8 +270,6 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
int sdw_be_num = 0, dmic_num = 0;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
- struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
- struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
struct snd_soc_aux_dev *sof_aux;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
@@ -289,12 +287,14 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
}
/* One per DAI link, worst case is a DAI link for every endpoint */
- sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
+ struct asoc_sdw_dailink *sof_dais __free(kfree) =
+ kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
if (!sof_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
- sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
+ struct asoc_sdw_endpoint *sof_ends __free(kfree) =
+ kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
if (!sof_ends)
return -ENOMEM;
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
index 871b4f054a84..dd2b010efdaa 100644
--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -7,6 +7,7 @@
*/
#include <sound/soc-acpi.h>
+#include "soc-acpi-amd-sdca-quirks.h"
#include "../mach-config.h"
static const struct snd_soc_acpi_endpoint single_endpoint = {
@@ -44,6 +45,39 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.group_id = 1
};
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp Endpoint, work as spk_l_endpoint */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+ },
+ /* DMIC Endpoint */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt712_vb_1_group1_adr[] = {
+ {
+ .adr = 0x000130025D071201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt712"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
{
.adr = 0x000030025D071101ull,
@@ -153,7 +187,106 @@ static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
},
};
-static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
+static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = {
+ {
+ .adr = 0x00013301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00013101FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00013001FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x2_l0u01_adr[] = {
+ {
+ .adr = 0x00003001FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003101FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x2_l1u01_adr[] = {
+ {
+ .adr = 0x00013001FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013101FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x2_l1u13_adr[] = {
+ {
+ .adr = 0x00013101FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00013301FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63x4_l0u0246_adr[] = {
+ {
+ .adr = 0x00003001FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP2"
+ },
+ {
+ .adr = 0x00003401FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP3"
+ },
+ {
+ .adr = 0x00003601FA356301ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP4"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = {
{
.adr = 0x00003001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
@@ -162,7 +295,25 @@ static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
}
};
-static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = {
+static const struct snd_soc_acpi_adr_device cs42l43_l0u1_adr[] = {
+ {
+ .adr = 0x00003101FA424301ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43b_l0u1_adr[] = {
+ {
+ .adr = 0x00003101FA2A3B01ull,
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] = {
{
.adr = 0x00013001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
@@ -195,61 +346,164 @@ static const struct snd_soc_acpi_adr_device cs42l43_1_cs35l56x4_1_adr[] = {
},
};
-static const struct snd_soc_acpi_adr_device cs35l56x4_1_adr[] = {
+static const struct snd_soc_acpi_adr_device cs42l45_l0u0_adr[] = {
{
- .adr = 0x00013301FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_l_endpoint,
- .name_prefix = "AMP1"
+ .adr = 0x00003001FA424501ull,
+ /* Re-use endpoints, but cs42l45 has no speaker */
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l45"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs42l45_l1u0_adr[] = {
+ {
+ .adr = 0x00013001FA424501ull,
+ /* Re-use endpoints, but cs42l45 has no speaker */
+ .num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
+ .endpoints = cs42l43_endpoints,
+ .name_prefix = "cs42l45"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
+ .adr_d = cs35l56x4_l1u3210_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs35l63x4_l0u0246[] = {
{
- .adr = 0x00013201FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_r_endpoint,
- .name_prefix = "AMP2"
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
+ .adr_d = cs35l63x4_l0u0246_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u1[] = {
{
- .adr = 0x00013101FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_2_endpoint,
- .name_prefix = "AMP3"
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_l0u1_adr),
+ .adr_d = cs42l43_l0u1_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43b_l0u1[] = {
{
- .adr = 0x00013001FA355601ull,
- .num_endpoints = 1,
- .endpoints = &spk_3_endpoint,
- .name_prefix = "AMP4"
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43b_l0u1_adr),
+ .adr_d = cs42l43b_l0u1_adr,
},
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u0_cs35l56x4_l1u3210[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l43_l0u0_adr),
+ .adr_d = cs42l43_l0u0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
+ .adr_d = cs35l56x4_l1u3210_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr),
+ .adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr,
+ },
+ {}
};
-static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1_cs35l56x4_l1[] = {
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
+ .adr_d = cs42l45_l0u0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u01[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
+ .adr_d = cs42l45_l0u0_adr,
+ },
{
.mask = BIT(1),
- .num_adr = ARRAY_SIZE(cs42l43_1_cs35l56x4_1_adr),
- .adr_d = cs42l43_1_cs35l56x4_1_adr,
+ .num_adr = ARRAY_SIZE(cs35l63x2_l1u01_adr),
+ .adr_d = cs35l63x2_l1u01_adr,
},
{}
};
-static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0_cs35l56x4_l1[] = {
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u13[] = {
{
.mask = BIT(0),
- .num_adr = ARRAY_SIZE(cs42l43_0_adr),
- .adr_d = cs42l43_0_adr,
+ .num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
+ .adr_d = cs42l45_l0u0_adr,
},
{
.mask = BIT(1),
- .num_adr = ARRAY_SIZE(cs35l56x4_1_adr),
- .adr_d = cs35l56x4_1_adr,
+ .num_adr = ARRAY_SIZE(cs35l63x2_l1u13_adr),
+ .adr_d = cs35l63x2_l1u13_adr,
},
{}
};
-static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1[] = {
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0[] = {
{
.mask = BIT(1),
- .num_adr = ARRAY_SIZE(cs35l56x4_1_adr),
- .adr_d = cs35l56x4_1_adr,
+ .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
+ .adr_d = cs42l45_l1u0_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x2_l0u01[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
+ .adr_d = cs42l45_l1u0_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l63x2_l0u01_adr),
+ .adr_d = cs35l63x2_l0u01_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x4_l0u0246[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
+ .adr_d = cs42l45_l1u0_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
+ .adr_d = cs35l63x4_l0u0246_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_alc712_vb_l1[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr),
+ .adr_d = rt712_vb_1_group1_adr,
},
{}
};
@@ -284,28 +538,79 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
.drv_name = "amd_sdw",
},
{
- .link_mask = BIT(0),
- .links = acp70_rt722_only,
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_4_in_1_sdca,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
- .links = acp70_4_in_1_sdca,
+ .links = acp70_cs42l43_l0u0_cs35l56x4_l1u3210,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l45_l1u0_cs35l63x4_l0u0246,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l45_l0u0_cs35l63x2_l1u01,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
- .links = acp70_cs42l43_l0_cs35l56x4_l1,
+ .links = acp70_cs42l45_l0u0_cs35l63x2_l1u13,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_cs42l45_l1u0_cs35l63x2_l0u01,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(1),
+ .links = acp70_cs42l43_l1u0_cs35l56x4_l1u0123,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(1),
+ .links = acp70_cs35l56x4_l1u3210,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs35l63x4_l0u0246,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs42l43_l0u1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs42l43b_l0u1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_cs42l45_l0u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
- .links = acp70_cs42l43_l1_cs35l56x4_l1,
+ .links = acp70_cs42l45_l1u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
- .links = acp70_cs35l56x4_l1,
+ .links = acp70_alc712_vb_l1,
+ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb,
.drv_name = "amd_sdw",
},
{},
@@ -327,3 +632,6 @@ EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines);
MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
+MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS");
+#endif
diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
new file mode 100644
index 000000000000..63bf9e3c0ae1
--- /dev/null
+++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-acpi-amd-sdca-quirks.c - tables and support for SDCA quirks
+ *
+ * Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ */
+
+#include <linux/soundwire/sdw_amd.h>
+#include <sound/sdca.h>
+#include <sound/soc-acpi.h>
+#include "soc-acpi-amd-sdca-quirks.h"
+
+/*
+ * Pretend machine quirk. The argument type is not the traditional
+ * 'struct snd_soc_acpi_mach' pointer but instead the sdw_amd_ctx
+ * which contains the peripheral information required for the
+ * SoundWire/SDCA filter on the SMART_MIC setup and interface
+ * revision. When the return value is false, the entry in the
+ * 'snd_soc_acpi_mach' table needs to be skipped.
+ */
+bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
+{
+ struct sdw_amd_ctx *ctx = arg;
+ int i;
+
+ if (!ctx)
+ return false;
+
+ for (i = 0; i < ctx->peripherals->num_peripherals; i++) {
+ if (sdca_device_quirk_match(ctx->peripherals->array[i],
+ SDCA_QUIRKS_RT712_VB))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_amd_sdca_is_device_rt712_vb, "SND_SOC_ACPI_AMD_SDCA_QUIRKS");
+
+MODULE_DESCRIPTION("ASoC ACPI AMD SDCA quirks");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h
new file mode 100644
index 000000000000..0e644e71e76f
--- /dev/null
+++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-acpi-amd-sdca-quirks.h - tables and support for SDCA quirks
+ *
+ * Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ */
+
+#ifndef _SND_SOC_ACPI_AMD_SDCA_QUIRKS
+#define _SND_SOC_ACPI_AMD_SDCA_QUIRKS
+
+#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
+
+bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg);
+
+#else
+
+static inline bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
+{
+ return false;
+}
+
+#endif
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061791e61907..adb3fb923be3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -46,7 +46,6 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_AK4554
imply SND_SOC_AK4613
imply SND_SOC_AK4619
- imply SND_SOC_AK4641
imply SND_SOC_AK4642
imply SND_SOC_AK4671
imply SND_SOC_AK5386
@@ -212,6 +211,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT1305
imply SND_SOC_RT1308
imply SND_SOC_RT5514
+ imply SND_SOC_RT5575
imply SND_SOC_RT5616
imply SND_SOC_RT5631
imply SND_SOC_RT5640
@@ -624,11 +624,6 @@ config SND_SOC_AK4619
tristate "AKM AK4619 CODEC"
depends on I2C
-config SND_SOC_AK4641
- tristate
- depends on I2C
- depends on GPIOLIB_LEGACY
-
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
depends on I2C
@@ -785,9 +780,14 @@ config SND_SOC_CROS_EC_CODEC
config SND_SOC_CS_AMP_LIB
tristate
+config SND_SOC_CS_AMP_LIB_TEST_HOOKS
+ bool
+ depends on SND_SOC_CS_AMP_LIB
+
config SND_SOC_CS_AMP_LIB_TEST
tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS
depends on SND_SOC_CS_AMP_LIB && KUNIT
+ select SND_SOC_CS_AMP_LIB_TEST_HOOKS
default KUNIT_ALL_TESTS
help
This builds KUnit tests for the Cirrus Logic common
@@ -927,6 +927,33 @@ config SND_SOC_CS35L56_CAL_SET_CTRL
On most platforms this is not needed.
If unsure select "N".
+
+config SND_SOC_CS35L56_TEST
+ tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS35L56 && KUNIT
+ default KUNIT_ALL_TESTS
+ select SND_SOC_CS_AMP_LIB_TEST_HOOKS
+ help
+ This builds KUnit tests for the Cirrus Logic cs35l56
+ codec driver.
+
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
+config SND_SOC_CS35L56_SHARED_TEST
+ tristate "KUnit test for Cirrus Logic cs35l56-shared" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS35L56_SHARED && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the Cirrus Logic cs35l56-shared
+ module.
+
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
endmenu
config SND_SOC_CS40L50
@@ -1783,6 +1810,16 @@ config SND_SOC_RT5514_SPI_BUILTIN
bool # force RT5514_SPI to be built-in to avoid link errors
default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m
+config SND_SOC_RT5575
+ tristate "Realtek ALC5575 Codec - I2C"
+ depends on I2C
+
+config SND_SOC_RT5575_SPI
+ bool "Realtek ALC5575 Codec - SPI"
+ depends on SPI_MASTER && I2C
+ depends on SND_SOC_RT5575
+ depends on SPI_MASTER=y || SND_SOC_RT5575=m
+
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
depends on I2C
@@ -2146,6 +2183,7 @@ config SND_SOC_TAS2781_I2C
config SND_SOC_TAS2783_SDW
tristate "Texas Instruments TAS2783 speaker amplifier (sdw)"
depends on SOUNDWIRE
+ depends on SND_SOC_SDCA
depends on EFI
select REGMAP_SOUNDWIRE
select REGMAP_SOUNDWIRE_MBQ
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d687d4f74363..3ddee5298721 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -40,7 +40,6 @@ snd-soc-ak4535-y := ak4535.o
snd-soc-ak4554-y := ak4554.o
snd-soc-ak4613-y := ak4613.o
snd-soc-ak4619-y := ak4619.o
-snd-soc-ak4641-y := ak4641.o
snd-soc-ak4642-y := ak4642.o
snd-soc-ak4671-y := ak4671.o
snd-soc-ak5386-y := ak5386.o
@@ -78,9 +77,11 @@ snd-soc-cs35l45-spi-y := cs35l45-spi.o
snd-soc-cs35l45-i2c-y := cs35l45-i2c.o
snd-soc-cs35l56-y := cs35l56.o
snd-soc-cs35l56-shared-y := cs35l56-shared.o
+snd-soc-cs35l56-shared-test-y := cs35l56-shared-test.o
snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
snd-soc-cs35l56-spi-y := cs35l56-spi.o
snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
+snd-soc-cs35l56-test-y := cs35l56-test.o
snd-soc-cs40l50-y := cs40l50-codec.o
snd-soc-cs42l42-y := cs42l42.o
snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
@@ -253,6 +254,8 @@ snd-soc-rt286-y := rt286.o
snd-soc-rt298-y := rt298.o
snd-soc-rt5514-y := rt5514.o
snd-soc-rt5514-spi-y := rt5514-spi.o
+snd-soc-rt5575-y := rt5575.o
+snd-soc-rt5575-$(CONFIG_SND_SOC_RT5575_SPI) += rt5575-spi.o
snd-soc-rt5616-y := rt5616.o
snd-soc-rt5631-y := rt5631.o
snd-soc-rt5640-y := rt5640.o
@@ -472,7 +475,6 @@ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o
obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o
-obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
@@ -511,9 +513,11 @@ obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o
obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
+obj-$(CONFIG_SND_SOC_CS35L56_SHARED_TEST) += snd-soc-cs35l56-shared-test.o
obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o
obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o
obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o
+obj-$(CONFIG_SND_SOC_CS35L56_TEST) += snd-soc-cs35l56-test.o
obj-$(CONFIG_SND_SOC_CS40L50) += snd-soc-cs40l50.o
obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
@@ -686,6 +690,7 @@ obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o
+obj-$(CONFIG_SND_SOC_RT5575) += snd-soc-rt5575.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
deleted file mode 100644
index 9db8cdb26d33..000000000000
--- a/sound/soc/codecs/ak4641.c
+++ /dev/null
@@ -1,641 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ak4641.c -- AK4641 ALSA Soc Audio driver
- *
- * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
- * Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
- *
- * Based on ak4535.c by Richard Purdie
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/pm.h>
-#include <linux/i2c.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/ak4641.h>
-
-/* AK4641 register space */
-#define AK4641_PM1 0x00
-#define AK4641_PM2 0x01
-#define AK4641_SIG1 0x02
-#define AK4641_SIG2 0x03
-#define AK4641_MODE1 0x04
-#define AK4641_MODE2 0x05
-#define AK4641_DAC 0x06
-#define AK4641_MIC 0x07
-#define AK4641_TIMER 0x08
-#define AK4641_ALC1 0x09
-#define AK4641_ALC2 0x0a
-#define AK4641_PGA 0x0b
-#define AK4641_LATT 0x0c
-#define AK4641_RATT 0x0d
-#define AK4641_VOL 0x0e
-#define AK4641_STATUS 0x0f
-#define AK4641_EQLO 0x10
-#define AK4641_EQMID 0x11
-#define AK4641_EQHI 0x12
-#define AK4641_BTIF 0x13
-
-/* codec private data */
-struct ak4641_priv {
- struct regmap *regmap;
- unsigned int sysclk;
- int deemph;
- int playback_fs;
-};
-
-/*
- * ak4641 register cache
- */
-static const struct reg_default ak4641_reg_defaults[] = {
- { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 },
- { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 },
- { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 },
- { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 },
- { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 }
-};
-
-static const int deemph_settings[] = {44100, 0, 48000, 32000};
-
-static int ak4641_set_deemph(struct snd_soc_component *component)
-{
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- int i, best = 0;
-
- for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
- /* if deemphasis is on, select the nearest available rate */
- if (ak4641->deemph && deemph_settings[i] != 0 &&
- abs(deemph_settings[i] - ak4641->playback_fs) <
- abs(deemph_settings[best] - ak4641->playback_fs))
- best = i;
-
- if (!ak4641->deemph && deemph_settings[i] == 0)
- best = i;
- }
-
- dev_dbg(component->dev, "Set deemphasis %d\n", best);
-
- return snd_soc_component_update_bits(component, AK4641_DAC, 0x3, best);
-}
-
-static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- int deemph = ucontrol->value.integer.value[0];
-
- if (deemph > 1)
- return -EINVAL;
-
- ak4641->deemph = deemph;
-
- return ak4641_set_deemph(component);
-}
-
-static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
-
- ucontrol->value.integer.value[0] = ak4641->deemph;
- return 0;
-};
-
-static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
-static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
-static const char *ak4641_mic_select[] = {"Internal", "External"};
-static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
-
-
-static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
-static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
-static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
-static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
-static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
-static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
-static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
-static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
-static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
-
-
-static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum,
- AK4641_SIG1, 6, ak4641_mono_out);
-static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum,
- AK4641_MODE2, 2, ak4641_hp_out);
-static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum,
- AK4641_MIC, 1, ak4641_mic_select);
-static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum,
- AK4641_BTIF, 4, ak4641_mic_or_dac);
-
-static const struct snd_kcontrol_new ak4641_snd_controls[] = {
- SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
- SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
- mono_gain_tlv),
- SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
- SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
- ak4641_get_deemph, ak4641_put_deemph),
-
- SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
-
- SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
- SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
- SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
-
- SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
-
- SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
- SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
- SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
-
- SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
-
- SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
- AK4641_RATT, 0, 255, 1, master_tlv),
-
- SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
-
- SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
- SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
- SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
-};
-
-/* Mono 1 Mixer */
-static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
- SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
- mic_mono_sidetone_tlv),
- SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
- SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
-};
-
-/* Stereo Mixer */
-static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
- SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
- mic_stereo_sidetone_tlv),
- SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
- SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
-};
-
-/* Input Mixer */
-static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
- SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
- SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
-};
-
-/* Mic mux */
-static const struct snd_kcontrol_new ak4641_mic_mux_control =
- SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
-
-/* Input mux */
-static const struct snd_kcontrol_new ak4641_input_mux_control =
- SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
-
-/* mono 2 switch */
-static const struct snd_kcontrol_new ak4641_mono2_control =
- SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
-
-/* ak4641 dapm widgets */
-static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
- SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
- &ak4641_stereo_mixer_controls[0],
- ARRAY_SIZE(ak4641_stereo_mixer_controls)),
- SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
- &ak4641_mono1_mixer_controls[0],
- ARRAY_SIZE(ak4641_mono1_mixer_controls)),
- SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
- &ak4641_input_mixer_controls[0],
- ARRAY_SIZE(ak4641_input_mixer_controls)),
- SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
- &ak4641_mic_mux_control),
- SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
- &ak4641_input_mux_control),
- SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
- &ak4641_mono2_control),
-
- SND_SOC_DAPM_OUTPUT("LOUT"),
- SND_SOC_DAPM_OUTPUT("ROUT"),
- SND_SOC_DAPM_OUTPUT("MOUT1"),
- SND_SOC_DAPM_OUTPUT("MOUT2"),
- SND_SOC_DAPM_OUTPUT("MICOUT"),
-
- SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
- SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
-
- SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
- SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
-
- SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
- SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
-
- SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
- SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
-
- SND_SOC_DAPM_INPUT("MICIN"),
- SND_SOC_DAPM_INPUT("MICEXT"),
- SND_SOC_DAPM_INPUT("AUX"),
- SND_SOC_DAPM_INPUT("AIN"),
-};
-
-static const struct snd_soc_dapm_route ak4641_audio_map[] = {
- /* Stereo Mixer */
- {"Stereo Mixer", "Playback Switch", "DAC"},
- {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
- {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
-
- /* Mono 1 Mixer */
- {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
- {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
-
- /* Mic */
- {"Mic", NULL, "AIN"},
- {"Mic Mux", "Internal", "Mic Int Bias"},
- {"Mic Mux", "External", "Mic Ext Bias"},
- {"Mic Int Bias", NULL, "MICIN"},
- {"Mic Ext Bias", NULL, "MICEXT"},
- {"MICOUT", NULL, "Mic Mux"},
-
- /* Input Mux */
- {"Input Mux", "Microphone", "Mic"},
- {"Input Mux", "Voice DAC", "Voice DAC"},
-
- /* Line Out */
- {"LOUT", NULL, "Line Out"},
- {"ROUT", NULL, "Line Out"},
- {"Line Out", NULL, "Stereo Mixer"},
-
- /* Mono 1 Out */
- {"MOUT1", NULL, "Mono Out"},
- {"Mono Out", NULL, "Mono1 Mixer"},
-
- /* Mono 2 Out */
- {"MOUT2", NULL, "Mono 2 Enable"},
- {"Mono 2 Enable", "Switch", "Mono Out 2"},
- {"Mono Out 2", NULL, "Stereo Mixer"},
-
- {"Voice ADC", NULL, "Mono 2 Enable"},
-
- /* Aux In */
- {"AUX In", NULL, "AUX"},
-
- /* ADC */
- {"ADC", NULL, "Input Mixer"},
- {"Input Mixer", "Mic Capture Switch", "Mic"},
- {"Input Mixer", "Aux Capture Switch", "AUX In"},
-};
-
-static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_component *component = codec_dai->component;
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
-
- ak4641->sysclk = freq;
- return 0;
-}
-
-static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- int rate = params_rate(params), fs = 256;
- u8 mode2;
-
- if (rate)
- fs = ak4641->sysclk / rate;
- else
- return -EINVAL;
-
- /* set fs */
- switch (fs) {
- case 1024:
- mode2 = (0x2 << 5);
- break;
- case 512:
- mode2 = (0x1 << 5);
- break;
- case 256:
- mode2 = (0x0 << 5);
- break;
- default:
- dev_err(component->dev, "Error: unsupported fs=%d\n", fs);
- return -EINVAL;
- }
-
- snd_soc_component_update_bits(component, AK4641_MODE2, (0x3 << 5), mode2);
-
- /* Update de-emphasis filter for the new rate */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ak4641->playback_fs = rate;
- ak4641_set_deemph(component);
- }
-
- return 0;
-}
-
-static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
-{
- struct snd_soc_component *component = codec_dai->component;
- u8 btif;
- int ret;
-
- /* interface format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- btif = (0x3 << 5);
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- btif = (0x2 << 5);
- break;
- case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
- btif = (0x0 << 5);
- break;
- case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
- btif = (0x1 << 5);
- break;
- default:
- return -EINVAL;
- }
-
- ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
-{
- struct snd_soc_component *component = codec_dai->component;
- u8 mode1 = 0;
-
- /* interface format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- mode1 = 0x02;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- mode1 = 0x01;
- break;
- default:
- return -EINVAL;
- }
-
- return snd_soc_component_write(component, AK4641_MODE1, mode1);
-}
-
-static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction)
-{
- struct snd_soc_component *component = dai->component;
-
- return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0);
-}
-
-static int ak4641_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- struct ak4641_platform_data *pdata = component->dev->platform_data;
- int ret;
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- /* unmute */
- snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0);
- break;
- case SND_SOC_BIAS_PREPARE:
- /* mute */
- snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20);
- break;
- case SND_SOC_BIAS_STANDBY:
- if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 1);
- mdelay(1);
- if (pdata && gpio_is_valid(pdata->gpio_npdn))
- gpio_set_value(pdata->gpio_npdn, 1);
- mdelay(1);
-
- ret = regcache_sync(ak4641->regmap);
- if (ret) {
- dev_err(component->dev,
- "Failed to sync cache: %d\n", ret);
- return ret;
- }
- }
- snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0x80);
- snd_soc_component_update_bits(component, AK4641_PM2, 0x80, 0);
- break;
- case SND_SOC_BIAS_OFF:
- snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0);
- if (pdata && gpio_is_valid(pdata->gpio_npdn))
- gpio_set_value(pdata->gpio_npdn, 0);
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 0);
- regcache_mark_dirty(ak4641->regmap);
- break;
- }
- return 0;
-}
-
-#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
-#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_16000)
-#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
-
-static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
- .hw_params = ak4641_i2s_hw_params,
- .set_fmt = ak4641_i2s_set_dai_fmt,
- .mute_stream = ak4641_mute,
- .set_sysclk = ak4641_set_dai_sysclk,
- .no_capture_mute = 1,
-};
-
-static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
- .hw_params = NULL, /* rates are controlled by BT chip */
- .set_fmt = ak4641_pcm_set_dai_fmt,
- .mute_stream = ak4641_mute,
- .set_sysclk = ak4641_set_dai_sysclk,
- .no_capture_mute = 1,
-};
-
-static struct snd_soc_dai_driver ak4641_dai[] = {
-{
- .name = "ak4641-hifi",
- .id = 1,
- .playback = {
- .stream_name = "HiFi Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AK4641_RATES,
- .formats = AK4641_FORMATS,
- },
- .capture = {
- .stream_name = "HiFi Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AK4641_RATES,
- .formats = AK4641_FORMATS,
- },
- .ops = &ak4641_i2s_dai_ops,
- .symmetric_rate = 1,
-},
-{
- .name = "ak4641-voice",
- .id = 1,
- .playback = {
- .stream_name = "Voice Playback",
- .channels_min = 1,
- .channels_max = 1,
- .rates = AK4641_RATES_BT,
- .formats = AK4641_FORMATS,
- },
- .capture = {
- .stream_name = "Voice Capture",
- .channels_min = 1,
- .channels_max = 1,
- .rates = AK4641_RATES_BT,
- .formats = AK4641_FORMATS,
- },
- .ops = &ak4641_pcm_dai_ops,
- .symmetric_rate = 1,
-},
-};
-
-static const struct snd_soc_component_driver soc_component_dev_ak4641 = {
- .controls = ak4641_snd_controls,
- .num_controls = ARRAY_SIZE(ak4641_snd_controls),
- .dapm_widgets = ak4641_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
- .dapm_routes = ak4641_audio_map,
- .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
- .set_bias_level = ak4641_set_bias_level,
- .suspend_bias_off = 1,
- .idle_bias_on = 1,
- .use_pmdown_time = 1,
- .endianness = 1,
-};
-
-static const struct regmap_config ak4641_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = AK4641_BTIF,
- .reg_defaults = ak4641_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-};
-
-static int ak4641_i2c_probe(struct i2c_client *i2c)
-{
- struct ak4641_platform_data *pdata = i2c->dev.platform_data;
- struct ak4641_priv *ak4641;
- int ret;
-
- ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv),
- GFP_KERNEL);
- if (!ak4641)
- return -ENOMEM;
-
- ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap);
- if (IS_ERR(ak4641->regmap))
- return PTR_ERR(ak4641->regmap);
-
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power)) {
- ret = gpio_request_one(pdata->gpio_power,
- GPIOF_OUT_INIT_LOW, "ak4641 power");
- if (ret)
- goto err_out;
- }
- if (gpio_is_valid(pdata->gpio_npdn)) {
- ret = gpio_request_one(pdata->gpio_npdn,
- GPIOF_OUT_INIT_LOW, "ak4641 npdn");
- if (ret)
- goto err_gpio;
-
- udelay(1); /* > 150 ns */
- gpio_set_value(pdata->gpio_npdn, 1);
- }
- }
-
- i2c_set_clientdata(i2c, ak4641);
-
- ret = devm_snd_soc_register_component(&i2c->dev,
- &soc_component_dev_ak4641,
- ak4641_dai, ARRAY_SIZE(ak4641_dai));
- if (ret != 0)
- goto err_gpio2;
-
- return 0;
-
-err_gpio2:
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power))
- gpio_set_value(pdata->gpio_power, 0);
- if (gpio_is_valid(pdata->gpio_npdn))
- gpio_free(pdata->gpio_npdn);
- }
-err_gpio:
- if (pdata && gpio_is_valid(pdata->gpio_power))
- gpio_free(pdata->gpio_power);
-err_out:
- return ret;
-}
-
-static void ak4641_i2c_remove(struct i2c_client *i2c)
-{
- struct ak4641_platform_data *pdata = i2c->dev.platform_data;
-
- if (pdata) {
- if (gpio_is_valid(pdata->gpio_power)) {
- gpio_set_value(pdata->gpio_power, 0);
- gpio_free(pdata->gpio_power);
- }
- if (gpio_is_valid(pdata->gpio_npdn))
- gpio_free(pdata->gpio_npdn);
- }
-}
-
-static const struct i2c_device_id ak4641_i2c_id[] = {
- { "ak4641" },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
-
-static struct i2c_driver ak4641_i2c_driver = {
- .driver = {
- .name = "ak4641",
- },
- .probe = ak4641_i2c_probe,
- .remove = ak4641_i2c_remove,
- .id_table = ak4641_i2c_id,
-};
-
-module_i2c_driver(ak4641_i2c_driver);
-
-MODULE_DESCRIPTION("SoC AK4641 driver");
-MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index a9946dcdc9f6..ec229b315f9f 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -678,14 +678,9 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
- case SND_SOC_DAIFMT_IB_IF:
- iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
- break;
case SND_SOC_DAIFMT_IB_NF:
iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
break;
- case SND_SOC_DAIFMT_NB_IF:
- break;
default:
return -EINVAL;
}
diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c
index d7fd865c349f..613daccca3af 100644
--- a/sound/soc/codecs/aw87390.c
+++ b/sound/soc/codecs/aw87390.c
@@ -314,6 +314,45 @@ static int aw87390_drv_event(struct snd_soc_dapm_widget *w,
return ret;
}
+static int aw87391_rgds_drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw87390->aw_pa;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!IS_ERR(aw87390->vdd_reg)) {
+ if (regulator_enable(aw87390->vdd_reg))
+ dev_warn(aw_dev->dev, "Failed to enable vdd\n");
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+ AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP |
+ AW87391_REG_EN_2X | AW87391_EN_SPK |
+ AW87391_EN_PA | AW87391_REG_EN_CP |
+ AW87391_EN_SW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG,
+ AW87390_POWER_DOWN_VALUE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!IS_ERR(aw87390->vdd_reg)) {
+ if (regulator_disable(aw87390->vdd_reg))
+ dev_warn(aw_dev->dev, "Failed to disable vdd\n");
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN"),
SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event,
@@ -321,6 +360,14 @@ static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("OUT"),
};
+static const struct snd_soc_dapm_widget aw87391_rgds_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87391_rgds_drv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
static const struct snd_soc_dapm_route aw87390_dapm_routes[] = {
{ "SPK PA", NULL, "IN" },
{ "OUT", NULL, "SPK PA" },
@@ -339,6 +386,80 @@ static int aw87390_codec_probe(struct snd_soc_component *component)
return 0;
}
+/*
+ * Firmware typically is used to load the sequence of init commands,
+ * however for the Anbernic RG-DS we don't have a firmware file just
+ * a list of registers and values. Most of these values are undocumented
+ * in the AW87391 datasheet.
+ */
+static void aw87391_rgds_codec_init(struct aw87390 *aw87390)
+{
+ struct aw_device *aw_dev = aw87390->aw_pa;
+
+ /* Undocumented command per datasheet. */
+ regmap_write(aw_dev->regmap, 0x64, 0x3a);
+
+ /* Bits 7:4 are undocumented but provided by manufacturer. */
+ regmap_write(aw_dev->regmap, AW87391_CP_REG,
+ (5 << 4) | AW87391_REG_CP_OVP_8_50V);
+
+ regmap_write(aw_dev->regmap, AW87391_AGCPO_REG,
+ AW87391_AK1_S_016 | AW87391_AGC2PO_MW(500));
+
+ regmap_write(aw_dev->regmap, AW87391_AGC2PA_REG,
+ AW87391_RK_S_20_48 | AW87391_AK2_S_41 | AW87391_AK2F_S_41);
+
+ /* Undocumented commands per datasheet. */
+ regmap_write(aw_dev->regmap, 0x5d, 0x00);
+ regmap_write(aw_dev->regmap, 0x5e, 0xb4);
+ regmap_write(aw_dev->regmap, 0x5f, 0x30);
+ regmap_write(aw_dev->regmap, 0x60, 0x39);
+ regmap_write(aw_dev->regmap, 0x61, 0x10);
+ regmap_write(aw_dev->regmap, 0x62, 0x03);
+ regmap_write(aw_dev->regmap, 0x63, 0x7d);
+ regmap_write(aw_dev->regmap, 0x65, 0xa0);
+ regmap_write(aw_dev->regmap, 0x66, 0x21);
+ regmap_write(aw_dev->regmap, 0x67, 0x41);
+ regmap_write(aw_dev->regmap, 0x68, 0x3b);
+ regmap_write(aw_dev->regmap, 0x6e, 0x00);
+ regmap_write(aw_dev->regmap, 0x6f, 0x00);
+ regmap_write(aw_dev->regmap, 0x70, 0x00);
+ regmap_write(aw_dev->regmap, 0x71, 0x00);
+ regmap_write(aw_dev->regmap, 0x72, 0x34);
+ regmap_write(aw_dev->regmap, 0x73, 0x06);
+ regmap_write(aw_dev->regmap, 0x74, 0x10);
+ regmap_write(aw_dev->regmap, 0x75, 0x00);
+ regmap_write(aw_dev->regmap, 0x7a, 0x00);
+ regmap_write(aw_dev->regmap, 0x7b, 0x00);
+ regmap_write(aw_dev->regmap, 0x7c, 0x00);
+ regmap_write(aw_dev->regmap, 0x7d, 0x00);
+
+ regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_12DB);
+ regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+ AW87391_EN_PA | AW87391_REG_EN_CP | AW87391_EN_SW);
+ regmap_write(aw_dev->regmap, AW87391_SYSCTRL_REG,
+ AW87391_REG_VER_SEL_LOW | AW87391_REG_EN_ADAP |
+ AW87391_REG_EN_2X | AW87391_EN_SPK | AW87391_EN_PA |
+ AW87391_REG_EN_CP | AW87391_EN_SW);
+ regmap_write(aw_dev->regmap, AW87391_PAG_REG, AW87391_GAIN_15DB);
+}
+
+static int aw87391_rgds_codec_probe(struct snd_soc_component *component)
+{
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+
+ aw87390->vdd_reg = devm_regulator_get_optional(aw87390->aw_pa->dev,
+ "vdd");
+ if (IS_ERR(aw87390->vdd_reg) && PTR_ERR(aw87390->vdd_reg) != -ENODEV)
+ return dev_err_probe(aw87390->aw_pa->dev,
+ PTR_ERR(aw87390->vdd_reg),
+ "Could not get vdd regulator\n");
+
+ aw87391_rgds_codec_init(aw87390);
+
+ return 0;
+}
+
static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
.probe = aw87390_codec_probe,
.dapm_widgets = aw87390_dapm_widgets,
@@ -349,6 +470,14 @@ static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
.num_controls = ARRAY_SIZE(aw87390_controls),
};
+static const struct snd_soc_component_driver soc_codec_dev_anbernic_rgds = {
+ .probe = aw87391_rgds_codec_probe,
+ .dapm_widgets = aw87391_rgds_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw87391_rgds_dapm_widgets),
+ .dapm_routes = aw87390_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes),
+};
+
static void aw87390_parse_channel_dt(struct aw87390 *aw87390)
{
struct aw_device *aw_dev = aw87390->aw_pa;
@@ -366,6 +495,10 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
unsigned int chip_id;
int ret;
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
/* read chip id */
ret = regmap_read(regmap, AW87390_ID_REG, &chip_id);
if (ret) {
@@ -373,22 +506,24 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
return ret;
}
- if (chip_id != AW87390_CHIP_ID) {
+ switch (chip_id) {
+ case AW87390_CHIP_ID:
+ aw_dev->chip_id = AW87390_CHIP_ID;
+ break;
+ case AW87391_CHIP_ID:
+ aw_dev->chip_id = AW87391_CHIP_ID;
+ break;
+ default:
dev_err(&i2c->dev, "unsupported device\n");
return -ENXIO;
}
dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
- aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
- if (!aw_dev)
- return -ENOMEM;
-
aw87390->aw_pa = aw_dev;
aw_dev->i2c = i2c;
aw_dev->regmap = regmap;
aw_dev->dev = &i2c->dev;
- aw_dev->chip_id = AW87390_CHIP_ID;
aw_dev->acf = NULL;
aw_dev->prof_info.prof_desc = NULL;
aw_dev->prof_info.count = 0;
@@ -406,6 +541,7 @@ static int aw87390_init(struct aw87390 *aw87390, struct i2c_client *i2c, struct
static int aw87390_i2c_probe(struct i2c_client *i2c)
{
struct aw87390 *aw87390;
+ const struct snd_soc_component_driver *priv;
int ret;
ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
@@ -434,16 +570,38 @@ static int aw87390_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
- ret = devm_snd_soc_register_component(&i2c->dev,
- &soc_codec_dev_aw87390, NULL, 0);
+ switch (aw87390->aw_pa->chip_id) {
+ case AW87390_CHIP_ID:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw87390, NULL, 0);
+ break;
+ case AW87391_CHIP_ID:
+ priv = of_device_get_match_data(&i2c->dev);
+ if (!priv)
+ return dev_err_probe(&i2c->dev, -EINVAL,
+ "aw87391 not currently supported\n");
+ ret = devm_snd_soc_register_component(&i2c->dev, priv, NULL, 0);
+ break;
+ default:
+ return -ENXIO;
+ }
+
if (ret)
dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret);
return ret;
}
+static const struct of_device_id aw87390_of_match[] = {
+ { .compatible = "awinic,aw87390" },
+ { .compatible = "anbernic,rgds-amp", .data = &soc_codec_dev_anbernic_rgds },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aw87390_of_match);
+
static const struct i2c_device_id aw87390_i2c_id[] = {
{ AW87390_I2C_NAME },
+ { AW87391_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
@@ -451,6 +609,7 @@ MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
static struct i2c_driver aw87390_i2c_driver = {
.driver = {
.name = AW87390_I2C_NAME,
+ .of_match_table = of_match_ptr(aw87390_of_match),
},
.probe = aw87390_i2c_probe,
.id_table = aw87390_i2c_id,
diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h
index d0d049e65991..f48b207e4bb4 100644
--- a/sound/soc/codecs/aw87390.h
+++ b/sound/soc/codecs/aw87390.h
@@ -52,6 +52,90 @@
#define AW87390_I2C_NAME "aw87390"
#define AW87390_ACF_FILE "aw87390_acf.bin"
+#define AW87391_SYSCTRL_REG (0x01)
+#define AW87391_REG_VER_SEL_LOW (0 << 6)
+#define AW87391_REG_VER_SEL_NORMAL (1 << 6)
+#define AW87391_REG_VER_SEL_SUPER (2 << 6)
+#define AW87391_REG_EN_ADAP BIT(5)
+#define AW87391_REG_EN_2X BIT(4)
+#define AW87391_EN_SPK BIT(3)
+#define AW87391_EN_PA BIT(2)
+#define AW87391_REG_EN_CP BIT(1)
+#define AW87391_EN_SW BIT(0)
+
+#define AW87391_CP_REG (0x02)
+#define AW87391_REG_CP_OVP_6_50V 0
+#define AW87391_REG_CP_OVP_6_75V 1
+#define AW87391_REG_CP_OVP_7_00V 2
+#define AW87391_REG_CP_OVP_7_25V 3
+#define AW87391_REG_CP_OVP_7_50V 4
+#define AW87391_REG_CP_OVP_7_75V 5
+#define AW87391_REG_CP_OVP_8_00V 6
+#define AW87391_REG_CP_OVP_8_25V 7
+#define AW87391_REG_CP_OVP_8_50V 8
+
+#define AW87391_PAG_REG (0x03)
+#define AW87391_GAIN_12DB 0
+#define AW87391_GAIN_15DB 1
+#define AW87391_GAIN_18DB 2
+#define AW87391_GAIN_21DB 3
+#define AW87391_GAIN_24DB 4
+
+#define AW87391_AGCPO_REG (0x04)
+#define AW87391_AK1_S_016 (2 << 5)
+#define AW87391_AK1_S_032 (3 << 5)
+#define AW87391_PD_AGC1_PWRDN BIT(4)
+/* AGC2PO supports values between 500mW (0000) to 1600mW (1011) */
+#define AW87391_AGC2PO_MW(n) ((n / 100) - 5)
+
+#define AW87391_AGC2PA_REG (0x05)
+#define AW87391_RK_S_5_12 (0 << 5)
+#define AW87391_RK_S_10_24 (1 << 5)
+#define AW87391_RK_S_20_48 (2 << 5)
+#define AW87391_RK_S_41 (3 << 5)
+#define AW87391_RK_S_82 (4 << 5)
+#define AW87391_RK_S_164 (5 << 5)
+#define AW87391_RK_S_328 (6 << 5)
+#define AW87391_RK_S_656 (7 << 5)
+#define AW87391_AK2_S_1_28 (0 << 2)
+#define AW87391_AK2_S_2_56 (1 << 2)
+#define AW87391_AK2_S_10_24 (2 << 2)
+#define AW87391_AK2_S_41 (3 << 2)
+#define AW87391_AK2_S_82 (4 << 2)
+#define AW87391_AK2_S_164 (5 << 2)
+#define AW87391_AK2_S_328 (6 << 2)
+#define AW87391_AK2_S_656 (7 << 2)
+#define AW87391_AK2F_S_10_24 0
+#define AW87391_AK2F_S_20_48 1
+#define AW87391_AK2F_S_41 2
+#define AW87391_AK2F_S_82 3
+
+#define AW87391_SYSST_REG (0x06)
+#define AW87391_UVLO BIT(7)
+#define AW87391_OTN BIT(6)
+#define AW87391_OC_FLAG BIT(5)
+#define AW87391_ADAP_CP BIT(4)
+#define AW87391_STARTOK BIT(3)
+#define AW87391_CP_OVP BIT(2)
+#define AW87391_PORN BIT(1)
+
+#define AW87391_SYSINT_REG (0x07)
+#define AW87391_UVLOI BIT(7)
+#define AW87391_ONTI BIT(6)
+#define AW87391_OC_FLAGI BIT(5)
+#define AW87391_ADAP_CPI BIT(4)
+#define AW87391_STARTOKI BIT(3)
+#define AW87391_CP_OVPI BIT(2)
+#define AW87391_PORNI BIT(1)
+
+#define AW87391_DFT_THGEN0_REG (0x63)
+#define AW87391_ADAPVTH_01W (0 << 2)
+#define AW87391_ADAPVTH_02W (1 << 2)
+#define AW87391_ADAPVTH_03W (2 << 2)
+#define AW87391_ADAPVTH_04W (3 << 2)
+
+#define AW87391_I2C_NAME "aw87391"
+
#define AW87390_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -63,6 +147,7 @@
enum aw87390_id {
AW87390_CHIP_ID = 0x76,
+ AW87391_CHIP_ID = 0xc1,
};
enum {
@@ -80,6 +165,7 @@ struct aw87390 {
struct mutex lock;
struct regmap *regmap;
struct aw_container *aw_cfg;
+ struct regulator *vdd_reg;
};
#endif
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index 8f37bfb974ae..29b3fc8a1ea4 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -11,6 +11,7 @@
#include <linux/i2c.h>
#include <linux/firmware.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include "aw88261.h"
#include "aw88395/aw88395_data_type.h"
@@ -1190,6 +1191,10 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
unsigned int chip_id;
int ret;
+ ret = devm_regulator_get_enable(&i2c->dev, "dvdd");
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Failed to enable dvdd supply\n");
+
/* read chip id */
ret = regmap_read(regmap, AW88261_ID_REG, &chip_id);
if (ret) {
@@ -1264,14 +1269,21 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88261_i2c_id[] = {
- { AW88261_I2C_NAME },
+ { "aw88261" },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
+static const struct of_device_id aw88261_of_table[] = {
+ { .compatible = "awinic,aw88261" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw88261_of_table);
+
static struct i2c_driver aw88261_i2c_driver = {
.driver = {
- .name = AW88261_I2C_NAME,
+ .name = "aw88261",
+ .of_match_table = aw88261_of_table,
},
.probe = aw88261_i2c_probe,
.id_table = aw88261_i2c_id,
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 734d0f93ced9..1fee589608d6 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -370,8 +370,6 @@
#define AW88261_START_RETRIES (5)
#define AW88261_START_WORK_DELAY_MS (0)
-#define AW88261_I2C_NAME "aw88261"
-
#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
SNDRV_PCM_RATE_96000)
#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
index 923f1857e45b..2b1529343f11 100644
--- a/sound/soc/codecs/cs-amp-lib-test.c
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/overflow.h>
+#include <linux/pci_ids.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#include <sound/cs-amp-lib.h>
@@ -56,6 +57,8 @@ struct cs_amp_lib_test_ctl_write_entry {
struct cs_amp_lib_test_param {
int num_amps;
int amp_index;
+ char *vendor_sysid;
+ char *expected_sysid;
};
static struct cirrus_amp_efi_data *cs_amp_lib_test_cal_blob_dup(struct kunit *test)
@@ -2305,6 +2308,98 @@ static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
}
+static efi_status_t cs_amp_lib_test_get_efi_vendor_sysid(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ unsigned int len;
+
+ KUNIT_ASSERT_NOT_NULL(test, param->vendor_sysid);
+ len = strlen(param->vendor_sysid);
+
+ if (*size < len) {
+ *size = len;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf, param->vendor_sysid, len);
+
+ return EFI_SUCCESS;
+}
+
+/* Fetch SSIDExV2 string from UEFI */
+static void cs_amp_lib_test_ssidexv2_fetch(struct kunit *test)
+{
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_vendor_sysid);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, got);
+ KUNIT_EXPECT_STREQ(test, got, param->expected_sysid);
+}
+
+/* Invalid SSIDExV2 string should be ignored */
+static void cs_amp_lib_test_ssidexv2_fetch_invalid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_vendor_sysid);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+}
+
+static void cs_amp_lib_test_ssidexv2_not_dell(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_vendor_sysid);
+
+ /* Not returned if SSID vendor is not Dell */
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_CIRRUS, 0xabcd);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+}
+
+static void cs_amp_lib_test_vendor_variant_id_not_found(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ const char *got;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+
+ got = cs_amp_devm_get_vendor_specific_variant_id(dev, -1, -1);
+ KUNIT_EXPECT_NOT_NULL(test, got);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
+}
+
static int cs_amp_lib_test_case_init(struct kunit *test)
{
struct cs_amp_lib_test_priv *priv;
@@ -2375,6 +2470,71 @@ static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_para
KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
cs_amp_lib_test_get_cal_param_desc);
+static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_param_cases[] = {
+ { .vendor_sysid = "abcd_00", .expected_sysid = "00" },
+ { .vendor_sysid = "abcd_01", .expected_sysid = "01" },
+ { .vendor_sysid = "abcd_XY", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "1028abcd_00", .expected_sysid = "00" },
+ { .vendor_sysid = "1028abcd_01", .expected_sysid = "01" },
+ { .vendor_sysid = "1028abcd_XY", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "abcd_00_WF", .expected_sysid = "00" },
+ { .vendor_sysid = "abcd_01_WF", .expected_sysid = "01" },
+ { .vendor_sysid = "abcd_XY_WF", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "1028abcd_00_WF", .expected_sysid = "00" },
+ { .vendor_sysid = "1028abcd_01_WF", .expected_sysid = "01" },
+ { .vendor_sysid = "1028abcd_XY_WF", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "abcd_00_AA_BB", .expected_sysid = "00" },
+ { .vendor_sysid = "abcd_01_AA_BB", .expected_sysid = "01" },
+ { .vendor_sysid = "abcd_XY_AA_BB", .expected_sysid = "XY" },
+
+ { .vendor_sysid = "1028abcd_00_AA_BB", .expected_sysid = "00" },
+ { .vendor_sysid = "1028abcd_01_AA_BB", .expected_sysid = "01" },
+ { .vendor_sysid = "1028abcd_XY_A_BB", .expected_sysid = "XY" },
+};
+
+static void cs_amp_lib_test_ssidexv2_param_desc(const struct cs_amp_lib_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "vendor_sysid:'%s' expected_sysid:'%s'",
+ param->vendor_sysid, param->expected_sysid);
+}
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2, cs_amp_lib_test_ssidexv2_param_cases,
+ cs_amp_lib_test_ssidexv2_param_desc);
+
+static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_invalid_param_cases[] = {
+ { .vendor_sysid = "abcd" },
+ { .vendor_sysid = "abcd_0" },
+ { .vendor_sysid = "abcd_1" },
+ { .vendor_sysid = "abcd_0_1" },
+ { .vendor_sysid = "abcd_1_1" },
+ { .vendor_sysid = "abcd_1_X" },
+ { .vendor_sysid = "abcd_1_X" },
+ { .vendor_sysid = "abcd_000" },
+ { .vendor_sysid = "abcd_010" },
+ { .vendor_sysid = "abcd_000_01" },
+ { .vendor_sysid = "abcd_000_01" },
+
+ { .vendor_sysid = "1234abcd" },
+ { .vendor_sysid = "1234abcd_0" },
+ { .vendor_sysid = "1234abcd_1" },
+ { .vendor_sysid = "1234abcd_0_1" },
+ { .vendor_sysid = "1234abcd_1_1" },
+ { .vendor_sysid = "1234abcd_1_X" },
+ { .vendor_sysid = "1234abcd_1_X" },
+ { .vendor_sysid = "1234abcd_000" },
+ { .vendor_sysid = "1234abcd_010" },
+ { .vendor_sysid = "1234abcd_000_01" },
+ { .vendor_sysid = "1234abcd_000_01" },
+};
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2_invalid, cs_amp_lib_test_ssidexv2_invalid_param_cases,
+ cs_amp_lib_test_ssidexv2_param_desc);
+
static struct kunit_case cs_amp_lib_test_cases[] = {
/* Tests for getting calibration data from EFI */
KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
@@ -2434,6 +2594,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+ /* Test cases for SSIDExV2 */
+ KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch,
+ cs_amp_lib_test_ssidexv2_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch_invalid,
+ cs_amp_lib_test_ssidexv2_invalid_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_not_dell,
+ cs_amp_lib_test_ssidexv2_gen_params),
+ KUNIT_CASE(cs_amp_lib_test_vendor_variant_id_not_found),
+
{ } /* terminator */
};
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index d8f8b0259cd1..8b131975143d 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/overflow.h>
+#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
@@ -36,6 +37,10 @@
#define HP_CALIBRATION_EFI_GUID \
EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+#define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data"
+#define DELL_SSIDEXV2_EFI_GUID \
+ EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0)
+
static const struct cs_amp_lib_cal_efivar {
efi_char16_t *name;
efi_guid_t *guid;
@@ -206,7 +211,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
const struct cirrus_amp_cal_data *data)
{
- if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return _cs_amp_write_cal_coeffs(dsp, controls, data);
else
return -ENODEV;
@@ -225,7 +230,7 @@ int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
struct cirrus_amp_cal_data *data)
{
- if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return _cs_amp_read_cal_coeffs(dsp, controls, data);
else
return -ENODEV;
@@ -244,10 +249,7 @@ int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
u32 temp)
{
- if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
- return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
- else
- return -ENODEV;
+ return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
}
EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
@@ -304,6 +306,29 @@ static int cs_amp_convert_efi_status(efi_status_t status)
}
}
+static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr)
+{
+ efi_status_t status;
+ unsigned long size = 0;
+
+ status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return ERR_PTR(cs_amp_convert_efi_status(status));
+
+ /* Over-alloc to ensure strings are always NUL-terminated */
+ void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf);
+ if (status != EFI_SUCCESS)
+ return ERR_PTR(cs_amp_convert_efi_status(status));
+
+ return_ptr(buf);
+}
+
static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
efi_char16_t **name,
efi_guid_t **guid,
@@ -452,7 +477,7 @@ static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, i
{
u64 cal_target = cs_amp_cal_target_u64(in_data);
unsigned long num_entries;
- struct cirrus_amp_efi_data *data __free(kfree) = NULL;
+ struct cirrus_amp_efi_data *data;
efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
@@ -515,28 +540,33 @@ alloc_new:
num_entries = max(num_amps, amp_index + 1);
if (!data || (data->count < num_entries)) {
- struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data);
+ struct cirrus_amp_efi_data *new_data;
unsigned int new_data_size = struct_size(data, data, num_entries);
- data = kzalloc(new_data_size, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
+ new_data = kzalloc(new_data_size, GFP_KERNEL);
+ if (!new_data) {
+ ret = -ENOMEM;
+ goto err;
+ }
- if (old_data)
- memcpy(data, old_data, struct_size(old_data, data, old_data->count));
+ if (data) {
+ memcpy(new_data, data, struct_size(data, data, data->count));
+ kfree(data);
+ }
+ data = new_data;
data->count = num_entries;
data->size = new_data_size;
}
data->data[amp_index] = *in_data;
ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
- if (ret) {
+ if (ret)
dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
- return ret;
- }
+err:
+ kfree(data);
- return 0;
+ return ret;
}
/**
@@ -578,7 +608,7 @@ alloc_new:
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
struct cirrus_amp_cal_data *out_data)
{
- if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
else
return -ENOENT;
@@ -614,7 +644,7 @@ EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
const struct cirrus_amp_cal_data *in_data)
{
- if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) {
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) {
scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
return _cs_amp_set_efi_calibration_data(dev, amp_index,
num_amps, in_data);
@@ -687,7 +717,7 @@ int cs_amp_get_vendor_spkid(struct device *dev)
int i, ret;
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
- !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
return -ENOENT;
for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
@@ -700,6 +730,92 @@ int cs_amp_get_vendor_spkid(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+static const char *cs_amp_devm_get_dell_ssidex(struct device *dev,
+ int ssid_vendor, int ssid_device)
+{
+ unsigned int hex_prefix;
+ char audio_id[4];
+ char delim;
+ char *p;
+ int ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
+ return ERR_PTR(-ENOENT);
+
+ char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME,
+ &DELL_SSIDEXV2_EFI_GUID,
+ NULL);
+ ret = PTR_ERR_OR_ZERO(ssidex_buf);
+ if (ret == -ENOENT)
+ return ERR_PTR(-ENOENT);
+ else if (ret < 0)
+ return ssidex_buf;
+
+ /*
+ * SSIDExV2 string is a series of underscore delimited fields.
+ * First field is all or part of the SSID. Second field should be
+ * a 2-character audio hardware id, followed by other identifiers.
+ * Older models did not have the 2-character audio id, so reject
+ * the string if the second field is not 2 characters.
+ */
+ ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim);
+ if (ret < 2)
+ return ERR_PTR(-ENOENT);
+
+ if ((ret == 3) && (delim != '_'))
+ return ERR_PTR(-ENOENT);
+
+ if (strlen(audio_id) != 2)
+ return ERR_PTR(-ENOENT);
+
+ p = devm_kstrdup(dev, audio_id, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ return p;
+}
+
+/**
+ * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string
+ * @dev: pointer to struct device
+ * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown)
+ * @ssid_device: PCI Subsystem Device (-1 if unknown)
+ *
+ * Known vendor-specific hardware identifiers are checked and if one is
+ * found its content is returned as a NUL-terminated string. The returned
+ * string is devm-managed.
+ *
+ * The returned string is not guaranteed to be globally unique.
+ * Generally it should be combined with some other qualifier, such as
+ * PCI SSID, to create a globally unique ID.
+ *
+ * If the caller has a PCI SSID it should pass it in @ssid_vendor and
+ * @ssid_device. If the vendor-spefic ID contains this SSID it will be
+ * stripped from the returned string to prevent duplication.
+ *
+ * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and
+ * @ssid_device.
+ *
+ * Return:
+ * * a pointer to a devm-managed string
+ * * ERR_PTR(-ENOENT) if no vendor-specific qualifier
+ * * ERR_PTR error value
+ */
+const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
+ int ssid_vendor,
+ int ssid_device)
+{
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id,
+ dev, ssid_vendor, ssid_device);
+
+ if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0))
+ return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device);
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB");
+
/**
* cs_amp_create_debugfs - create a debugfs directory for a device
*
@@ -733,7 +849,7 @@ static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
};
const struct cs_amp_test_hooks * const cs_amp_test_hooks =
- PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
+ PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs);
EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
MODULE_DESCRIPTION("Cirrus Logic amplifier library");
diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c
new file mode 100644
index 000000000000..94db02aef7dc
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared-test.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus Logic cs35l56-shared module.
+//
+// Copyright (C) 2026 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/device/faux.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/seq_buf.h>
+#include <sound/cs35l56.h>
+
+struct cs35l56_shared_test_priv {
+ struct kunit *test;
+ struct faux_device *amp_dev;
+ struct regmap *registers;
+ struct cs35l56_base *cs35l56_base;
+ u8 applied_pad_pull_state[CS35L56_MAX_GPIO];
+};
+
+struct cs35l56_shared_test_param {
+ int spkid_gpios[4];
+ int spkid_pulls[4];
+ unsigned long gpio_status;
+ int spkid;
+};
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_wrapper, regmap_exit, struct regmap *)
+
+static const struct regmap_config cs35l56_shared_test_mock_registers_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_bus cs35l56_shared_test_mock_registers_regmap_bus = {
+ /* No handlers because it is always in cache-only */
+};
+
+static unsigned int cs35l56_shared_test_read_gpio_status(struct cs35l56_shared_test_priv *priv)
+{
+ const struct cs35l56_shared_test_param *param = priv->test->param_value;
+ unsigned int reg_offs, pad_cfg, val;
+ unsigned int status = 0;
+ unsigned int mask = 1;
+
+ for (reg_offs = 0; reg_offs < CS35L56_MAX_GPIO * sizeof(u32); reg_offs += sizeof(u32)) {
+ regmap_read(priv->registers, CS35L56_SYNC_GPIO1_CFG + reg_offs, &pad_cfg);
+ regmap_read(priv->registers, CS35L56_GPIO1_CTRL1 + reg_offs, &val);
+
+ /* Only read a value if set as an input pin and as a GPIO */
+ val &= (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK);
+ if ((pad_cfg & CS35L56_PAD_GPIO_IE) &&
+ (val == (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO)))
+ status |= (param->gpio_status & mask);
+
+ mask <<= 1;
+ }
+
+ return status;
+}
+
+static int cs35l56_shared_test_updt_gpio_pres(struct cs35l56_shared_test_priv *priv,
+ unsigned int reg, unsigned int val)
+{
+ int i, ret;
+
+ ret = regmap_write(priv->registers, reg, val);
+ if (ret)
+ return ret;
+
+ if (val & CS35L56_UPDT_GPIO_PRES) {
+ /* Simulate transferring register state to internal latches */
+ for (i = 0; i < ARRAY_SIZE(priv->applied_pad_pull_state); i++) {
+ reg = CS35L56_SYNC_GPIO1_CFG + (i * sizeof(u32));
+ regmap_read(priv->registers, reg, &val);
+ val = FIELD_GET(CS35L56_PAD_GPIO_PULL_MASK, val);
+ priv->applied_pad_pull_state[i] = val;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_shared_test_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct cs35l56_shared_test_priv *priv = context;
+
+ switch (reg) {
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
+ return regmap_read(priv->registers, reg, val);
+ case CS35L56_UPDATE_REGS:
+ *val = 0;
+ return 0;
+ case CS35L56_GPIO_STATUS1:
+ *val = cs35l56_shared_test_read_gpio_status(priv);
+ return 0;
+ default:
+ kunit_fail_current_test("Bad regmap read address %#x\n", reg);
+ return -EINVAL;
+ }
+}
+
+static int cs35l56_shared_test_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct cs35l56_shared_test_priv *priv = context;
+
+ switch (reg) {
+ case CS35L56_UPDATE_REGS:
+ return cs35l56_shared_test_updt_gpio_pres(priv, reg, val);
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
+ return regmap_write(priv->registers, reg, val);
+ default:
+ kunit_fail_current_test("Bad regmap write address %#x\n", reg);
+ return -EINVAL;
+ }
+}
+
+static const struct regmap_bus cs35l56_shared_test_regmap_bus = {
+ .reg_read = cs35l56_shared_test_reg_read,
+ .reg_write = cs35l56_shared_test_reg_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+/*
+ * Self-test that the mock GPIO registers obey the configuration bits.
+ * Other tests rely on the mocked registers only returning a GPIO state
+ * if the pin is correctly set as a GPIO input.
+ */
+static void cs35l56_shared_test_mock_gpio_status_selftest(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ unsigned int reg, val;
+
+ KUNIT_ASSERT_NOT_NULL(test, param);
+
+ /* Set all pins non-GPIO and output. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ /* Set all pads as inputs */
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_PAD_GPIO_IE));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+
+ /* Set all pins as GPIO outputs. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_FN_GPIO));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+
+ /* Set all pins as non-GPIO inputs. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_DIR_MASK));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+
+ /* Set all pins as GPIO inputs. Mock GPIO_STATUS should match param->gpio_status */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0,
+ regmap_write(priv->registers, reg,
+ CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, param->gpio_status);
+
+ /* Set all pads as outputs. Mock GPIO_STATUS should read 0 */
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
+ KUNIT_EXPECT_EQ(test, val, 0);
+}
+
+/* Test that the listed chip pins are assembled into a speaker ID integer. */
+static void cs35l56_shared_test_get_onchip_speaker_id(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ unsigned int i, reg;
+
+ /* Set all pins non-GPIO and output */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ /* Init GPIO array */
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1;
+ cs35l56_base->num_onchip_spkid_gpios++;
+ }
+
+ cs35l56_base->num_onchip_spkid_pulls = 0;
+
+ KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), param->spkid);
+}
+
+/* Test that the listed chip pins and the corresponding pads are configured correctly. */
+static void cs35l56_shared_test_onchip_speaker_id_pad_config(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ unsigned int i, reg, val;
+
+ /* Init values in all pin registers */
+ for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
+
+ /* Init GPIO array */
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1;
+ cs35l56_base->num_onchip_spkid_gpios++;
+ }
+
+ /* Init pulls array */
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ cs35l56_base->onchip_spkid_pulls[i] = param->spkid_pulls[i];
+ cs35l56_base->num_onchip_spkid_pulls++;
+ }
+
+ KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ /* Pad should be an input */
+ reg = CS35L56_SYNC_GPIO1_CFG + ((param->spkid_gpios[i] - 1) * sizeof(u32));
+ KUNIT_EXPECT_EQ(test, regmap_read(priv->registers, reg, &val), 0);
+ KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_IE, CS35L56_PAD_GPIO_IE);
+
+ /* Specified pulls should be set, others should be none */
+ if (i < cs35l56_base->num_onchip_spkid_pulls) {
+ KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK,
+ FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
+ param->spkid_pulls[i]));
+ } else {
+ KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK,
+ CS35L56_PAD_PULL_NONE);
+ }
+
+ /* Pulls for all specfied GPIOs should have been transferred to AO latch */
+ if (i < cs35l56_base->num_onchip_spkid_pulls) {
+ KUNIT_EXPECT_EQ(test,
+ priv->applied_pad_pull_state[param->spkid_gpios[i] - 1],
+ param->spkid_pulls[i]);
+ } else {
+ KUNIT_EXPECT_EQ(test,
+ priv->applied_pad_pull_state[param->spkid_gpios[i] - 1],
+ CS35L56_PAD_PULL_NONE);
+ }
+ }
+}
+
+/* Test that the listed chip pins are stashed correctly. */
+static void cs35l56_shared_test_stash_onchip_spkid_pins(struct kunit *test)
+{
+ const struct cs35l56_shared_test_param *param = test->param_value;
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ u32 gpios[5], pulls[5];
+ int i, num_gpios, num_pulls;
+
+ static_assert(ARRAY_SIZE(gpios) >= ARRAY_SIZE(param->spkid_gpios));
+ static_assert(ARRAY_SIZE(pulls) >= ARRAY_SIZE(param->spkid_pulls));
+
+ num_gpios = 0;
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ gpios[i] = (u32)param->spkid_gpios[i];
+ num_gpios++;
+ }
+
+ num_pulls = 0;
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ pulls[i] = (u32)param->spkid_pulls[i];
+ num_pulls++;
+ }
+
+ cs35l56_base->num_onchip_spkid_gpios = 0;
+ cs35l56_base->num_onchip_spkid_pulls = 0;
+
+ KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+ KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+
+ KUNIT_EXPECT_EQ(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, num_gpios,
+ pulls, num_pulls),
+ 0);
+
+ KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_gpios, num_gpios);
+ KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_pulls, num_pulls);
+
+ /* GPIO numbers are adjusted from 1-based to 0-based */
+ for (i = 0; i < num_gpios; i++)
+ KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_gpios[i], gpios[i] - 1);
+
+ for (i = 0; i < num_pulls; i++)
+ KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_pulls[i], pulls[i]);
+}
+
+/* Test that illegal GPIO numbers are rejected. */
+static void cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ u32 gpios[8] = { }, pulls[8] = { };
+
+ KUNIT_EXPECT_LE(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, 0),
+ 0);
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ gpios[0] = CS35L56_MAX_GPIO + 1;
+ break;
+ case 0x63:
+ gpios[0] = CS35L63_MAX_GPIO + 1;
+ break;
+ default:
+ kunit_fail_current_test("Unsupported type:%#x\n", cs35l56_base->type);
+ return;
+ }
+ KUNIT_EXPECT_LE(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, 0),
+ 0);
+
+ gpios[0] = 1;
+ pulls[0] = 3;
+ KUNIT_EXPECT_LE(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, 1),
+ 0);
+
+ static_assert(ARRAY_SIZE(gpios) > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+ static_assert(ARRAY_SIZE(pulls) > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+ KUNIT_EXPECT_EQ(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, ARRAY_SIZE(gpios),
+ pulls, 0),
+ -EOVERFLOW);
+ KUNIT_EXPECT_EQ(test,
+ cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
+ gpios, 1,
+ pulls, ARRAY_SIZE(pulls)),
+ -EOVERFLOW);
+}
+
+static void cs35l56_shared_test_onchip_speaker_id_not_defined(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+
+ memset(cs35l56_base->onchip_spkid_gpios, 0, sizeof(cs35l56_base->onchip_spkid_gpios));
+ memset(cs35l56_base->onchip_spkid_pulls, 0, sizeof(cs35l56_base->onchip_spkid_pulls));
+ cs35l56_base->num_onchip_spkid_gpios = 0;
+ cs35l56_base->num_onchip_spkid_pulls = 0;
+ KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), -ENOENT);
+}
+
+static int cs35l56_shared_test_case_regmap_init(struct kunit *test,
+ const struct regmap_config *regmap_config)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base;
+
+ /*
+ * Create a dummy regmap to simulate a register map by holding the
+ * values of all simulated registers in the regmap cache.
+ */
+ priv->registers = regmap_init(&priv->amp_dev->dev,
+ &cs35l56_shared_test_mock_registers_regmap_bus,
+ priv,
+ &cs35l56_shared_test_mock_registers_regmap);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->registers);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test, regmap_exit_wrapper,
+ priv->registers));
+ regcache_cache_only(priv->registers, true);
+
+ /* Create dummy regmap for cs35l56 driver */
+ cs35l56_base = priv->cs35l56_base;
+ cs35l56_base->regmap = regmap_init(cs35l56_base->dev,
+ &cs35l56_shared_test_regmap_bus,
+ priv,
+ regmap_config);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cs35l56_base->regmap);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test, regmap_exit_wrapper,
+ cs35l56_base->regmap));
+
+ return 0;
+}
+
+static int cs35l56_shared_test_case_base_init(struct kunit *test, u8 type, u8 rev,
+ const struct regmap_config *regmap_config)
+{
+ struct cs35l56_shared_test_priv *priv;
+ int ret;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+ priv->test = test;
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs35l56_shared_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ priv->cs35l56_base = kunit_kzalloc(test, sizeof(*priv->cs35l56_base), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_base);
+ priv->cs35l56_base->dev = &priv->amp_dev->dev;
+ priv->cs35l56_base->type = type;
+ priv->cs35l56_base->rev = rev;
+
+ if (regmap_config) {
+ ret = cs35l56_shared_test_case_regmap_init(test, regmap_config);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B0_sdw(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_sdw);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B0_spi(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_spi);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B0_i2c(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_i2c);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B2_sdw(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_sdw);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B2_spi(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_spi);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L56_B2_i2c(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_i2c);
+}
+
+static int cs35l56_shared_test_case_regmap_init_L63_A1_sdw(struct kunit *test)
+{
+ return cs35l56_shared_test_case_base_init(test, 0x63, 0xa1, &cs35l63_regmap_sdw);
+}
+
+static void cs35l56_shared_test_gpio_param_desc(const struct cs35l56_shared_test_param *param,
+ char *desc)
+{
+ DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
+ DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
+ }
+
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s} status:%#lx spkid:%d",
+ seq_buf_str(&gpios), seq_buf_str(&pulls), param->gpio_status, param->spkid);
+}
+
+static const struct cs35l56_shared_test_param cs35l56_shared_test_gpios_selftest_cases[] = {
+ { .spkid_gpios = { -1 }, .gpio_status = GENMASK(12, 0) },
+};
+KUNIT_ARRAY_PARAM(cs35l56_shared_test_gpios_selftest,
+ cs35l56_shared_test_gpios_selftest_cases,
+ cs35l56_shared_test_gpio_param_desc);
+
+static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_cases[] = {
+ { .spkid_gpios = { 1, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 1, -1 }, .gpio_status = ~BIT(0), .spkid = 0 },
+ { .spkid_gpios = { 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
+
+ { .spkid_gpios = { 7, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 7, -1 }, .gpio_status = ~BIT(6), .spkid = 0 },
+ { .spkid_gpios = { 7, -1 }, .gpio_status = BIT(6), .spkid = 1 },
+
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = ~(BIT(0) | BIT(6)), .spkid = 0 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6), .spkid = 1 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(0), .spkid = 2 },
+ { .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
+
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = ~(BIT(6) | BIT(0)), .spkid = 0 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 },
+ { .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
+
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = 0, .spkid = 0 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2), .spkid = 4 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(0), .spkid = 5 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6), .spkid = 6 },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6) | BIT(0), .spkid = 7 },
+};
+KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid, cs35l56_shared_test_onchip_spkid_cases,
+ cs35l56_shared_test_gpio_param_desc);
+
+static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_pull_cases[] = {
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
+};
+KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid_pull,
+ cs35l56_shared_test_onchip_spkid_pull_cases,
+ cs35l56_shared_test_gpio_param_desc);
+
+static struct kunit_case cs35l56_shared_test_cases[] = {
+ /* Tests for speaker id */
+ KUNIT_CASE_PARAM(cs35l56_shared_test_mock_gpio_status_selftest,
+ cs35l56_shared_test_gpios_selftest_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_get_onchip_speaker_id,
+ cs35l56_shared_test_onchip_spkid_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config,
+ cs35l56_shared_test_onchip_spkid_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config,
+ cs35l56_shared_test_onchip_spkid_pull_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_shared_test_stash_onchip_spkid_pins,
+ cs35l56_shared_test_onchip_spkid_pull_gen_params),
+ KUNIT_CASE(cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid),
+ KUNIT_CASE(cs35l56_shared_test_onchip_speaker_id_not_defined),
+ { }
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B0_sdw = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B0_sdw",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B0_sdw,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B2_sdw = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B2_sdw",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B2_sdw,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L63_A1_sdw = {
+ .name = "snd-soc-cs35l56-shared-test_L63_A1_sdw",
+ .init = cs35l56_shared_test_case_regmap_init_L63_A1_sdw,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B0_spi = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B0_spi",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B0_spi,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B2_spi = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B2_spi",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B2_spi,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B0_i2c = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B0_i2c",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B0_i2c,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+static struct kunit_suite cs35l56_shared_test_suite_L56_B2_i2c = {
+ .name = "snd-soc-cs35l56-shared-test_L56_B2_i2c",
+ .init = cs35l56_shared_test_case_regmap_init_L56_B2_i2c,
+ .test_cases = cs35l56_shared_test_cases,
+};
+
+kunit_test_suites(
+ &cs35l56_shared_test_suite_L56_B0_sdw,
+ &cs35l56_shared_test_suite_L56_B2_sdw,
+ &cs35l56_shared_test_suite_L63_A1_sdw,
+
+ &cs35l56_shared_test_suite_L56_B0_spi,
+ &cs35l56_shared_test_suite_L56_B2_spi,
+
+ &cs35l56_shared_test_suite_L56_B0_i2c,
+ &cs35l56_shared_test_suite_L56_B2_i2c,
+);
+
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_DESCRIPTION("KUnit test for cs35l56-shared module");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 60100c8f8c95..4707f28bfca2 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -5,13 +5,16 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <kunit/static_stub.h>
#include <linux/array_size.h>
+#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/fs.h>
#include <linux/gpio/consumer.h>
#include <linux/kstrtox.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -182,6 +185,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
case CS35L56_OTP_MEM_53:
case CS35L56_OTP_MEM_54:
case CS35L56_OTP_MEM_55:
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_UPDATE_REGS:
case CS35L56_ASP1_ENABLES1:
case CS35L56_ASP1_CONTROL1:
case CS35L56_ASP1_CONTROL2:
@@ -213,6 +218,7 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
case CS35L56_IRQ1_MASK_8:
case CS35L56_IRQ1_MASK_18:
case CS35L56_IRQ1_MASK_20:
+ case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
case CS35L56_MIXER_NGATE_CH1_CFG:
case CS35L56_MIXER_NGATE_CH2_CFG:
case CS35L56_DSP_VIRTUAL1_MBOX_1:
@@ -262,6 +268,8 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
case CS35L56_GLOBAL_ENABLES: /* owned by firmware */
case CS35L56_BLOCK_ENABLES: /* owned by firmware */
case CS35L56_BLOCK_ENABLES2: /* owned by firmware */
+ case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
+ case CS35L56_UPDATE_REGS:
case CS35L56_REFCLK_INPUT: /* owned by firmware */
case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */
case CS35L56_DACPCM1_INPUT: /* owned by firmware */
@@ -272,6 +280,7 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
case CS35L56_IRQ1_EINT_18:
case CS35L56_IRQ1_EINT_20:
+ case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
case CS35L56_MIXER_NGATE_CH1_CFG:
case CS35L56_MIXER_NGATE_CH2_CFG:
case CS35L56_DSP_VIRTUAL1_MBOX_1:
@@ -1552,6 +1561,169 @@ err:
}
EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "SND_SOC_CS35L56_SHARED");
+int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
+ const u32 *gpios, int num_gpios,
+ const u32 *pulls, int num_pulls)
+{
+ int max_gpio;
+ int ret = 0;
+ int i;
+
+ if ((num_gpios > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)) ||
+ (num_pulls > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)))
+ return -EOVERFLOW;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ max_gpio = CS35L56_MAX_GPIO;
+ break;
+ default:
+ max_gpio = CS35L63_MAX_GPIO;
+ break;
+ }
+
+ for (i = 0; i < num_gpios; i++) {
+ if (gpios[i] < 1 || gpios[i] > max_gpio) {
+ dev_err(cs35l56_base->dev, "Invalid spkid GPIO %d\n", gpios[i]);
+ /* Keep going so we log all bad values */
+ ret = -EINVAL;
+ }
+
+ /* Change to zero-based */
+ cs35l56_base->onchip_spkid_gpios[i] = gpios[i] - 1;
+ }
+
+ for (i = 0; i < num_pulls; i++) {
+ switch (pulls[i]) {
+ case 0:
+ cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_NONE;
+ break;
+ case 1:
+ cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_UP;
+ break;
+ case 2:
+ cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_DOWN;
+ break;
+ default:
+ dev_err(cs35l56_base->dev, "Invalid spkid pull %d\n", pulls[i]);
+ /* Keep going so we log all bad values */
+ ret = -EINVAL;
+ break;
+ }
+ }
+ if (ret)
+ return ret;
+
+ cs35l56_base->num_onchip_spkid_gpios = num_gpios;
+ cs35l56_base->num_onchip_spkid_pulls = num_pulls;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_check_and_save_onchip_spkid_gpios, "SND_SOC_CS35L56_SHARED");
+
+/* Caller must pm_runtime resume before calling this function */
+int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
+{
+ struct regmap *regmap = cs35l56_base->regmap;
+ unsigned int addr_offset, val;
+ int num_gpios, num_pulls;
+ int i, ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs35l56_configure_onchip_spkid_pads, cs35l56_base);
+
+ if (cs35l56_base->num_onchip_spkid_gpios == 0)
+ return 0;
+
+ num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
+ ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+ num_pulls = min(cs35l56_base->num_onchip_spkid_pulls,
+ ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
+
+ for (i = 0; i < num_gpios; i++) {
+ addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
+
+ /* Set unspecified pulls to NONE */
+ if (i < num_pulls) {
+ val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
+ cs35l56_base->onchip_spkid_pulls[i]);
+ } else {
+ val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, CS35L56_PAD_PULL_NONE);
+ }
+
+ ret = regmap_update_bits(regmap, CS35L56_SYNC_GPIO1_CFG + addr_offset,
+ CS35L56_PAD_GPIO_PULL_MASK | CS35L56_PAD_GPIO_IE,
+ val | CS35L56_PAD_GPIO_IE);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "GPIO%d set pad fail: %d\n",
+ cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+ return ret;
+ }
+ }
+
+ ret = regmap_write(regmap, CS35L56_UPDATE_REGS, CS35L56_UPDT_GPIO_PRES);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "UPDT_GPIO_PRES failed:%d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS35L56_PAD_PULL_SETTLE_US, CS35L56_PAD_PULL_SETTLE_US * 2);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_configure_onchip_spkid_pads, "SND_SOC_CS35L56_SHARED");
+
+/* Caller must pm_runtime resume before calling this function */
+int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
+{
+ struct regmap *regmap = cs35l56_base->regmap;
+ unsigned int addr_offset, val;
+ int num_gpios;
+ int speaker_id = 0;
+ int i, ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs35l56_read_onchip_spkid, cs35l56_base);
+
+ if (cs35l56_base->num_onchip_spkid_gpios == 0)
+ return -ENOENT;
+
+ num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
+ ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
+
+ for (i = 0; i < num_gpios; i++) {
+ addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
+
+ ret = regmap_update_bits(regmap, CS35L56_GPIO1_CTRL1 + addr_offset,
+ CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK,
+ CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "GPIO%u set func fail: %d\n",
+ cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+ return ret;
+ }
+ }
+
+ ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n",
+ cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
+ return ret;
+ }
+
+ for (i = 0; i < num_gpios; i++) {
+ speaker_id <<= 1;
+
+ if (val & BIT(cs35l56_base->onchip_spkid_gpios[i]))
+ speaker_id |= 1;
+ }
+
+ dev_dbg(cs35l56_base->dev, "Onchip GPIO Speaker ID = %d\n", speaker_id);
+
+ return speaker_id;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_onchip_spkid, "SND_SOC_CS35L56_SHARED");
+
static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
[0x0C] = 128000,
[0x0F] = 256000,
diff --git a/sound/soc/codecs/cs35l56-test.c b/sound/soc/codecs/cs35l56-test.c
new file mode 100644
index 000000000000..b6c8c08e3ade
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-test.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus Logic cs35l56 driver.
+//
+// Copyright (C) 2026 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/efi.h>
+#include <linux/device/faux.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/pci_ids.h>
+#include <linux/property.h>
+#include <linux/seq_buf.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/cs35l56.h>
+#include <sound/cs-amp-lib.h>
+#include "cs35l56.h"
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper,
+ software_node_unregister_node_group,
+ const struct software_node * const *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper,
+ software_node_unregister,
+ const struct software_node *)
+
+KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
+ device_remove_software_node,
+ struct device *)
+
+struct cs35l56_test_priv {
+ struct faux_device *amp_dev;
+ struct cs35l56_private *cs35l56_priv;
+
+ const char *ssidexv2;
+
+ bool read_onchip_spkid_called;
+ bool configure_onchip_spkid_pads_called;
+};
+
+struct cs35l56_test_param {
+ u8 type;
+ u8 rev;
+
+ s32 spkid_gpios[4];
+ s32 spkid_pulls[4];
+};
+
+static const struct software_node cs35l56_test_dev_sw_node =
+ SOFTWARE_NODE("SWD1", NULL, NULL);
+
+static const struct software_node cs35l56_test_af01_sw_node =
+ SOFTWARE_NODE("AF01", NULL, &cs35l56_test_dev_sw_node);
+
+static const struct software_node *cs35l56_test_dev_and_af01_node_group[] = {
+ &cs35l56_test_dev_sw_node,
+ &cs35l56_test_af01_sw_node,
+ NULL
+};
+
+static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev,
+ int ssid_vendor,
+ int ssid_device)
+{
+ return ERR_PTR(-ENOENT);
+}
+
+static void cs35l56_test_l56_b0_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set device type info */
+ cs35l56->base.type = 0x56;
+ cs35l56->base.rev = 0xb0;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ kunit_activate_static_stub(test,
+ cs35l56_test_devm_get_vendor_specific_variant_id_none,
+ cs_amp_devm_get_vendor_specific_variant_id);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the legacy ALSA prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1");
+
+ /* Fallback suffix should be the new SoundWire ID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
+}
+
+static void cs35l56_test_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ kunit_activate_static_stub(test,
+ cs35l56_test_devm_get_vendor_specific_variant_id_none,
+ cs_amp_devm_get_vendor_specific_variant_id);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Suffix should be the SoundWire ID without a fallback */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "l1u5");
+ KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix);
+}
+
+static void cs35l56_test_suffix_i2cspi(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ kunit_activate_static_stub(test,
+ cs35l56_test_devm_get_vendor_specific_variant_id_none,
+ cs_amp_devm_get_vendor_specific_variant_id);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Suffix strings should not be set: use default wm_adsp suffixing */
+ KUNIT_EXPECT_NULL(test, cs35l56->dsp.fwf_suffix);
+ KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix);
+}
+
+static efi_status_t cs35l56_test_get_efi_ssidexv2(efi_char16_t *name,
+ efi_guid_t *guid,
+ u32 *returned_attr,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs35l56_test_priv *priv = test->priv;
+ unsigned int len;
+
+ KUNIT_ASSERT_NOT_NULL(test, priv->ssidexv2);
+ len = strlen(priv->ssidexv2);
+
+ if (*size < len) {
+ *size = len;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf, priv->ssidexv2, len);
+
+ return EFI_SUCCESS;
+}
+
+static void cs35l56_test_ssidexv2_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ /* Set a SSID to enable lookup of SSIDExV2 */
+ snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);
+
+ priv->ssidexv2 = "10281234_01_BB_CC";
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs35l56_test_get_efi_ssidexv2);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the SSIDExV2 string with SoundWire ID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-l1u5");
+
+ /* Fallback suffix should be the SoundWireID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
+}
+
+static void cs35l56_test_ssidexv2_suffix_i2cspi(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set a SSID to enable lookup of SSIDExV2 */
+ snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);
+
+ priv->ssidexv2 = "10281234_01_BB_CC";
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs35l56_test_get_efi_ssidexv2);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the SSIDExV2 string with ALSA name prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-AMP1");
+
+ /* Fallback suffix should be the ALSA name prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "AMP1");
+}
+
+/*
+ * CS35L56 B0 SoundWire should ignore any SSIDExV2 suffix. It isn't needed
+ * on any products with B0 silicon and would interfere with the fallback
+ * to legacy naming convention for early B0-based laptops.
+ */
+static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Set device type info */
+ cs35l56->base.type = 0x56;
+ cs35l56->base.rev = 0xb0;
+
+ /* Set the ALSA name prefix */
+ cs35l56->component->name_prefix = "AMP1";
+
+ /* Set SoundWire link and UID number */
+ cs35l56->sdw_link_num = 1;
+ cs35l56->sdw_unique_id = 5;
+
+ /* Set a SSID to enable lookup of SSIDExV2 */
+ snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);
+
+ priv->ssidexv2 = "10281234_01_BB_CC";
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs35l56_test_get_efi_ssidexv2);
+
+ KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));
+
+ /* Priority suffix should be the legacy ALSA prefix */
+ KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1");
+
+ /* Fallback suffix should be the new SoundWire ID */
+ KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
+}
+
+/*
+ * Test that cs35l56_process_xu_properties() correctly parses the GPIO and
+ * pull values from properties into the arrays in struct cs35l56_base.
+ *
+ * This test creates the node tree:
+ *
+ * Node("SWD1") { // top-level device node
+ * Node("AF01") {
+ * Node("mipi-sdca-function-expansion-subproperties") {
+ * property: "01fa-spk-id-gpios-onchip"
+ * property: 01fa-spk-id-gpios-onchip-pull
+ * }
+ * }
+ * }
+ *
+ * Note that in ACPI "mipi-sdca-function-expansion-subproperties" is
+ * a special _DSD property that points to a Device(EXT0) node but behaves
+ * as an alias of the EXT0 node. The equivalent in software nodes is to
+ * create a Node named "mipi-sdca-function-expansion-subproperties" with
+ * the properties.
+ *
+ */
+static void cs35l56_test_parse_xu_onchip_spkid(struct kunit *test)
+{
+ const struct cs35l56_test_param *param = test->param_value;
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+ struct software_node *ext0_node;
+ int num_gpios = 0;
+ int num_pulls = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++, num_gpios++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+ }
+ KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++, num_pulls++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+ }
+ KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
+
+ const struct property_entry ext0_props[] = {
+ PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip",
+ param->spkid_gpios, num_gpios),
+ PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip-pull",
+ param->spkid_pulls, num_pulls),
+ { }
+ };
+
+ KUNIT_ASSERT_EQ(test,
+ software_node_register_node_group(cs35l56_test_dev_and_af01_node_group),
+ 0);
+ KUNIT_ASSERT_EQ(test,
+ kunit_add_action_or_reset(test,
+ software_node_unregister_node_group_wrapper,
+ cs35l56_test_dev_and_af01_node_group),
+ 0);
+
+ ext0_node = kunit_kzalloc(test, sizeof(*ext0_node), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ext0_node);
+ *ext0_node = SOFTWARE_NODE("mipi-sdca-function-expansion-subproperties",
+ ext0_props, &cs35l56_test_af01_sw_node);
+
+ KUNIT_ASSERT_EQ(test, software_node_register(ext0_node), 0);
+ KUNIT_ASSERT_EQ(test,
+ kunit_add_action_or_reset(test,
+ software_node_unregister_wrapper,
+ ext0_node),
+ 0);
+
+ KUNIT_ASSERT_EQ(test,
+ device_add_software_node(cs35l56->base.dev, &cs35l56_test_dev_sw_node), 0);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ device_remove_software_node_wrapper,
+ cs35l56->base.dev));
+
+ KUNIT_EXPECT_EQ(test, cs35l56_process_xu_properties(cs35l56), 0);
+
+ KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_gpios, num_gpios);
+ KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_pulls, num_pulls);
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ /*
+ * cs35l56_process_xu_properties() stores the GPIO numbers
+ * zero-based, which is one less than the value in the property.
+ */
+ KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_gpios[i],
+ param->spkid_gpios[i] - 1,
+ "i=%d", i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_pulls[i],
+ param->spkid_pulls[i], "i=%d", i);
+ }
+}
+
+static int cs35l56_test_dummy_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs35l56_test_priv *priv = test->priv;
+
+ priv->read_onchip_spkid_called = true;
+
+ return 4;
+}
+
+static int cs35l56_test_dummy_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs35l56_test_priv *priv = test->priv;
+
+ priv->configure_onchip_spkid_pads_called = true;
+
+ return 0;
+}
+
+static void cs35l56_test_set_fw_name_reads_onchip_spkid(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Provide some on-chip GPIOs for spkid */
+ cs35l56->base.onchip_spkid_gpios[0] = 1;
+ cs35l56->base.num_onchip_spkid_gpios = 1;
+
+ cs35l56->speaker_id = -ENOENT;
+
+ kunit_activate_static_stub(test,
+ cs35l56_configure_onchip_spkid_pads,
+ cs35l56_test_dummy_configure_onchip_spkid_pads);
+ kunit_activate_static_stub(test,
+ cs35l56_read_onchip_spkid,
+ cs35l56_test_dummy_read_onchip_spkid);
+
+ priv->configure_onchip_spkid_pads_called = false;
+ priv->read_onchip_spkid_called = false;
+ KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
+ KUNIT_EXPECT_TRUE(test, priv->configure_onchip_spkid_pads_called);
+ KUNIT_EXPECT_TRUE(test, priv->read_onchip_spkid_called);
+ KUNIT_EXPECT_EQ(test, cs35l56->speaker_id,
+ cs35l56_test_dummy_read_onchip_spkid(&cs35l56->base));
+}
+
+static void cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ /* Provide some on-chip GPIOs for spkid */
+ cs35l56->base.onchip_spkid_gpios[0] = 1;
+ cs35l56->base.num_onchip_spkid_gpios = 1;
+
+ /* Simulate that the driver already got a spkid from somewhere */
+ cs35l56->speaker_id = 15;
+
+ KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
+}
+
+static void cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv = test->priv;
+ struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
+
+ cs35l56->base.num_onchip_spkid_gpios = 0;
+
+ /* Simulate that the driver already got a spkid from somewhere */
+ cs35l56->speaker_id = 15;
+
+ KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
+ KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
+}
+
+static int cs35l56_test_case_init_common(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv;
+ const struct cs35l56_test_param *param = test->param_value;
+ struct cs35l56_private *cs35l56;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs35l56_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ /* Construct minimal set of driver structs */
+ priv->cs35l56_priv = kunit_kzalloc(test, sizeof(*priv->cs35l56_priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_priv);
+ cs35l56 = priv->cs35l56_priv;
+ cs35l56->base.dev = &priv->amp_dev->dev;
+
+ cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cs35l56->component);
+ cs35l56->component->dev = cs35l56->base.dev;
+ snd_soc_component_set_drvdata(cs35l56->component, cs35l56);
+
+ cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cs35l56->component->card);
+
+ if (param) {
+ cs35l56->base.type = param->type;
+ cs35l56->base.rev = param->rev;
+ }
+
+ return 0;
+}
+
+static int cs35l56_test_case_init_soundwire(struct kunit *test)
+{
+ struct cs35l56_test_priv *priv;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ ret = cs35l56_test_case_init_common(test);
+ if (ret)
+ return ret;
+
+ priv = test->priv;
+ cs35l56 = priv->cs35l56_priv;
+
+ /* Dummy to indicate this is Soundwire */
+ cs35l56->sdw_peripheral = kunit_kzalloc(test, sizeof(*cs35l56->sdw_peripheral),
+ GFP_KERNEL);
+ if (!cs35l56->sdw_peripheral)
+ return -ENOMEM;
+
+
+ return 0;
+}
+
+static void cs35l56_test_gpio_param_desc(const struct cs35l56_test_param *param, char *desc)
+{
+ DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
+ DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
+ if (param->spkid_gpios[i] < 0)
+ break;
+
+ seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
+ if (param->spkid_pulls[i] < 0)
+ break;
+
+ seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
+ }
+
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s}",
+ seq_buf_str(&gpios), seq_buf_str(&pulls));
+}
+
+static const struct cs35l56_test_param cs35l56_test_onchip_spkid_cases[] = {
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
+ { .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
+
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
+ { .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
+
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
+ { .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
+};
+KUNIT_ARRAY_PARAM(cs35l56_test_onchip_spkid,
+ cs35l56_test_onchip_spkid_cases,
+ cs35l56_test_gpio_param_desc);
+
+static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "type: %02x rev: %02x",
+ param->type, param->rev);
+}
+
+static const struct cs35l56_test_param cs35l56_test_type_rev_ex_b0_param_cases[] = {
+ { .type = 0x56, .rev = 0xb2 },
+ { .type = 0x57, .rev = 0xb2 },
+ { .type = 0x63, .rev = 0xa1 },
+};
+KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_ex_b0, cs35l56_test_type_rev_ex_b0_param_cases,
+ cs35l56_test_type_rev_param_desc);
+
+
+static const struct cs35l56_test_param cs35l56_test_type_rev_all_param_cases[] = {
+ { .type = 0x56, .rev = 0xb0 },
+ { .type = 0x56, .rev = 0xb2 },
+ { .type = 0x57, .rev = 0xb2 },
+ { .type = 0x63, .rev = 0xa1 },
+};
+KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_all, cs35l56_test_type_rev_all_param_cases,
+ cs35l56_test_type_rev_param_desc);
+
+static struct kunit_case cs35l56_test_cases_soundwire[] = {
+ KUNIT_CASE(cs35l56_test_l56_b0_suffix_sdw),
+ KUNIT_CASE_PARAM(cs35l56_test_suffix_sdw, cs35l56_test_type_rev_ex_b0_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_sdw,
+ cs35l56_test_type_rev_ex_b0_gen_params),
+ KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw),
+
+ KUNIT_CASE_PARAM(cs35l56_test_parse_xu_onchip_spkid,
+ cs35l56_test_onchip_spkid_gen_params),
+
+ KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
+
+ { } /* terminator */
+};
+
+static struct kunit_case cs35l56_test_cases_not_soundwire[] = {
+ KUNIT_CASE_PARAM(cs35l56_test_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params),
+ KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi,
+ cs35l56_test_type_rev_all_gen_params),
+
+ KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
+ KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
+
+ { } /* terminator */
+};
+
+static struct kunit_suite cs35l56_test_suite_soundwire = {
+ .name = "snd-soc-cs35l56-test-soundwire",
+ .init = cs35l56_test_case_init_soundwire,
+ .test_cases = cs35l56_test_cases_soundwire,
+};
+
+static struct kunit_suite cs35l56_test_suite_not_soundwire = {
+ .name = "snd-soc-cs35l56-test-not-soundwire",
+ .init = cs35l56_test_case_init_common,
+ .test_cases = cs35l56_test_cases_not_soundwire,
+};
+
+kunit_test_suites(
+ &cs35l56_test_suite_soundwire,
+ &cs35l56_test_suite_not_soundwire,
+);
+
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 55b4d0d55712..2ff8b172b76e 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -5,6 +5,8 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <kunit/static_stub.h>
+#include <kunit/visibility.h>
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/completion.h>
@@ -1107,43 +1109,98 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
};
-static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
+VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
{
+ unsigned short vendor, device;
+ const char *vendor_id;
+ int ret;
+
if (cs35l56->dsp.fwf_suffix)
return 0;
- if (!cs35l56->sdw_peripheral)
- return 0;
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "l%uu%u",
+ cs35l56->sdw_link_num,
+ cs35l56->sdw_unique_id);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
- cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
- "l%uu%u",
- cs35l56->sdw_link_num,
- cs35l56->sdw_unique_id);
- if (!cs35l56->dsp.fwf_suffix)
- return -ENOMEM;
+ /*
+ * There are published firmware files for L56 B0 silicon using
+ * the ALSA prefix as the filename suffix. Default to trying these
+ * first, with the new SoundWire suffix as a fallback.
+ * None of these older systems use a vendor-specific ID.
+ */
+ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+
+ return 0;
+ }
+ }
/*
- * There are published firmware files for L56 B0 silicon using
- * the ALSA prefix as the filename suffix. Default to trying these
- * first, with the new name as an alternate.
+ * Some manufacturers use the same SSID on multiple products and have
+ * a vendor-specific qualifier to distinguish different models.
+ * Models with the same SSID but different qualifier might require
+ * different audio firmware, or they might all have the same audio
+ * firmware.
+ * Try searching for a firmware with this qualifier first, else
+ * fallback to standard naming.
*/
- if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
- cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
- cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+ if (snd_soc_card_get_pci_ssid(cs35l56->component->card, &vendor, &device) < 0) {
+ vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, -1, -1);
+ } else {
+ vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev,
+ vendor, device);
+ }
+ ret = PTR_ERR_OR_ZERO(vendor_id);
+ if (ret == -ENOENT)
+ return 0;
+ else if (ret)
+ return ret;
+
+ if (vendor_id) {
+ if (cs35l56->dsp.fwf_suffix)
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ else
+ cs35l56->fallback_fw_suffix = cs35l56->component->name_prefix;
+
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "%s-%s",
+ vendor_id,
+ cs35l56->fallback_fw_suffix);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
}
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix);
-static int cs35l56_component_probe(struct snd_soc_component *component)
+VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
- struct dentry *debugfs_root = component->debugfs_root;
unsigned short vendor, device;
int ret;
- BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+ if ((cs35l56->speaker_id < 0) && cs35l56->base.num_onchip_spkid_gpios) {
+ PM_RUNTIME_ACQUIRE(cs35l56->base.dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_configure_onchip_spkid_pads(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_read_onchip_spkid(&cs35l56->base);
+ if (ret < 0)
+ return ret;
+
+ cs35l56->speaker_id = ret;
+ }
if (!cs35l56->dsp.system_name &&
(snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
@@ -1164,6 +1221,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
return -ENOMEM;
}
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name);
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct dentry *debugfs_root = component->debugfs_root;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
if (!wait_for_completion_timeout(&cs35l56->init_completion,
msecs_to_jiffies(5000))) {
dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
@@ -1175,6 +1245,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
return -ENOMEM;
cs35l56->component = component;
+ ret = cs35l56_set_fw_name(component);
+ if (ret)
+ return ret;
+
ret = cs35l56_set_fw_suffix(cs35l56);
if (ret)
return ret;
@@ -1488,6 +1562,105 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
return 0;
}
+static int cs35l56_read_fwnode_u32_array(struct device *dev,
+ struct fwnode_handle *parent_node,
+ const char *prop_name,
+ int max_count,
+ u32 *dest)
+{
+ int count, ret;
+
+ count = fwnode_property_count_u32(parent_node, prop_name);
+ if ((count == 0) || (count == -EINVAL) || (count == -ENODATA)) {
+ dev_dbg(dev, "%s not found in %s\n", prop_name, fwnode_get_name(parent_node));
+ return 0;
+ }
+
+ if (count < 0) {
+ dev_err(dev, "Get %s error:%d\n", prop_name, count);
+ return count;
+ }
+
+ if (count > max_count) {
+ dev_err(dev, "%s too many entries (%d)\n", prop_name, count);
+ return -EOVERFLOW;
+ }
+
+ ret = fwnode_property_read_u32_array(parent_node, prop_name, dest, count);
+ if (ret) {
+ dev_err(dev, "Error reading %s: %d\n", prop_name, ret);
+ return ret;
+ }
+
+ return count;
+}
+
+static int cs35l56_process_xu_onchip_speaker_id(struct cs35l56_private *cs35l56,
+ struct fwnode_handle *ext_node)
+{
+ static const char * const gpio_name = "01fa-spk-id-gpios-onchip";
+ static const char * const pull_name = "01fa-spk-id-gpios-onchip-pull";
+ u32 gpios[5], pulls[5];
+ int num_gpios, num_pulls;
+ int ret;
+
+ static_assert(ARRAY_SIZE(gpios) == ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
+ static_assert(ARRAY_SIZE(pulls) == ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
+
+ num_gpios = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, gpio_name,
+ ARRAY_SIZE(gpios), gpios);
+ if (num_gpios < 1)
+ return num_gpios;
+
+ num_pulls = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, pull_name,
+ ARRAY_SIZE(pulls), pulls);
+ if (num_pulls < 0)
+ return num_pulls;
+
+ if (num_pulls != num_gpios) {
+ dev_warn(cs35l56->base.dev, "%s count(%d) != %s count(%d)\n",
+ pull_name, num_pulls, gpio_name, num_gpios);
+ }
+
+ ret = cs35l56_check_and_save_onchip_spkid_gpios(&cs35l56->base,
+ gpios, num_gpios,
+ pulls, num_pulls);
+ if (ret) {
+ return dev_err_probe(cs35l56->base.dev, ret, "Error in %s/%s\n",
+ gpio_name, pull_name);
+ }
+
+ return 0;
+}
+
+VISIBLE_IF_KUNIT int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56)
+{
+ struct fwnode_handle *ext_node = NULL;
+ struct fwnode_handle *link;
+ int ret;
+
+ if (!cs35l56->sdw_peripheral)
+ return 0;
+
+ fwnode_for_each_child_node(dev_fwnode(cs35l56->base.dev), link) {
+ ext_node = fwnode_get_named_child_node(link,
+ "mipi-sdca-function-expansion-subproperties");
+ if (ext_node) {
+ fwnode_handle_put(link);
+ break;
+ }
+ }
+
+ if (!ext_node)
+ return 0;
+
+ ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node);
+ fwnode_handle_put(ext_node);
+
+ return ret;
+}
+EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties);
+
static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
{
struct device *dev = cs35l56->base.dev;
@@ -1668,6 +1841,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
if (ret != 0)
goto err;
+ ret = cs35l56_process_xu_properties(cs35l56);
+ if (ret)
+ goto err;
+
ret = cs35l56_dsp_init(cs35l56);
if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index 4c59f92f3206..691f857d0bd8 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -74,4 +74,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56);
int cs35l56_init(struct cs35l56_private *cs35l56);
void cs35l56_remove(struct cs35l56_private *cs35l56);
+#if IS_ENABLED(CONFIG_KUNIT)
+int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56);
+int cs35l56_set_fw_name(struct snd_soc_component *component);
+int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56);
+#endif
+
#endif /* ifndef CS35L56_H */
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 77dfc83a3c01..d8cdd37e9112 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -528,7 +528,7 @@ static int cs4271_soc_resume(struct snd_soc_component *component)
ret = clk_prepare_enable(cs4271->clk);
if (ret) {
dev_err(component->dev, "Failed to enable clk: %d\n", ret);
- return ret;
+ goto err_disable_regulators;
}
/* Do a proper reset after power up */
@@ -537,15 +537,21 @@ static int cs4271_soc_resume(struct snd_soc_component *component)
/* Restore codec state */
ret = regcache_sync(cs4271->regmap);
if (ret < 0)
- return ret;
+ goto err_disable_clk;
/* then disable the power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, 0);
if (ret < 0)
- return ret;
+ goto err_disable_clk;
return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(cs4271->clk);
+err_disable_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+ return ret;
}
#else
#define cs4271_soc_suspend NULL
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 0a2b50cdea95..19f69a523f22 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -79,8 +79,8 @@ static const char * const da7213_audio_hpf_corner_txt[] = {
};
static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner,
- DA7213_DAC_FILTERS1
- , DA7213_AUDIO_HPF_CORNER_SHIFT,
+ DA7213_DAC_FILTERS1,
+ DA7213_AUDIO_HPF_CORNER_SHIFT,
da7213_audio_hpf_corner_txt);
static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
index eb85b71e87f3..605375b154c8 100644
--- a/sound/soc/codecs/es8323.c
+++ b/sound/soc/codecs/es8323.c
@@ -12,6 +12,7 @@
// Further cleanup and restructuring by:
// Binbin Zhou <zhoubinbin@loongson.cn>
+#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/clk.h>
@@ -32,7 +33,6 @@ struct es8323_priv {
struct clk *mclk;
struct regmap *regmap;
struct snd_pcm_hw_constraint_list *sysclk_constraints;
- struct snd_soc_component *component;
};
/* es8323 register cache */
@@ -52,7 +52,7 @@ static const struct reg_default es8323_reg_defaults[] = {
{ ES8323_ADCCONTROL4, 0x00 },
{ ES8323_ADCCONTROL5, 0x06 },
{ ES8323_ADCCONTROL6, 0x30 },
- { ES8323_ADC_MUTE, 0x30 },
+ { ES8323_ADCCONTROL7, 0x30 },
{ ES8323_LADC_VOL, 0xc0 },
{ ES8323_RADC_VOL, 0xc0 },
{ ES8323_ADCCONTROL10, 0x38 },
@@ -62,7 +62,7 @@ static const struct reg_default es8323_reg_defaults[] = {
{ ES8323_ADCCONTROL14, 0x00 },
{ ES8323_DACCONTROL1, 0x00 },
{ ES8323_DACCONTROL2, 0x06 },
- { ES8323_DAC_MUTE, 0x30 },
+ { ES8323_DACCONTROL3, 0x30 },
{ ES8323_LDAC_VOL, 0xc0 },
{ ES8323_RDAC_VOL, 0xc0 },
{ ES8323_DACCONTROL6, 0x08 },
@@ -119,29 +119,43 @@ static const struct snd_kcontrol_new es8323_snd_controls[] = {
SOC_ENUM("ALC Capture NG Type", es8323_alc_ng_type_enum),
SOC_ENUM("Playback De-emphasis", es8323_playback_deemphasis_enum),
SOC_ENUM("Capture Polarity", es8323_capture_polarity_enum),
- SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0),
- SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0),
- SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0),
- SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0),
- SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0),
- SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0),
- SOC_SINGLE("Capture Mute Switch", ES8323_ADC_MUTE, 2, 1, 0),
- SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8,
- 0, es8323_bypass_tlv),
- SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0,
- 8, 0, es8323_bypass_tlv),
- SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3,
- 7, 1, es8323_bypass_tlv2),
+
+ SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13,
+ ES8323_ADCCONTROL13_ALCZC_OFF, 1, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12,
+ ES8323_ADCCONTROL12_ALCDCY_OFF, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12,
+ ES8323_ADCCONTROL12_ALCATK_OFF, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14,
+ ES8323_ADCCONTROL14_NGTH_OFF, 31, 0),
+ SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14,
+ ES8323_ADCCONTROL14_NGAT_OFF, 1, 0),
+ SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13,
+ ES8323_ADCCONTROL13_TIMEOUT_OFF, 1, 0),
+ SOC_SINGLE("Capture Mute Switch", ES8323_ADCCONTROL7,
+ ES8323_ADCCONTROL7_ADCMUTE_OFF, 1, 0),
+
+ SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1,
+ ES8323_ADCCONTROL1_MICAMPL_OFF, 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1,
+ ES8323_ADCCONTROL1_MICAMPR_OFF, 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17,
+ ES8323_DACCONTROL17_LI2LOVOL_OFF, 7, 1, es8323_bypass_tlv2),
SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20,
- 3, 7, 1, es8323_bypass_tlv2),
- SOC_DOUBLE_R_TLV("PCM Volume", ES8323_LDAC_VOL, ES8323_RDAC_VOL,
+ ES8323_DACCONTROL20_RI2ROVOL_OFF, 7, 1, es8323_bypass_tlv2),
+
+ SOC_DOUBLE_R_TLV("PCM Volume",
+ ES8323_LDAC_VOL, ES8323_RDAC_VOL,
0, 192, 1, es8323_dac_tlv),
- SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_LADC_VOL,
- ES8323_RADC_VOL, 0, 192, 1, es8323_adc_tlv),
- SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_LOUT1_VOL,
- ES8323_ROUT1_VOL, 0, 33, 0, es8323_out_tlv),
- SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_LOUT2_VOL,
- ES8323_ROUT2_VOL, 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Capture Digital Volume",
+ ES8323_LADC_VOL, ES8323_RADC_VOL,
+ 0, 192, 1, es8323_adc_tlv),
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
+ ES8323_LOUT1_VOL, ES8323_ROUT1_VOL,
+ 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
+ ES8323_LOUT2_VOL, ES8323_ROUT2_VOL,
+ 0, 33, 0, es8323_out_tlv),
};
/* Left DAC Route */
@@ -198,21 +212,50 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("RINPUT1"),
SND_SOC_DAPM_INPUT("RINPUT2"),
- SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 3, 1),
+ SND_SOC_DAPM_SUPPLY("Mic Bias", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNMICB_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNADCBIS_OFF, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC STM", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC STM", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC DIG", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DIG", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC DLL", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC DLL", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8323_CHIPPOWER,
+ ES8323_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
/* Muxes */
- SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_left_dac_mux_controls),
- SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_right_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Left PGA Mux", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNAINL_OFF, 1, &es8323_left_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNAINR_OFF, 1, &es8323_right_dac_mux_controls),
+
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, &es8323_diffmux_controls),
+
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, &es8323_left_line_controls),
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, &es8323_right_line_controls),
- SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
- SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
- SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
- SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+ ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCR_OFF, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+ ES8323_ADCPOWER, ES8323_ADCPOWER_PDNADCL_OFF, 1),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+ ES8323_DACPOWER, ES8323_DACPOWER_PDNDACR_OFF, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+ ES8323_DACPOWER, ES8323_DACPOWER_PDNDACL_OFF, 1),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&es8323_left_mixer_controls[0],
@@ -221,14 +264,12 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
&es8323_right_mixer_controls[0],
ARRAY_SIZE(es8323_right_mixer_controls)),
- SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
- SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
- SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
- SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, ES8323_DACPOWER_ROUT2_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, ES8323_DACPOWER_LOUT2_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, ES8323_DACPOWER_ROUT1_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, ES8323_DACPOWER_LOUT1_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, ES8323_ADCCONTROL1_MICAMPL_OFF, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, ES8323_ADCCONTROL1_MICAMPR_OFF, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
@@ -252,18 +293,30 @@ static const struct snd_soc_dapm_route es8323_dapm_routes[] = {
{"Differential Mux", "Line 2", "LINPUT2"},
{"Differential Mux", "Line 2", "RINPUT2"},
- {"Left ADC Mux", "Stereo", "Right PGA Mux"},
{"Left ADC Mux", "Stereo", "Left PGA Mux"},
{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
- {"Right ADC Mux", "Stereo", "Left PGA Mux"},
{"Right ADC Mux", "Stereo", "Right PGA Mux"},
{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
- {"Left ADC Power", NULL, "Left ADC Mux"},
- {"Right ADC Power", NULL, "Right ADC Mux"},
- {"Left ADC", NULL, "Left ADC Power"},
- {"Right ADC", NULL, "Right ADC Power"},
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+
+ { "Mic Bias", NULL, "Mic Bias Gen" },
+
+ { "ADC DIG", NULL, "ADC STM" },
+ { "ADC DIG", NULL, "ADC Vref" },
+ { "ADC DIG", NULL, "ADC DLL" },
+
+ { "Left ADC", NULL, "ADC DIG" },
+ { "Right ADC", NULL, "ADC DIG" },
+
+ { "DAC DIG", NULL, "DAC STM" },
+ { "DAC DIG", NULL, "DAC Vref" },
+ { "DAC DIG", NULL, "DAC DLL" },
+
+ { "Left DAC", NULL, "DAC DIG" },
+ { "Right DAC", NULL, "DAC DIG" },
{"Left Line Mux", "Line 1L", "LINPUT1"},
{"Left Line Mux", "Line 2L", "LINPUT2"},
@@ -423,16 +476,18 @@ static int es8323_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
- u8 iface = snd_soc_component_read(component, ES8323_MASTERMODE);
- u8 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE);
- u8 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE);
+ u8 format_mode, inv_mode;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_BC_FP:
- iface |= 0x80;
+ /* Master serial port mode */
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_MSC, ES8323_MASTERMODE_MSC);
break;
case SND_SOC_DAIFMT_BC_FC:
- iface &= 0x7f;
+ /* Slave serial port mode */
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_MSC, 0);
break;
default:
return -EINVAL;
@@ -441,55 +496,47 @@ static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- adciface &= 0xfc;
- daciface &= 0xf8;
+ format_mode = ES8323_FMT_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
- adciface &= 0xfd;
- daciface &= 0xf9;
+ format_mode = ES8323_FMT_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
- adciface &= 0xfe;
- daciface &= 0xfa;
+ format_mode = ES8323_FMT_RIGHT_J;
break;
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
- adciface &= 0xff;
- daciface &= 0xfb;
+ format_mode = ES8323_FMT_DSP;
break;
default:
return -EINVAL;
}
+ snd_soc_component_write_field(component, ES8323_ADCCONTROL4,
+ ES8323_ADCCONTROL4_ADCFORMAT, format_mode);
+ snd_soc_component_write_field(component, ES8323_DACCONTROL1,
+ ES8323_DACCONTROL1_DACFORMAT, format_mode);
+
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
- iface &= 0xdf;
- adciface &= 0xdf;
- daciface &= 0xbf;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- iface |= 0x20;
- adciface |= 0x20;
- daciface |= 0x40;
- break;
case SND_SOC_DAIFMT_IB_NF:
- iface |= 0x20;
- adciface &= 0xdf;
- daciface &= 0xbf;
+ inv_mode = 0;
break;
+ case SND_SOC_DAIFMT_IB_IF:
case SND_SOC_DAIFMT_NB_IF:
- iface &= 0xdf;
- adciface |= 0x20;
- daciface |= 0x40;
+ inv_mode = 1;
break;
default:
return -EINVAL;
}
- snd_soc_component_write(component, ES8323_MASTERMODE, iface);
- snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
- snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_BCLKINV, inv_mode);
+ snd_soc_component_update_bits(component, ES8323_ADCCONTROL4,
+ ES8323_ADCCONTROL4_ADCLRP, inv_mode);
+ snd_soc_component_update_bits(component, ES8323_DACCONTROL1,
+ ES8323_DACCONTROL1_DACLRP, inv_mode);
return 0;
}
@@ -515,66 +562,65 @@ static int es8323_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
- u16 srate = snd_soc_component_read(component, ES8323_MASTERMODE) & 0x80;
- u16 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE) & 0xe3;
- u16 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE) & 0xc7;
+ u8 wl_mode, fs;
int coeff;
coeff = get_coeff(es8323->sysclk, params_rate(params));
if (coeff < 0) {
coeff = get_coeff(es8323->sysclk / 2, params_rate(params));
- srate |= 0x40;
+ if (coeff < 0) {
+ dev_err(component->dev,
+ "Unable to configure sample rate %dHz with %dHz MCLK\n",
+ params_rate(params), es8323->sysclk);
+ return coeff;
+ }
+
+ snd_soc_component_update_bits(component, ES8323_MASTERMODE,
+ ES8323_MASTERMODE_MCLKDIV2,
+ ES8323_MASTERMODE_MCLKDIV2);
}
- if (coeff < 0) {
- dev_err(component->dev,
- "Unable to configure sample rate %dHz with %dHz MCLK\n",
- params_rate(params), es8323->sysclk);
- return coeff;
- }
+ fs = FIELD_PREP(ES8323_DACCONTROL2_DACFSMODE, es8323_coeff_div[coeff].usb)
+ | FIELD_PREP(ES8323_DACCONTROL2_DACFSRATIO, es8323_coeff_div[coeff].sr);
+
+ snd_soc_component_write_field(component, ES8323_ADCCONTROL5,
+ ES8323_ADCCONTROL5_ADCFS_MASK, fs);
- /* bit size */
+ snd_soc_component_write_field(component, ES8323_DACCONTROL2,
+ ES8323_DACCONTROL2_DACFS_MASK, fs);
+
+ /* serial audio data word length */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- adciface |= 0xc;
- daciface |= 0x18;
+ wl_mode = ES8323_S16_LE;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
- adciface |= 0x4;
- daciface |= 0x8;
+ wl_mode = ES8323_S20_LE;
break;
case SNDRV_PCM_FORMAT_S24_LE:
+ wl_mode = ES8323_S24_LE;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- adciface |= 0x10;
- daciface |= 0x20;
+ wl_mode = ES8323_S32_LE;
break;
+ default:
+ return -EINVAL;
}
- snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
- snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+ snd_soc_component_write_field(component, ES8323_ADCCONTROL4,
+ ES8323_ADCCONTROL4_ADCWL, wl_mode);
- snd_soc_component_write(component, ES8323_MASTERMODE, srate);
- snd_soc_component_write(component, ES8323_ADCCONTROL5,
- es8323_coeff_div[coeff].sr |
- (es8323_coeff_div[coeff].usb) << 4);
- snd_soc_component_write(component, ES8323_DACCONTROL2,
- es8323_coeff_div[coeff].sr |
- (es8323_coeff_div[coeff].usb) << 4);
-
- snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+ snd_soc_component_write_field(component, ES8323_DACCONTROL1,
+ ES8323_DACCONTROL1_DACWL, wl_mode);
return 0;
}
static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_component *component = dai->component;
- u32 val = mute ? 0x6 : 0x2;
-
- snd_soc_component_write(component, ES8323_DAC_MUTE, val);
-
- return 0;
+ return snd_soc_component_update_bits(dai->component, ES8323_DACCONTROL3,
+ ES8323_DACCONTROL3_DACMUTE,
+ mute ? ES8323_DACCONTROL3_DACMUTE : 0);
}
static const struct snd_soc_dai_ops es8323_ops = {
@@ -613,8 +659,6 @@ static int es8323_probe(struct snd_soc_component *component)
struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
int ret;
- es8323->component = component;
-
es8323->mclk = devm_clk_get_optional(component->dev, "mclk");
if (IS_ERR(es8323->mclk)) {
dev_err(component->dev, "unable to get mclk\n");
@@ -631,7 +675,7 @@ static int es8323_probe(struct snd_soc_component *component)
}
snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_DACCONTROL21, 0x80);
return 0;
}
@@ -644,37 +688,23 @@ static int es8323_set_bias_level(struct snd_soc_component *component,
switch (level) {
case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
ret = clk_prepare_enable(es8323->mclk);
if (ret)
return ret;
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0);
- usleep_range(18000, 20000);
- snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
- snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
- snd_soc_component_write(component, ES8323_ADCPOWER, 0x09);
- snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00);
- break;
- case SND_SOC_BIAS_PREPARE:
+ snd_soc_component_update_bits(component, ES8323_ADCPOWER,
+ ES8323_ADCPOWER_PDNADCBIS, 0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
- snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
- snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
- snd_soc_component_write(component, ES8323_ADCPOWER, 0x59);
break;
case SND_SOC_BIAS_OFF:
- clk_disable_unprepare(es8323->mclk);
- snd_soc_component_write(component, ES8323_ADCPOWER, 0xff);
- snd_soc_component_write(component, ES8323_DACPOWER, 0xC0);
snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff);
snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff);
- snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff);
- snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7b);
+ clk_disable_unprepare(es8323->mclk);
break;
}
diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h
index f986c9301dc6..6e37e0480f04 100644
--- a/sound/soc/codecs/es8323.h
+++ b/sound/soc/codecs/es8323.h
@@ -16,33 +16,124 @@
#define ES8323_CONTROL1 0x00
#define ES8323_CONTROL2 0x01
#define ES8323_CHIPPOWER 0x02
+
+#define ES8323_CHIPPOWER_DACVREF_OFF 0
+#define ES8323_CHIPPOWER_ADCVREF_OFF 1
+#define ES8323_CHIPPOWER_DACDLL_OFF 2
+#define ES8323_CHIPPOWER_ADCDLL_OFF 3
+#define ES8323_CHIPPOWER_DACSTM_RESET 4
+#define ES8323_CHIPPOWER_ADCSTM_RESET 5
+#define ES8323_CHIPPOWER_DACDIG_OFF 6
+#define ES8323_CHIPPOWER_ADCDIG_OFF 7
+
#define ES8323_ADCPOWER 0x03
+
+#define ES8323_ADCPOWER_INT1LP BIT(0)
+#define ES8323_ADCPOWER_FLASHLP BIT(1)
+#define ES8323_ADCPOWER_PDNADCBIS BIT(2)
+#define ES8323_ADCPOWER_PDNMICB BIT(3)
+
+#define ES8323_ADCPOWER_PDNADCBIS_OFF 2
+#define ES8323_ADCPOWER_PDNMICB_OFF 3
+#define ES8323_ADCPOWER_PDNADCR_OFF 4
+#define ES8323_ADCPOWER_PDNADCL_OFF 5
+#define ES8323_ADCPOWER_PDNAINR_OFF 6
+#define ES8323_ADCPOWER_PDNAINL_OFF 7
+
#define ES8323_DACPOWER 0x04
+
+#define ES8323_DACPOWER_ROUT2_OFF 2
+#define ES8323_DACPOWER_LOUT2_OFF 3
+#define ES8323_DACPOWER_ROUT1_OFF 4
+#define ES8323_DACPOWER_LOUT1_OFF 5
+#define ES8323_DACPOWER_PDNDACR_OFF 6
+#define ES8323_DACPOWER_PDNDACL_OFF 7
+
#define ES8323_CHIPLOPOW1 0x05
#define ES8323_CHIPLOPOW2 0x06
#define ES8323_ANAVOLMANAG 0x07
#define ES8323_MASTERMODE 0x08
+#define ES8323_MASTERMODE_BCLKDIV GENMASK(4, 0)
+#define ES8323_MASTERMODE_BCLKINV BIT(5)
+#define ES8323_MASTERMODE_MCLKDIV2 BIT(6)
+#define ES8323_MASTERMODE_MSC BIT(7)
+
/* ADC Control */
#define ES8323_ADCCONTROL1 0x09
+
+#define ES8323_ADCCONTROL1_MICAMPR_OFF 0
+#define ES8323_ADCCONTROL1_MICAMPL_OFF 4
+
#define ES8323_ADCCONTROL2 0x0a
#define ES8323_ADCCONTROL3 0x0b
#define ES8323_ADCCONTROL4 0x0c
+
+#define ES8323_ADCCONTROL4_ADCFORMAT GENMASK(1, 0)
+#define ES8323_FMT_I2S 0x0
+#define ES8323_FMT_LEFT_J 0x1
+#define ES8323_FMT_RIGHT_J 0x2
+#define ES8323_FMT_DSP 0x3
+#define ES8323_ADCCONTROL4_ADCWL GENMASK(4, 2)
+#define ES8323_S24_LE 0x0
+#define ES8323_S20_LE 0x1
+#define ES8323_S18_LE 0x2
+#define ES8323_S16_LE 0x3
+#define ES8323_S32_LE 0x4
+#define ES8323_ADCCONTROL4_ADCLRP BIT(5)
+#define ES8323_ADCCONTROL4_DATSEL GENMASK(7, 6)
+
#define ES8323_ADCCONTROL5 0x0d
+
+#define ES8323_ADCCONTROL5_ADCFSRATIO GENMASK(4, 0)
+#define ES8323_ADCCONTROL5_ADCFSMODE BIT(5)
+#define ES8323_ADCCONTROL5_ADCFS_MASK (ES8323_ADCCONTROL5_ADCFSRATIO |\
+ ES8323_ADCCONTROL5_ADCFSMODE)
+
#define ES8323_ADCCONTROL6 0x0e
-#define ES8323_ADC_MUTE 0x0f
+#define ES8323_ADCCONTROL7 0x0f
+
+#define ES8323_ADCCONTROL7_ADCMUTE_OFF 2
+
#define ES8323_LADC_VOL 0x10
#define ES8323_RADC_VOL 0x11
#define ES8323_ADCCONTROL10 0x12
#define ES8323_ADCCONTROL11 0x13
#define ES8323_ADCCONTROL12 0x14
+
+#define ES8323_ADCCONTROL12_ALCATK_OFF 0
+#define ES8323_ADCCONTROL12_ALCDCY_OFF 4
+
#define ES8323_ADCCONTROL13 0x15
+
+#define ES8323_ADCCONTROL13_TIMEOUT_OFF 5
+#define ES8323_ADCCONTROL13_ALCZC_OFF 6
+
#define ES8323_ADCCONTROL14 0x16
+#define ES8323_ADCCONTROL14_NGAT_OFF 0
+#define ES8323_ADCCONTROL14_NGG_OFF 1
+#define ES8323_ADCCONTROL14_NGTH_OFF 3
+
/* DAC Control */
#define ES8323_DACCONTROL1 0x17
+
+#define ES8323_DACCONTROL1_DACFORMAT GENMASK(1, 0)
+#define ES8323_DACCONTROL1_DACWL GENMASK(5, 3)
+#define ES8323_DACCONTROL1_DACLRP BIT(6)
+#define ES8323_DACCONTROL1_DACLRSWAP BIT(7)
+
#define ES8323_DACCONTROL2 0x18
-#define ES8323_DAC_MUTE 0x19
+
+#define ES8323_DACCONTROL2_DACFSRATIO GENMASK(4, 0)
+#define ES8323_DACCONTROL2_DACFSMODE BIT(5)
+#define ES8323_DACCONTROL2_DACFS_MASK (ES8323_DACCONTROL2_DACFSRATIO |\
+ ES8323_DACCONTROL2_DACFSMODE)
+
+#define ES8323_DACCONTROL3 0x19
+
+#define ES8323_DACCONTROL3_DACMUTE BIT(2)
+
#define ES8323_LDAC_VOL 0x1a
#define ES8323_RDAC_VOL 0x1b
#define ES8323_DACCONTROL6 0x1c
@@ -57,9 +148,15 @@
#define ES8323_DACCONTROL15 0x25
#define ES8323_DACCONTROL16 0x26
#define ES8323_DACCONTROL17 0x27
+
+#define ES8323_DACCONTROL17_LI2LOVOL_OFF 3
+
#define ES8323_DACCONTROL18 0x28
#define ES8323_DACCONTROL19 0x29
#define ES8323_DACCONTROL20 0x2a
+
+#define ES8323_DACCONTROL20_RI2ROVOL_OFF 3
+
#define ES8323_DACCONTROL21 0x2b
#define ES8323_DACCONTROL22 0x2c
#define ES8323_DACCONTROL23 0x2d
@@ -71,8 +168,4 @@
#define ES8323_DACCONTROL29 0x33
#define ES8323_DACCONTROL30 0x34
-#define ES8323_ADC_IFACE ES8323_ADCCONTROL4
-#define ES8323_ADC_SRATE ES8323_ADCCONTROL5
-#define ES8323_DAC_IFACE ES8323_DACCONTROL1
-#define ES8323_DAC_SRATE ES8323_DACCONTROL2
#endif
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 05b13661c38c..55a65ef99208 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -194,6 +194,8 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dre_gain_tlv, -9550, 400, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dre_gate_tlv, -9600, 600, 0);
static const char *const winsize[] = {
"0.25db/2 LRCK",
@@ -226,6 +228,8 @@ static const char *const hp_spkvol_switch[] = {
static const struct soc_enum dacpol =
SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt);
+static const struct soc_enum dre_winsize =
+ SOC_ENUM_SINGLE(ES8326_ADC_DRE, 0, 16, winsize);
static const struct soc_enum alc_winsize =
SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize);
static const struct soc_enum drc_winsize =
@@ -245,7 +249,18 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
adc_vol_tlv),
SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv),
SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv),
+ SOC_SINGLE("ADC PGA SE Switch", ES8326_PGAGAIN, 7, 1, 0),
SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE("ADC4 DRE Switch", ES8326_ADC_DRE, 4, 1, 0),
+ SOC_SINGLE("ADC3 DRE Switch", ES8326_ADC_DRE, 5, 1, 0),
+ SOC_SINGLE("ADC2 DRE Switch", ES8326_ADC_DRE, 6, 1, 0),
+ SOC_SINGLE("ADC1 DRE Switch", ES8326_ADC_DRE, 7, 1, 0),
+ SOC_ENUM("DRE Winsize", dre_winsize),
+ SOC_SINGLE("DRE Gain Switch", ES8326_ADC_DRE_GAIN, 5, 1, 0),
+ SOC_SINGLE_TLV("DRE Gain Volume", ES8326_ADC_DRE_GAIN,
+ 0, 0x1F, 0, dre_gain_tlv),
+ SOC_SINGLE_TLV("DRE Gate Volume", ES8326_ADC_DRE_GATE,
+ 4, 0x07, 0, dre_gate_tlv),
SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0),
SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL,
0, 4, 0, drc_recovery_tlv),
@@ -934,11 +949,8 @@ static void es8326_jack_detect_handler(struct work_struct *work)
dev_dbg(comp->dev, "Headset detected\n");
snd_soc_jack_report(es8326->jack,
SND_JACK_HEADSET, SND_JACK_HEADSET);
-
regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
0x08, 0x08);
- regmap_update_bits(es8326->regmap, ES8326_PGAGAIN,
- 0x80, 0x80);
regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index c3e52e7bdef5..1c5b3ec70a1e 100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
@@ -58,6 +58,9 @@
#define ES8326_ADC1_VOL 0x2c
#define ES8326_ADC2_VOL 0x2d
#define ES8326_ADC_RAMPRATE 0x2e
+#define ES8326_ADC_DRE 0x2f
+#define ES8326_ADC_DRE_GAIN 0x30
+#define ES8326_ADC_DRE_GATE 0x31
#define ES8326_ALC_RECOVERY 0x32
#define ES8326_ALC_LEVEL 0x33
#define ES8326_ADC_HPFS1 0x34
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 1e11175cfbbb..9838fe42cb6f 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -163,12 +163,11 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
if (es8328->deemph == deemph)
return 0;
+ es8328->deemph = deemph;
ret = es8328_set_deemph(component);
if (ret < 0)
return ret;
- es8328->deemph = deemph;
-
return 1;
}
@@ -406,16 +405,6 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "Mic Bias", NULL, "Mic Bias Gen" },
- { "Left Line Mux", "Line 1", "LINPUT1" },
- { "Left Line Mux", "Line 2", "LINPUT2" },
- { "Left Line Mux", "PGA", "Left PGA Mux" },
- { "Left Line Mux", "Differential", "Differential Mux" },
-
- { "Right Line Mux", "Line 1", "RINPUT1" },
- { "Right Line Mux", "Line 2", "RINPUT2" },
- { "Right Line Mux", "PGA", "Right PGA Mux" },
- { "Right Line Mux", "Differential", "Differential Mux" },
-
{ "Left Mixer", NULL, "Left DAC" },
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
@@ -471,6 +460,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int ret;
int i;
int reg;
int wl;
@@ -504,9 +494,12 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
es8328->mclkdiv2 = 0;
}
- snd_soc_component_update_bits(component, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MCLKDIV2,
- es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+ ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MCLKDIV2,
+ es8328->mclkdiv2 ?
+ ES8328_MASTERMODE_MCLKDIV2 : 0);
+ if (ret < 0)
+ return ret;
switch (params_width(params)) {
case 16:
@@ -529,18 +522,28 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
- ES8328_DACCONTROL1_DACWL_MASK,
- wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+ ret = snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACWL_MASK,
+ wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+ if (ret < 0)
+ return ret;
es8328->playback_fs = params_rate(params);
- es8328_set_deemph(component);
- } else
- snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
- ES8328_ADCCONTROL4_ADCWL_MASK,
- wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+ ret = es8328_set_deemph(component);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCWL_MASK,
+ wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
+ if (ret < 0)
+ return ret;
+ }
- return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+ ret = snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio);
+ if (ret < 0)
+ return ret;
+ return 0;
}
static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
@@ -589,21 +592,26 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
{
struct snd_soc_component *component = codec_dai->component;
struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
+ int ret;
u8 dac_mode = 0;
u8 adc_mode = 0;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBP_CFP:
/* Master serial port mode, with BCLK generated automatically */
- snd_soc_component_update_bits(component, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC,
- ES8328_MASTERMODE_MSC);
+ ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC,
+ ES8328_MASTERMODE_MSC);
+ if (ret < 0)
+ return ret;
es8328->provider = true;
break;
case SND_SOC_DAIFMT_CBC_CFC:
/* Slave serial port mode */
- snd_soc_component_update_bits(component, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC, 0);
+ ret = snd_soc_component_update_bits(component, ES8328_MASTERMODE,
+ ES8328_MASTERMODE_MSC, 0);
+ if (ret < 0)
+ return ret;
es8328->provider = false;
break;
default:
@@ -632,10 +640,17 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
- snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
- ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
- snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
- ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
+ ret = snd_soc_component_update_bits(component, ES8328_DACCONTROL1,
+ ES8328_DACCONTROL1_DACFORMAT_MASK,
+ dac_mode);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, ES8328_ADCCONTROL4,
+ ES8328_ADCCONTROL4_ADCFORMAT_MASK,
+ adc_mode);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -644,6 +659,7 @@ static int es8328_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -651,43 +667,56 @@ static int es8328_set_bias_level(struct snd_soc_component *component,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- ES8328_CONTROL1_VMIDSEL_50k |
- ES8328_CONTROL1_ENREF);
+ ret = snd_soc_component_write(component, ES8328_CHIPPOWER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_50k |
+ ES8328_CONTROL1_ENREF);
+ if (ret < 0)
+ return ret;
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- ES8328_CONTROL1_VMIDSEL_5k |
- ES8328_CONTROL1_ENREF);
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_5k |
+ ES8328_CONTROL1_ENREF);
+ if (ret < 0)
+ return ret;
/* Charge caps */
msleep(100);
}
- snd_soc_component_write(component, ES8328_CONTROL2,
- ES8328_CONTROL2_OVERCURRENT_ON |
- ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+ ret = snd_soc_component_write(component, ES8328_CONTROL2,
+ ES8328_CONTROL2_OVERCURRENT_ON |
+ ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+ if (ret < 0)
+ return ret;
/* VREF, VMID=2*500k, digital stopped */
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- ES8328_CONTROL1_VMIDSEL_500k |
- ES8328_CONTROL1_ENREF);
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ ES8328_CONTROL1_VMIDSEL_500k |
+ ES8328_CONTROL1_ENREF);
+ if (ret < 0)
+ return ret;
break;
case SND_SOC_BIAS_OFF:
- snd_soc_component_update_bits(component, ES8328_CONTROL1,
- ES8328_CONTROL1_VMIDSEL_MASK |
- ES8328_CONTROL1_ENREF,
- 0);
+ ret = snd_soc_component_update_bits(component, ES8328_CONTROL1,
+ ES8328_CONTROL1_VMIDSEL_MASK |
+ ES8328_CONTROL1_ENREF,
+ 0);
+ if (ret < 0)
+ return ret;
break;
}
return 0;
@@ -742,12 +771,9 @@ static int es8328_suspend(struct snd_soc_component *component)
static int es8328_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(component->dev, NULL);
- struct es8328_priv *es8328;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int ret;
- es8328 = snd_soc_component_get_drvdata(component);
-
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(component->dev, "unable to enable clock\n");
@@ -758,17 +784,23 @@ static int es8328_resume(struct snd_soc_component *component)
es8328->supplies);
if (ret) {
dev_err(component->dev, "unable to enable regulators\n");
- return ret;
+ goto err_clk;
}
- regcache_mark_dirty(regmap);
- ret = regcache_sync(regmap);
+ regcache_mark_dirty(es8328->regmap);
+ ret = regcache_sync(es8328->regmap);
if (ret) {
dev_err(component->dev, "unable to sync regcache\n");
- return ret;
+ goto err_regulators;
}
return 0;
+
+err_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), es8328->supplies);
+err_clk:
+ clk_disable_unprepare(es8328->clk);
+ return ret;
}
static int es8328_component_probe(struct snd_soc_component *component)
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
index 36b0ebdce514..0b9406e93c0e 100644
--- a/sound/soc/codecs/es8375.c
+++ b/sound/soc/codecs/es8375.c
@@ -397,8 +397,6 @@ static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_I2S:
codeciface &= 0xFC;
break;
- case SND_SOC_DAIFMT_RIGHT_J:
- return -EINVAL;
case SND_SOC_DAIFMT_LEFT_J:
codeciface &= 0xFC;
codeciface |= 0x01;
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
index a84d79f9d3d1..8d418cae371a 100644
--- a/sound/soc/codecs/es8389.c
+++ b/sound/soc/codecs/es8389.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -31,13 +32,21 @@
struct es8389_private {
struct regmap *regmap;
struct clk *mclk;
+ struct regulator_bulk_data core_supply[2];
unsigned int sysclk;
int mastermode;
u8 mclk_src;
+ u8 vddd;
+ int version;
enum snd_soc_bias_level bias_level;
};
+static const char * const es8389_core_supplies[] = {
+ "vddd",
+ "vdda",
+};
+
static bool es8389_volatile_register(struct device *dev,
unsigned int reg)
{
@@ -367,95 +376,110 @@ struct _coeff_div {
u8 Reg0x16;
u8 Reg0x18;
u8 Reg0x19;
+ u8 dvdd_vol;
+ u8 dmic_sel;
};
/* codec hifi mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
- {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
- {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
- {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14},
- {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B},
- {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
- {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25},
- {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
- {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
- {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
- {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F},
-
- {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
- {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
- {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {50, 400000, 8000, 0x00, 0x15, 0x85, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x00, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {600, 4800000, 8000, 0x02, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {2400, 19200000, 8000, 0x05, 0x14, 0x01, 0xC9, 0x00, 0xD2, 0x80, 0x80, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x80, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 2},
+ {3250, 26000000, 8000, 0x40, 0x15, 0x85, 0xD0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 1},
+ {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07, 2, 0},
+ {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2},
+ {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2},
+ {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0},
+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2},
+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2},
+ {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2},
+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2},
+ {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2},
+ {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2},
+ {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2},
};
-static inline int get_coeff(int mclk, int rate)
+static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
{
int i;
+ u8 dmic_det, vddd_det;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
- if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
- return i;
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
+ vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01;
+ dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01;
+ vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01;
+ dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01;
+
+ if (vddd_det && dmic_det)
+ return i;
+ }
}
+
return -EINVAL;
}
@@ -537,8 +561,9 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
- int coeff;
- u8 state = 0;
+ int coeff, ret;
+ u8 dmic_enable, state = 0;
+ unsigned int regv;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -569,7 +594,23 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream,
es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params);
}
- coeff = get_coeff(es8389->sysclk, params_rate(params));
+ regmap_read(es8389->regmap, ES8389_DMIC_EN, &regv);
+ dmic_enable = regv >> 7 & 0x01;
+
+ ret = regulator_get_voltage(es8389->core_supply[ES8389_SUPPLY_VD].consumer);
+ switch (ret) {
+ case 1800000 ... 2000000:
+ es8389->vddd = ES8389_1V8;
+ break;
+ case 2500000 ... 3300000:
+ es8389->vddd = ES8389_3V3;
+ break;
+ default:
+ es8389->vddd = ES8389_3V3;
+ break;
+ }
+
+ coeff = get_coeff(es8389->vddd, dmic_enable, es8389->sysclk, params_rate(params));
if (coeff >= 0) {
regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04);
regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05);
@@ -621,10 +662,6 @@ static int es8389_set_bias_level(struct snd_soc_component *component,
regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
regmap_write(es8389->regmap, ES8389_RESET, 0x01);
regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
- usleep_range(70000, 72000);
- regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -655,6 +692,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
{
struct snd_soc_component *component = dai->component;
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int regv;
if (mute) {
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -665,10 +703,26 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
0x03, 0x03);
}
} else {
+ regmap_read(es8389->regmap, ES8389_CSM_STATE1, &regv);
+ if (regv != ES8389_STATE_ON) {
+ regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
+ }
+
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!es8389->version) {
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
+ usleep_range(70000, 72000);
+ }
regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
0x03, 0x00);
} else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
0x03, 0x00);
}
@@ -713,7 +767,10 @@ static struct snd_soc_dai_driver es8389_dai = {
static void es8389_init(struct snd_soc_component *component)
{
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+ regmap_read(es8389->regmap, ES8389_MAX_REGISTER, &reg);
+ es8389->version = reg;
regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00);
regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38);
@@ -818,7 +875,7 @@ static int es8389_resume(struct snd_soc_component *component)
static int es8389_probe(struct snd_soc_component *component)
{
- int ret;
+ int ret, i;
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
@@ -827,6 +884,14 @@ static int es8389_probe(struct snd_soc_component *component)
es8389->mclk_src = ES8389_MCLK_SOURCE;
}
+ for (i = 0; i < ARRAY_SIZE(es8389_core_supplies); i++)
+ es8389->core_supply[i].supply = es8389_core_supplies[i];
+ ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
+ if (ret) {
+ dev_err(component->dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
es8389->mclk = devm_clk_get(component->dev, "mclk");
if (IS_ERR(es8389->mclk))
return dev_err_probe(component->dev, PTR_ERR(es8389->mclk),
@@ -841,6 +906,13 @@ static int es8389_probe(struct snd_soc_component *component)
return ret;
}
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
+ if (ret) {
+ dev_err(component->dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(es8389->mclk);
+ return ret;
+ }
+
es8389_init(component);
es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
@@ -907,6 +979,8 @@ static void es8389_i2c_shutdown(struct i2c_client *i2c)
regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
}
static int es8389_i2c_probe(struct i2c_client *i2c_client)
diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h
index 123d1e4b2d53..d21e72f876a6 100644
--- a/sound/soc/codecs/es8389.h
+++ b/sound/soc/codecs/es8389.h
@@ -137,4 +137,12 @@
#define ES8389_STATE_ON (13 << 0)
#define ES8389_STATE_STANDBY (7 << 0)
+enum ES8389_supplies {
+ ES8389_SUPPLY_VD = 0,
+ ES8389_SUPPLY_VA,
+};
+
+#define ES8389_3V3 1
+#define ES8389_1V8 0
+
#endif
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index 3dd4dd94bc37..ff58805e97d1 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -1067,6 +1067,9 @@ static int max98390_i2c_probe(struct i2c_client *i2c)
reset_gpio = devm_gpiod_get_optional(&i2c->dev,
"reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(reset_gpio),
+ "Failed to get reset gpio\n");
/* Power on device */
if (reset_gpio) {
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index 3beb3c44dc2c..ffb526de0021 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -11,10 +11,10 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmi.h>
-#include <linux/init.h>
#include <linux/i2c.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/math64.h>
+#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -24,6 +24,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+
#include "nau8821.h"
#define NAU8821_QUIRK_JD_ACTIVE_HIGH BIT(0)
@@ -806,16 +807,20 @@ nau8821_get_osr(struct nau8821 *nau8821, int stream)
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
osr &= NAU8821_DAC_OVERSAMPLE_MASK;
+
if (osr >= ARRAY_SIZE(osr_dac_sel))
return NULL;
+
return &osr_dac_sel[osr];
- } else {
- regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
- osr &= NAU8821_ADC_SYNC_DOWN_MASK;
- if (osr >= ARRAY_SIZE(osr_adc_sel))
- return NULL;
- return &osr_adc_sel[osr];
}
+
+ regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
+ osr &= NAU8821_ADC_SYNC_DOWN_MASK;
+
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return NULL;
+
+ return &osr_adc_sel[osr];
}
static int nau8821_dai_startup(struct snd_pcm_substream *substream,
@@ -868,15 +873,16 @@ static int nau8821_hw_params(struct snd_pcm_substream *substream,
if (ctrl_val & NAU8821_I2S_MS_MASTER) {
/* get the bclk and fs ratio */
bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs;
+
if (bclk_fs <= 32)
clk_div = 3;
else if (bclk_fs <= 64)
clk_div = 2;
else if (bclk_fs <= 128)
clk_div = 1;
- else {
+ else
return -EINVAL;
- }
+
regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2,
NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK,
(clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div);
@@ -1264,6 +1270,14 @@ static int nau8821_component_probe(struct snd_soc_component *component)
return 0;
}
+static void nau8821_component_remove(struct snd_soc_component *component)
+{
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (nau8821->jdet_active)
+ cancel_delayed_work_sync(&nau8821->jdet_work);
+};
+
/**
* nau8821_calc_fll_param - Calculate FLL parameters.
* @fll_in: external clock provided to codec.
@@ -1597,6 +1611,10 @@ static int __maybe_unused nau8821_suspend(struct snd_soc_component *component)
if (nau8821->irq)
disable_irq(nau8821->irq);
+
+ if (nau8821->jdet_active)
+ cancel_delayed_work_sync(&nau8821->jdet_work);
+
snd_soc_dapm_force_bias_level(nau8821->dapm, SND_SOC_BIAS_OFF);
/* Power down codec power; don't support button wakeup */
snd_soc_dapm_disable_pin(nau8821->dapm, "MICBIAS");
@@ -1621,6 +1639,7 @@ static int __maybe_unused nau8821_resume(struct snd_soc_component *component)
static const struct snd_soc_component_driver nau8821_component_driver = {
.probe = nau8821_component_probe,
+ .remove = nau8821_component_remove,
.set_sysclk = nau8821_set_sysclk,
.set_pll = nau8821_set_fll,
.set_bias_level = nau8821_set_bias_level,
@@ -1655,17 +1674,20 @@ int nau8821_enable_jack_detect(struct snd_soc_component *component,
int ret;
nau8821->jack = jack;
+
+ if (nau8821->jdet_active)
+ return 0;
+
/* Initiate jack detection work queue */
INIT_DELAYED_WORK(&nau8821->jdet_work, nau8821_jdet_work);
+ nau8821->jdet_active = true;
ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL,
nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"nau8821", nau8821);
- if (ret) {
+ if (ret)
dev_err(nau8821->dev, "Cannot request irq %d (%d)\n",
nau8821->irq, ret);
- return ret;
- }
return ret;
}
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
index 88602923780d..f9d7cd8cbd21 100644
--- a/sound/soc/codecs/nau8821.h
+++ b/sound/soc/codecs/nau8821.h
@@ -562,6 +562,7 @@ struct nau8821 {
struct snd_soc_dapm_context *dapm;
struct snd_soc_jack *jack;
struct delayed_work jdet_work;
+ bool jdet_active;
int irq;
int clk_id;
int micbias_voltage;
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index e6142645b903..977881994e60 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -502,12 +502,8 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg)
case 0x2000301c:
case 0x2000900f:
case 0x20009018:
- case 0x3fc29d80 ... 0x3fc29d83:
- case 0x3fe2e000 ... 0x3fe2e003:
- case 0x3fc2ab80 ... 0x3fc2abd4:
- case 0x3fc2bfc0 ... 0x3fc2bfc8:
- case 0x3fc2d300 ... 0x3fc2d354:
- case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ case 0x3fc000c0 ... 0x3fc2dfc8:
+ case 0x3fe00000 ... 0x3fe36fff:
/* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
@@ -557,6 +553,8 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xc48c ... 0xc48f:
case 0xc560:
case 0xc5b5 ... 0xc5b7:
+ case 0xc5c3:
+ case 0xc5c8:
case 0xc5fc ... 0xc5ff:
case 0xc680 ... 0xc683:
case 0xc820:
@@ -590,6 +588,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xdd0c ... 0xdd13:
case 0xde02:
case 0xdf14 ... 0xdf1b:
+ case 0xe80b:
case 0xe83c ... 0xe847:
case 0xf01e:
case 0xf717 ... 0xf719:
@@ -602,7 +601,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0x2000301c:
case 0x2000900f:
case 0x20009018:
- case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2ab80 ... 0x3fc2ac4c:
case 0x3fc2b780:
case 0x3fc2bf80 ... 0x3fc2bf83:
case 0x3fc2bfc0 ... 0x3fc2bfc8:
@@ -720,6 +719,13 @@ static int rt1320_read_prop(struct sdw_slave *slave)
j++;
}
+ prop->dp0_prop = devm_kzalloc(&slave->dev, sizeof(*prop->dp0_prop), GFP_KERNEL);
+ if (!prop->dp0_prop)
+ return -ENOMEM;
+
+ prop->dp0_prop->simple_ch_prep_sm = true;
+ prop->dp0_prop->ch_prep_timeout = 10;
+
/* set the timeout values */
prop->clk_stop_timeout = 64;
@@ -754,6 +760,515 @@ static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned
return 0;
}
+static void rt1320_data_rw(struct rt1320_sdw_priv *rt1320, unsigned int start,
+ unsigned char *data, unsigned int size, enum rt1320_rw_type rw)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int tmp;
+ int ret = -1;
+ int i, j;
+
+ pm_runtime_set_autosuspend_delay(dev, 20000);
+ pm_runtime_mark_last_busy(dev);
+
+ switch (rw) {
+ case RT1320_BRA_WRITE:
+ case RT1320_BRA_READ:
+ ret = sdw_bpt_send_sync(rt1320->sdw_slave->bus, rt1320->sdw_slave, &rt1320->bra_msg);
+ if (ret < 0)
+ dev_err(dev, "%s: Failed to send BRA message: %d\n", __func__, ret);
+ fallthrough;
+ case RT1320_PARAM_WRITE:
+ case RT1320_PARAM_READ:
+ if (ret < 0) {
+ /* if BRA fails, we try to access by the control word */
+ if (rw == RT1320_BRA_WRITE || rw == RT1320_BRA_READ) {
+ for (i = 0; i < rt1320->bra_msg.sections; i++) {
+ pm_runtime_mark_last_busy(dev);
+ for (j = 0; j < rt1320->bra_msg.sec[i].len; j++) {
+ if (rw == RT1320_BRA_WRITE) {
+ regmap_write(rt1320->regmap,
+ rt1320->bra_msg.sec[i].addr + j, rt1320->bra_msg.sec[i].buf[j]);
+ } else {
+ regmap_read(rt1320->regmap, rt1320->bra_msg.sec[i].addr + j, &tmp);
+ rt1320->bra_msg.sec[i].buf[j] = tmp;
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ if (rw == RT1320_PARAM_WRITE)
+ regmap_write(rt1320->regmap, start + i, data[i]);
+ else {
+ regmap_read(rt1320->regmap, start + i, &tmp);
+ data[i] = tmp;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_mark_last_busy(dev);
+}
+
+static unsigned long long rt1320_rsgain_to_rsratio(struct rt1320_sdw_priv *rt1320, unsigned int rsgain)
+{
+ unsigned long long base = 1000000000U;
+ unsigned long long step = 1960784U;
+ unsigned long long tmp, result;
+
+ if (rsgain == 0 || rsgain == 0x1ff)
+ result = 1000000000;
+ else if (rsgain & 0x100) {
+ tmp = 0xff - (rsgain & 0xff);
+ tmp = tmp * step;
+ result = base + tmp;
+ } else {
+ tmp = (rsgain & 0xff);
+ tmp = tmp * step;
+ result = base - tmp;
+ }
+
+ return result;
+}
+
+static void rt1320_pr_read(struct rt1320_sdw_priv *rt1320, unsigned int reg, unsigned int *val)
+{
+ unsigned int byte3, byte2, byte1, byte0;
+
+ regmap_write(rt1320->regmap, 0xc483, 0x80);
+ regmap_write(rt1320->regmap, 0xc482, 0x40);
+ regmap_write(rt1320->regmap, 0xc481, 0x0c);
+ regmap_write(rt1320->regmap, 0xc480, 0x10);
+
+ regmap_write(rt1320->regmap, 0xc487, ((reg & 0xff000000) >> 24));
+ regmap_write(rt1320->regmap, 0xc486, ((reg & 0x00ff0000) >> 16));
+ regmap_write(rt1320->regmap, 0xc485, ((reg & 0x0000ff00) >> 8));
+ regmap_write(rt1320->regmap, 0xc484, (reg & 0x000000ff));
+
+ regmap_write(rt1320->regmap, 0xc482, 0xc0);
+
+ regmap_read(rt1320->regmap, 0xc48f, &byte3);
+ regmap_read(rt1320->regmap, 0xc48e, &byte2);
+ regmap_read(rt1320->regmap, 0xc48d, &byte1);
+ regmap_read(rt1320->regmap, 0xc48c, &byte0);
+
+ *val = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0;
+}
+
+static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int tmp, retry = 0;
+ unsigned int cmd_addr;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ cmd_addr = RT1320_CMD_ID;
+ break;
+ case RT1321_DEV_ID:
+ cmd_addr = RT1321_CMD_ID;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ /* check the value of cmd_addr becomes to zero */
+ while (retry < 500) {
+ regmap_read(rt1320->regmap, cmd_addr, &tmp);
+ if (tmp == 0)
+ break;
+ usleep_range(1000, 1100);
+ retry++;
+ }
+ if (retry == 500) {
+ dev_warn(dev, "%s FW is NOT ready!", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int retry = 0, tmp;
+
+ pm_runtime_mark_last_busy(dev);
+ while (retry < 200) {
+ regmap_read(rt1320->regmap, RT1320_POWER_STATE, &tmp);
+ dev_dbg(dev, "%s, RT1320_POWER_STATE=0x%x\n", __func__, tmp);
+ if (tmp >= ps)
+ break;
+ usleep_range(1000, 1500);
+ retry++;
+ }
+ if (retry == 200) {
+ dev_warn(dev, "%s FW Power State is NOT ready!", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int rt1320_process_fw_param(struct rt1320_sdw_priv *rt1320, unsigned char *buf, unsigned int buf_size)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ struct rt1320_paramcmd *paramhr = (struct rt1320_paramcmd *)buf;
+ unsigned char moudleid = paramhr->moudleid;
+ unsigned char cmdtype = paramhr->commandtype;
+ unsigned int fw_param_addr;
+ unsigned int start_addr;
+ int ret = 0;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ fw_param_addr = RT1320_FW_PARAM_ADDR;
+ start_addr = RT1320_CMD_PARAM_ADDR;
+ break;
+ case RT1321_DEV_ID:
+ fw_param_addr = RT1321_FW_PARAM_ADDR;
+ start_addr = RT1321_CMD_PARAM_ADDR;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ goto _timeout_;
+
+ /* don't set offset 0x0/0x1, it will be set later*/
+ paramhr->moudleid = 0;
+ paramhr->commandtype = 0;
+ rt1320_data_rw(rt1320, fw_param_addr, buf, buf_size, RT1320_PARAM_WRITE);
+
+ dev_dbg(dev, "%s, moudleid=%d, cmdtype=%d, paramid=%d, paramlength=%d\n", __func__,
+ moudleid, cmdtype, paramhr->paramid, paramhr->paramlength);
+
+ if (cmdtype == RT1320_SET_PARAM) {
+ regmap_write(rt1320->regmap, fw_param_addr, moudleid);
+ regmap_write(rt1320->regmap, fw_param_addr + 1, 0x01);
+ }
+ if (cmdtype == RT1320_GET_PARAM) {
+ regmap_write(rt1320->regmap, fw_param_addr, moudleid);
+ regmap_write(rt1320->regmap, fw_param_addr + 1, 0x02);
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ goto _timeout_;
+
+ rt1320_data_rw(rt1320, start_addr, buf + 0x10, paramhr->commandlength, RT1320_PARAM_READ);
+ }
+ return 0;
+
+_timeout_:
+ dev_err(&rt1320->sdw_slave->dev, "%s: FW is NOT ready for SET/GET_PARAM\n", __func__);
+ return ret;
+}
+
+static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_fw_cmdid cmdid,
+ unsigned int paramid, void *parambuf, unsigned int paramsize)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned char *tempbuf = NULL;
+ struct rt1320_paramcmd paramhr;
+ int ret = 0;
+
+ tempbuf = kzalloc(sizeof(paramhr) + paramsize, GFP_KERNEL);
+ if (!tempbuf)
+ return -ENOMEM;
+
+ paramhr.moudleid = 1;
+ paramhr.commandtype = cmdid;
+ /* 8 is "sizeof(paramid) + sizeof(paramlength)" */
+ paramhr.commandlength = 8 + paramsize;
+ paramhr.paramid = paramid;
+ paramhr.paramlength = paramsize;
+
+ memcpy(tempbuf, &paramhr, sizeof(paramhr));
+ if (cmdid == RT1320_SET_PARAM)
+ memcpy(tempbuf + sizeof(paramhr), parambuf, paramsize);
+
+ ret = rt1320_process_fw_param(rt1320, tempbuf, sizeof(paramhr) + paramsize);
+ if (ret < 0) {
+ dev_err(dev, "%s: process_fw_param failed\n", __func__);
+ goto _finish_;
+ }
+
+ if (cmdid == RT1320_GET_PARAM)
+ memcpy(parambuf, tempbuf + sizeof(paramhr), paramsize);
+
+_finish_:
+ kfree(tempbuf);
+ return ret;
+}
+
+static void rt1320_set_advancemode(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ struct rt1320_datafixpoint r0_data[2];
+ unsigned short l_advancegain, r_advancegain;
+ int ret;
+
+ /* Get advance gain/r0 */
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+ l_advancegain = r0_data[0].advancegain;
+ r_advancegain = r0_data[1].advancegain;
+ dev_dbg(dev, "%s, LR advanceGain=0x%x 0x%x\n", __func__, l_advancegain, r_advancegain);
+
+ /* set R0 and enable protection by SetParameter id 6, 7 */
+ r0_data[0].silencedetect = 0;
+ r0_data[0].r0 = rt1320->r0_l_reg;
+ r0_data[1].silencedetect = 0;
+ r0_data[1].r0 = rt1320->r0_r_reg;
+ dev_dbg(dev, "%s, write LR r0=%d, %d\n", __func__, r0_data[0].r0, r0_data[1].r0);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ dev_err(dev, "%s: Failed to set FW param 6,7!\n", __func__);
+
+ if (l_advancegain != 0 && r_advancegain != 0) {
+ regmap_write(rt1320->regmap, 0xdd0b, (l_advancegain & 0xff00) >> 8);
+ regmap_write(rt1320->regmap, 0xdd0a, (l_advancegain & 0xff));
+ regmap_write(rt1320->regmap, 0xdd09, (r_advancegain & 0xff00) >> 8);
+ regmap_write(rt1320->regmap, 0xdd08, (r_advancegain & 0xff));
+ dev_dbg(dev, "%s, set Advance mode gain\n", __func__);
+ }
+}
+
+static int rt1320_invrs_load(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned long long l_rsratio, r_rsratio;
+ unsigned int pr_1058, pr_1059, pr_105a;
+ unsigned long long l_invrs, r_invrs;
+ unsigned long long factor = (1 << 28);
+ unsigned int l_rsgain, r_rsgain;
+ struct rt1320_datafixpoint r0_data[2];
+ int ret;
+
+ /* read L/Rch Rs Gain - it uses for compensating the R0 value */
+ rt1320_pr_read(rt1320, 0x1058, &pr_1058);
+ rt1320_pr_read(rt1320, 0x1059, &pr_1059);
+ rt1320_pr_read(rt1320, 0x105a, &pr_105a);
+ l_rsgain = ((pr_1059 & 0x7f) << 2) | ((pr_105a & 0xc0) >> 6);
+ r_rsgain = ((pr_1058 & 0xff) << 1) | ((pr_1059 & 0x80) >> 7);
+ dev_dbg(dev, "%s, LR rsgain=0x%x, 0x%x\n", __func__, l_rsgain, r_rsgain);
+
+ l_rsratio = rt1320_rsgain_to_rsratio(rt1320, l_rsgain);
+ r_rsratio = rt1320_rsgain_to_rsratio(rt1320, r_rsgain);
+ dev_dbg(dev, "%s, LR rsratio=%lld, %lld\n", __func__, l_rsratio, r_rsratio);
+
+ l_invrs = div_u64(l_rsratio * factor, 1000000000U);
+ r_invrs = div_u64(r_rsratio * factor, 1000000000U);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+
+ r0_data[0].invrs = l_invrs;
+ r0_data[1].invrs = r_invrs;
+ dev_dbg(dev, "%s, write DSP LR invrs=0x%x, 0x%x\n", __func__, r0_data[0].invrs, r0_data[1].invrs);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint));
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint));
+ ret = rt1320_check_fw_ready(rt1320);
+ if (ret < 0)
+ dev_err(dev, "%s: Failed to set FW param 6,7!\n", __func__);
+
+ return ret;
+}
+
+static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned long long l_calir0, r_calir0, l_calir0_lo, r_calir0_lo;
+
+ l_calir0 = rt1320->r0_l_reg >> 27;
+ r_calir0 = rt1320->r0_r_reg >> 27;
+ l_calir0_lo = ((rt1320->r0_l_reg & ((1ull << 27) - 1)) * 1000) >> 27;
+ r_calir0_lo = ((rt1320->r0_r_reg & ((1ull << 27) - 1)) * 1000) >> 27;
+
+ dev_dbg(dev, "%s, l_calir0=%lld.%03lld ohm, r_calir0=%lld.%03lld ohm\n", __func__,
+ l_calir0, l_calir0_lo, r_calir0, r_calir0_lo);
+}
+
+static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ struct rt1320_datafixpoint audfixpoint[2];
+ unsigned int reg_c5fb, reg_c570, reg_cd00;
+ unsigned int vol_reg[4], fw_ready;
+ unsigned long long l_meanr0, r_meanr0;
+ unsigned int fw_status_addr;
+ int l_re[5], r_re[5];
+ int ret, tmp;
+ unsigned long long factor = (1 << 27);
+ unsigned short l_advancegain, r_advancegain;
+ unsigned int delay_s = 7; /* delay seconds for the calibration */
+
+ if (!rt1320->component)
+ return;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ fw_status_addr = RT1320_DSPFW_STATUS_ADDR;
+ break;
+ case RT1321_DEV_ID:
+ fw_status_addr = RT1321_DSPFW_STATUS_ADDR;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
+
+ /* set volume 0dB */
+ regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]);
+ regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]);
+ regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]);
+ regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]);
+ regmap_write(rt1320->regmap, 0xdd0b, 0x0f);
+ regmap_write(rt1320->regmap, 0xdd0a, 0xff);
+ regmap_write(rt1320->regmap, 0xdd09, 0x0f);
+ regmap_write(rt1320->regmap, 0xdd08, 0xff);
+
+ regmap_read(rt1320->regmap, 0xc5fb, &reg_c5fb);
+ regmap_read(rt1320->regmap, 0xc570, &reg_c570);
+ regmap_read(rt1320->regmap, 0xcd00, &reg_cd00);
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00);
+ ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, PDE=PS0 is NOT ready\n", __func__);
+ goto _finish_;
+ }
+
+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready);
+ fw_ready &= 0x1;
+ if (!fw_ready) {
+ dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__);
+ goto _finish_;
+ }
+
+ ret = rt1320_check_power_state_ready(rt1320, RT1320_NORMAL_STATE);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, DSP FW PS is NOT ready\n", __func__);
+ goto _finish_;
+ }
+
+ if (rt1320->dev_id == RT1320_DEV_ID)
+ regmap_write(rt1320->regmap, 0xc5fb, 0x00);
+ regmap_write(rt1320->regmap, 0xc570, 0x0b);
+ regmap_write(rt1320->regmap, 0xcd00, 0xc5);
+
+ /* disable silence detection */
+ regmap_update_bits(rt1320->regmap, 0xc044, 0xe0, 0x00);
+ dev_dbg(dev, "%s, disable silence detection\n", __func__);
+
+ ret = rt1320_check_power_state_ready(rt1320, RT1320_K_R0_STATE);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, check class D status before k r0\n", __func__);
+ goto _finish_;
+ }
+
+ for (tmp = 0; tmp < delay_s; tmp++) {
+ msleep(1000);
+ pm_runtime_mark_last_busy(dev);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 11, &l_re[0], sizeof(l_re));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 12, &r_re[0], sizeof(r_re));
+
+ dev_dbg(dev, "%s, LR re=0x%x, 0x%x\n", __func__, l_re[4], r_re[4]);
+ dev_dbg(dev, "%s, waiting for calibration R0...%d seconds\n", __func__, tmp + 1);
+ }
+
+ /* Get Calibration data */
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 11, &l_re[0], sizeof(l_re));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 12, &r_re[0], sizeof(r_re));
+ dev_dbg(dev, "%s, LR re=0x%x, 0x%x\n", __func__, l_re[4], r_re[4]);
+
+ /* Get advance gain/mean r0 */
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &audfixpoint[0], sizeof(struct rt1320_datafixpoint));
+ l_meanr0 = audfixpoint[0].meanr0;
+ l_advancegain = audfixpoint[0].advancegain;
+ l_meanr0 = ((l_meanr0 * 1000U) / factor);
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &audfixpoint[1], sizeof(struct rt1320_datafixpoint));
+ r_meanr0 = audfixpoint[1].meanr0;
+ r_advancegain = audfixpoint[1].advancegain;
+ r_meanr0 = ((r_meanr0 * 1000U) / factor);
+ dev_dbg(dev, "%s, LR meanr0=%lld, %lld\n", __func__, l_meanr0, r_meanr0);
+ dev_dbg(dev, "%s, LR advanceGain=0x%x, 0x%x\n", __func__, l_advancegain, r_advancegain);
+ dev_dbg(dev, "%s, LR invrs=0x%x, 0x%x\n", __func__, audfixpoint[0].invrs, audfixpoint[1].invrs);
+
+ /* enable silence detection */
+ regmap_update_bits(rt1320->regmap, 0xc044, 0xe0, 0xe0);
+ dev_dbg(dev, "%s, enable silence detection\n", __func__);
+
+ regmap_write(rt1320->regmap, 0xc5fb, reg_c5fb);
+ regmap_write(rt1320->regmap, 0xc570, reg_c570);
+ regmap_write(rt1320->regmap, 0xcd00, reg_cd00);
+
+ rt1320->r0_l_reg = l_re[4];
+ rt1320->r0_r_reg = r_re[4];
+ rt1320->cali_done = true;
+ rt1320_calc_r0(rt1320);
+
+_finish_:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03);
+
+ /* advance gain will be set when R0 load, not here */
+ regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]);
+ regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]);
+ regmap_write(rt1320->regmap, 0xdd09, vol_reg[1]);
+ regmap_write(rt1320->regmap, 0xdd08, vol_reg[0]);
+}
+
+static int rt1320_r0_cali_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->cali_done;
+ return 0;
+}
+
+static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ rt1320->cali_done = false;
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0]) {
+ rt1320_calibrate(rt1320);
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
/*
* The 'patch code' is written to the patch code area.
* The patch code area is used for SDCA register expansion flexibility.
@@ -844,6 +1359,431 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320)
}
}
+static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int factor = (1 << 22), fw_ready;
+ int l_t0_data[38], r_t0_data[38];
+ unsigned int fw_status_addr;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ fw_status_addr = RT1320_DSPFW_STATUS_ADDR;
+ break;
+ case RT1321_DEV_ID:
+ fw_status_addr = RT1321_DSPFW_STATUS_ADDR;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00);
+
+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready);
+ fw_ready &= 0x1;
+ if (!fw_ready) {
+ dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__);
+ goto _exit_;
+ }
+
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data));
+
+ l_t0_data[37] = l_t0 * factor;
+ r_t0_data[37] = r_t0 * factor;
+
+ dev_dbg(dev, "%s, write LR t0=0x%x, 0x%x\n", __func__, l_t0_data[37], r_t0_data[37]);
+
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_SET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data));
+ if (rt1320_check_fw_ready(rt1320) < 0)
+ dev_err(dev, "%s: Failed to set FW param 3,4!\n", __func__);
+
+ rt1320->temp_l_calib = l_t0;
+ rt1320->temp_r_calib = r_t0;
+
+ memset(&l_t0_data[0], 0x00, sizeof(l_t0_data));
+ memset(&r_t0_data[0], 0x00, sizeof(r_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 3, &l_t0_data[0], sizeof(l_t0_data));
+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 4, &r_t0_data[0], sizeof(r_t0_data));
+ dev_dbg(dev, "%s, read after writing LR t0=0x%x, 0x%x\n", __func__, l_t0_data[37], r_t0_data[37]);
+
+_exit_:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03);
+}
+
+static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = &rt1320->sdw_slave->dev;
+ static const char func_tag[] = "FUNC";
+ static const char xu_tag[] = "XU";
+ const struct firmware *rae_fw = NULL;
+ unsigned int fw_offset;
+ unsigned char *fw_data;
+ unsigned char *param_data;
+ unsigned int addr, size;
+ unsigned int func, value;
+ const char *dmi_vendor, *dmi_product, *dmi_sku;
+ int len_vendor, len_product, len_sku;
+ char rae_filename[512];
+ char tag[5];
+ int ret = 0;
+ int retry = 200;
+
+ dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME);
+ dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
+
+ if (dmi_vendor && dmi_product && dmi_sku) {
+ len_vendor = strchrnul(dmi_vendor, ' ') - dmi_vendor;
+ len_product = strchrnul(dmi_product, ' ') - dmi_product;
+ len_sku = strchrnul(dmi_sku, ' ') - dmi_sku;
+
+ snprintf(rae_filename, sizeof(rae_filename),
+ "realtek/rt1320/rt1320_RAE_%.*s_%.*s_%.*s.dat",
+ len_vendor, dmi_vendor, len_product, dmi_product, len_sku, dmi_sku);
+ dev_dbg(dev, "%s: try to load RAE file %s\n", __func__, rae_filename);
+ } else {
+ dev_warn(dev, "%s: Can't find proper RAE file name\n", __func__);
+ return -EINVAL;
+ }
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00);
+
+ request_firmware(&rae_fw, rae_filename, dev);
+ if (rae_fw) {
+
+ /* RAE CRC clear */
+ regmap_write(rt1320->regmap, 0xe80b, 0x0f);
+
+ /* RAE stop & CRC disable */
+ regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00);
+
+ while (--retry) {
+ regmap_read(rt1320->regmap, 0xe83f, &value);
+ if (value & 0x40)
+ break;
+ usleep_range(1000, 1100);
+ }
+ if (!retry && !(value & 0x40)) {
+ dev_err(dev, "%s: RAE is not ready to load\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size);
+ regcache_cache_bypass(rt1320->regmap, true);
+ for (fw_offset = 0; fw_offset < rae_fw->size;) {
+
+ dev_dbg(dev, "%s, fw_offset=0x%x\n", __func__, fw_offset);
+
+ fw_data = (unsigned char *)&rae_fw->data[fw_offset];
+
+ memcpy(tag, fw_data, 4);
+ tag[4] = '\0';
+ dev_dbg(dev, "%s, tag=%s\n", __func__, tag);
+ if (strcmp(tag, xu_tag) == 0) {
+ dev_dbg(dev, "%s: This is a XU tag", __func__);
+ memcpy(&addr, (fw_data + 4), 4);
+ memcpy(&size, (fw_data + 8), 4);
+ param_data = (unsigned char *)(fw_data + 12);
+
+ dev_dbg(dev, "%s: addr=0x%x, size=0x%x\n", __func__, addr, size);
+
+ /*
+ * UI register ranges from 0x1000d000 to 0x1000d7ff
+ * UI registers should be accessed by tuning tool.
+ * So, there registers should be cached.
+ */
+ if (addr <= 0x1000d7ff && addr >= 0x1000d000)
+ regcache_cache_bypass(rt1320->regmap, false);
+
+ rt1320_data_rw(rt1320, addr, param_data, size, RT1320_PARAM_WRITE);
+
+ regcache_cache_bypass(rt1320->regmap, true);
+
+ fw_offset += (size + 12);
+ } else if (strcmp(tag, func_tag) == 0) {
+ dev_err(dev, "%s: This is a FUNC tag", __func__);
+
+ memcpy(&func, (fw_data + 4), 4);
+ memcpy(&value, (fw_data + 8), 4);
+
+ dev_dbg(dev, "%s: func=0x%x, value=0x%x\n", __func__, func, value);
+ if (func == 1) //DelayMs
+ msleep(value);
+
+ fw_offset += 12;
+ } else {
+ dev_err(dev, "%s: This is NOT a XU file (wrong tag)", __func__);
+ break;
+ }
+ }
+
+ regcache_cache_bypass(rt1320->regmap, false);
+ release_firmware(rae_fw);
+
+ } else {
+ dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename);
+ ret = -EINVAL;
+ goto _exit_;
+ }
+
+ /* RAE CRC enable */
+ regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c);
+
+ /* RAE update */
+ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00);
+ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80);
+
+ /* RAE run */
+ regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80);
+
+ regmap_read(rt1320->regmap, 0xe80b, &value);
+ dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value);
+
+ rt1320->rae_update_done = true;
+
+_exit_:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03);
+
+ return ret;
+}
+
+static void rt1320_dspfw_load_code(struct rt1320_sdw_priv *rt1320)
+{
+struct rt1320_imageinfo {
+ unsigned int addr;
+ unsigned int size;
+};
+
+struct rt1320_dspfwheader {
+ unsigned int sync;
+ short num;
+ short crc;
+};
+
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ struct device *dev = &rt1320->sdw_slave->dev;
+ unsigned int val, i, fw_offset, fw_ready;
+ unsigned int fw_status_addr;
+ struct rt1320_dspfwheader *fwheader;
+ struct rt1320_imageinfo *ptr_img;
+ struct sdw_bpt_section sec[10];
+ const struct firmware *fw = NULL;
+ unsigned char *fw_data;
+ bool dev_fw_match = false;
+ static const char hdr_sig[] = "AFX";
+ unsigned int hdr_size = 0;
+ const char *dmi_vendor, *dmi_product, *dmi_sku;
+ int len_vendor, len_product, len_sku;
+ char filename[512];
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ fw_status_addr = RT1320_DSPFW_STATUS_ADDR;
+ break;
+ case RT1321_DEV_ID:
+ fw_status_addr = RT1321_DSPFW_STATUS_ADDR;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
+
+ dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME);
+ dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
+
+ if (dmi_vendor && dmi_product && dmi_sku) {
+ len_vendor = strchrnul(dmi_vendor, ' ') - dmi_vendor;
+ len_product = strchrnul(dmi_product, ' ') - dmi_product;
+ len_sku = strchrnul(dmi_sku, ' ') - dmi_sku;
+
+ snprintf(filename, sizeof(filename),
+ "realtek/rt1320/rt1320_%.*s_%.*s_%.*s.dat",
+ len_vendor, dmi_vendor, len_product, dmi_product, len_sku, dmi_sku);
+
+ dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename);
+ } else if (rt1320->dspfw_name) {
+ snprintf(filename, sizeof(filename), "rt1320_%s.dat",
+ rt1320->dspfw_name);
+ dev_dbg(dev, "%s: try to load FW file %s\n", __func__, filename);
+ } else {
+ dev_warn(dev, "%s: Can't find proper FW file name\n", __func__);
+ return;
+ }
+
+ snd_soc_dapm_mutex_lock(dapm);
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00);
+
+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready);
+ fw_ready &= 0x1;
+ if (fw_ready) {
+ dev_dbg(dev, "%s, DSP FW was already\n", __func__);
+ rt1320->fw_load_done = true;
+ goto _exit_;
+ }
+
+ /* change to IRAM */
+ regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00);
+
+ request_firmware(&fw, filename, dev);
+ if (fw) {
+ fwheader = (struct rt1320_dspfwheader *)fw->data;
+ dev_dbg(dev, "%s, fw sync = 0x%x, num=%d, crc=0x%x\n", __func__,
+ fwheader->sync, fwheader->num, fwheader->crc);
+
+ if (fwheader->sync != 0x0a1c5679) {
+ dev_err(dev, "%s: FW sync error\n", __func__);
+ release_firmware(fw);
+ goto _exit_;
+ }
+
+ fw_offset = sizeof(struct rt1320_dspfwheader) + (sizeof(struct rt1320_imageinfo) * fwheader->num);
+ dev_dbg(dev, "%s, fw_offset = 0x%x\n", __func__, fw_offset);
+
+ regcache_cache_bypass(rt1320->regmap, true);
+
+ for (i = 0; i < fwheader->num; i++) {
+ ptr_img = (struct rt1320_imageinfo *)&fw->data[sizeof(struct rt1320_dspfwheader) + (sizeof(struct rt1320_imageinfo) * i)];
+
+ dev_dbg(dev, "%s, fw_offset=0x%x, load fw addr=0x%x, size=%d\n", __func__,
+ fw_offset, ptr_img->addr, ptr_img->size);
+
+ fw_data = (unsigned char *)&fw->data[fw_offset];
+
+ /* The binary file has a header of 64 bytes */
+ if (memcmp(fw_data, hdr_sig, sizeof(hdr_sig)) == 0)
+ hdr_size = 64;
+ else
+ hdr_size = 0;
+
+ sec[i].addr = ptr_img->addr;
+ sec[i].len = ptr_img->size - hdr_size;
+ sec[i].buf = fw_data + hdr_size;
+
+ dev_dbg(dev, "%s, hdr_size=%d, sec[%d].buf[0]=0x%x\n",
+ __func__, hdr_size, i, sec[i].buf[0]);
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (ptr_img->addr == 0x3fc29d80)
+ if (fw_data[9] == '0')
+ dev_fw_match = true;
+ break;
+ case RT1321_DEV_ID:
+ if (ptr_img->addr == 0x3fc00000)
+ if (fw_data[9] == '1')
+ dev_fw_match = true;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ goto _exit_;
+ }
+
+ fw_offset += ptr_img->size;
+ }
+
+ if (dev_fw_match) {
+ dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__);
+ rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num;
+ rt1320->bra_msg.flags = SDW_MSG_FLAG_WRITE;
+ rt1320->bra_msg.sections = fwheader->num;
+ rt1320->bra_msg.sec = &sec[0];
+ rt1320_data_rw(rt1320, 0, NULL, 0, RT1320_BRA_WRITE);
+ dev_dbg(dev, "%s, BRA downloading FW done..\n", __func__);
+ }
+
+ regcache_cache_bypass(rt1320->regmap, false);
+ release_firmware(fw);
+
+ if (!dev_fw_match) {
+ dev_err(dev, "%s: FW file doesn't match to device\n", __func__);
+ goto _exit_;
+ }
+ } else {
+ dev_err(dev, "%s: Failed to load %s firmware\n", __func__, filename);
+ goto _exit_;
+ }
+
+ /* run RAM code */
+ regmap_read(rt1320->regmap, 0x3fc2bfc0, &val);
+ val |= 0x8;
+ regmap_write(rt1320->regmap, 0x3fc2bfc0, val);
+
+ /* clear frame counter */
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00);
+ break;
+ case RT1321_DEV_ID:
+ regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00);
+ regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00);
+ break;
+ }
+
+ /* enable DSP FW */
+ regmap_write(rt1320->regmap, 0xc081, 0xfc);
+ regmap_update_bits(rt1320->regmap, 0xf01e, 0x1, 0x0);
+
+ /* RsRatio should restore into DSP FW when FW was ready */
+ rt1320_invrs_load(rt1320);
+
+ /* DSP clock switches to PLL */
+ regmap_write(rt1320->regmap, 0xc081, 0xfc);
+ /* pass DSP settings */
+ regmap_write(rt1320->regmap, 0xc5c3, 0xf3);
+ regmap_write(rt1320->regmap, 0xc5c8, 0x05);
+
+ rt1320->fw_load_done = true;
+
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_mark_last_busy(dev);
+
+_exit_:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void rt1320_load_dspfw_work(struct work_struct *work)
+{
+ struct rt1320_sdw_priv *rt1320 =
+ container_of(work, struct rt1320_sdw_priv, load_dspfw_work);
+ int ret;
+
+ ret = pm_runtime_resume(rt1320->component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return;
+
+ dev_dbg(&rt1320->sdw_slave->dev, "%s, Starting to reload DSP FW", __func__);
+ rt1320_dspfw_load_code(rt1320);
+}
+
static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
{
struct sdw_slave *slave = rt1320->sdw_slave;
@@ -956,6 +1896,10 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
regmap_write(rt1320->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
FUNCTION_NEEDS_INITIALIZATION);
+
+ /* reload DSP FW */
+ if (rt1320->fw_load_done)
+ schedule_work(&rt1320->load_dspfw_work);
}
if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
@@ -1358,6 +2302,225 @@ static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum,
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320)
+{
+ struct device *dev = regmap_get_device(rt1320->regmap);
+ unsigned int fw_status_addr;
+ unsigned int fw_ready;
+ int ret = 0;
+
+ if (!rt1320->r0_l_reg || !rt1320->r0_r_reg)
+ return -EINVAL;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ fw_status_addr = RT1320_DSPFW_STATUS_ADDR;
+ break;
+ case RT1321_DEV_ID:
+ fw_status_addr = RT1321_DSPFW_STATUS_ADDR;
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00);
+ ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, PDE=PS0 is NOT ready\n", __func__);
+ goto _timeout_;
+ }
+
+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready);
+ fw_ready &= 0x1;
+ if (!fw_ready) {
+ dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__);
+ goto _timeout_;
+ }
+
+ ret = rt1320_check_power_state_ready(rt1320, RT1320_NORMAL_STATE);
+ if (ret < 0) {
+ dev_dbg(dev, "%s, DSP FW PS is NOT ready\n", __func__);
+ goto _timeout_;
+ }
+
+ rt1320_set_advancemode(rt1320);
+
+_timeout_:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03);
+
+ return ret;
+}
+
+static int rt1320_r0_load_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->r0_l_reg;
+ ucontrol->value.integer.value[1] = rt1320->r0_r_reg;
+
+ return 0;
+}
+
+static int rt1320_r0_load_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ if (ucontrol->value.integer.value[0] == 0 ||
+ ucontrol->value.integer.value[1] == 0)
+ return -EINVAL;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ rt1320->r0_l_reg = ucontrol->value.integer.value[0];
+ rt1320->r0_r_reg = ucontrol->value.integer.value[1];
+ rt1320_calc_r0(rt1320);
+ rt1320_r0_load(rt1320);
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static int rt1320_t0_r0_load_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.max = kcontrol->private_value;
+
+ return 0;
+}
+
+#define RT1320_T0_R0_LOAD(xname, xmax, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = rt1320_t0_r0_load_info, \
+ .get = xhandler_get, \
+ .put = xhandler_put, \
+ .private_value = xmax, \
+}
+
+static int rt1320_dspfw_load_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->fw_load_done;
+ return 0;
+}
+
+static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0])
+ rt1320_dspfw_load_code(rt1320);
+
+ if (!ucontrol->value.integer.value[0])
+ rt1320->fw_load_done = false;
+
+ return 0;
+}
+
+static int rt1320_rae_update_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->rae_update_done;
+ return 0;
+}
+
+static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF &&
+ ucontrol->value.integer.value[0] && rt1320->fw_load_done)
+ rt1320_rae_load(rt1320);
+
+ if (!ucontrol->value.integer.value[0])
+ rt1320->rae_update_done = false;
+
+ return 0;
+}
+
+static int rt1320_r0_temperature_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1320->temp_l_calib;
+ ucontrol->value.integer.value[1] = rt1320->temp_r_calib;
+ return 0;
+}
+
+static int rt1320_r0_temperature_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component);
+ int ret;
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if ((snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) &&
+ ucontrol->value.integer.value[0] && ucontrol->value.integer.value[1])
+ rt1320_t0_load(rt1320, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
static const struct snd_kcontrol_new rt1320_snd_controls[] = {
SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume",
SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
@@ -1371,6 +2534,17 @@ static const struct snd_kcontrol_new rt1320_snd_controls[] = {
RT_SDCA_EXT_TLV("FU Capture Volume",
SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
rt1320_set_gain_get, rt1320_set_gain_put, 4, 0x3f, in_vol_tlv, rt1320_dmic_fu_info),
+
+ SOC_SINGLE_EXT("R0 Calibration", SND_SOC_NOPM, 0, 1, 0,
+ rt1320_r0_cali_get, rt1320_r0_cali_put),
+ SOC_SINGLE_EXT("DSP FW Update", SND_SOC_NOPM, 0, 1, 0,
+ rt1320_dspfw_load_get, rt1320_dspfw_load_put),
+ RT1320_T0_R0_LOAD("R0 Load Mode", 0xffffffff,
+ rt1320_r0_load_mode_get, rt1320_r0_load_mode_put),
+ RT1320_T0_R0_LOAD("R0 Temperature", 0xff,
+ rt1320_r0_temperature_get, rt1320_r0_temperature_put),
+ SOC_SINGLE_EXT("RAE Update", SND_SOC_NOPM, 0, 1, 0,
+ rt1320_rae_update_get, rt1320_rae_update_put),
};
static const struct snd_kcontrol_new rt1320_spk_l_dac =
@@ -1606,6 +2780,18 @@ static int rt1320_sdw_component_probe(struct snd_soc_component *component)
if (ret < 0 && ret != -EACCES)
return ret;
+ /* Apply temperature and calibration data from device property */
+ if ((rt1320->temp_l_calib <= 0xff) && (rt1320->temp_l_calib > 0) &&
+ (rt1320->temp_r_calib <= 0xff) && (rt1320->temp_r_calib > 0))
+ rt1320_t0_load(rt1320, rt1320->temp_l_calib, rt1320->temp_r_calib);
+
+ if (rt1320->r0_l_calib && rt1320->r0_r_calib) {
+ rt1320->r0_l_reg = rt1320->r0_l_calib;
+ rt1320->r0_r_reg = rt1320->r0_r_calib;
+ rt1320_calc_r0(rt1320);
+ rt1320_r0_load(rt1320);
+ }
+
return 0;
}
@@ -1667,6 +2853,26 @@ static struct snd_soc_dai_driver rt1320_sdw_dai[] = {
},
};
+static int rt1320_parse_dp(struct rt1320_sdw_priv *rt1320, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,temperature_l_calib",
+ &rt1320->temp_l_calib);
+ device_property_read_u32(dev, "realtek,temperature_r_calib",
+ &rt1320->temp_r_calib);
+ device_property_read_u32(dev, "realtek,r0_l_calib",
+ &rt1320->r0_l_calib);
+ device_property_read_u32(dev, "realtek,r0_r_calib",
+ &rt1320->r0_r_calib);
+ device_property_read_string(dev, "realtek,dspfw-name",
+ &rt1320->dspfw_name);
+
+ dev_dbg(dev, "%s: temp_l_calib: %d temp_r_calib: %d r0_l_calib: %d, r0_r_calib: %d",
+ __func__, rt1320->temp_l_calib, rt1320->temp_r_calib, rt1320->r0_l_calib, rt1320->r0_r_calib);
+ dev_dbg(dev, "%s: dspfw_name: %s", __func__, rt1320->dspfw_name);
+
+ return 0;
+}
+
static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
struct regmap *mbq_regmap, struct sdw_slave *slave)
{
@@ -1685,6 +2891,8 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
regcache_cache_only(rt1320->regmap, true);
regcache_cache_only(rt1320->mbq_regmap, true);
+ rt1320_parse_dp(rt1320, dev);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -1696,6 +2904,8 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] =
rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true;
+ INIT_WORK(&rt1320->load_dspfw_work, rt1320_load_dspfw_work);
+
ret = devm_snd_soc_register_component(dev,
&soc_component_sdw_rt1320,
rt1320_sdw_dai,
@@ -1742,6 +2952,9 @@ static int rt1320_sdw_probe(struct sdw_slave *slave,
static int rt1320_sdw_remove(struct sdw_slave *slave)
{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev);
+
+ cancel_work_sync(&rt1320->load_dspfw_work);
pm_runtime_disable(&slave->dev);
return 0;
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
index a6d90e259dc9..5a9f496dd848 100644
--- a/sound/soc/codecs/rt1320-sdw.h
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -13,6 +13,7 @@
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/soc.h>
+#include "../../../drivers/soundwire/bus.h"
#define RT1320_DEV_ID 0x6981
#define RT1321_DEV_ID 0x7045
@@ -22,6 +23,8 @@
#define RT1320_DEV_ID_1 0xc405
#define RT1320_DEV_ID_0 0xc406
+#define RT1320_POWER_STATE 0xc560
+
#define RT1321_PATCH_MAIN_VER 0x1000cffe
#define RT1321_PATCH_BETA_VER 0x1000cfff
@@ -96,6 +99,57 @@ enum rt1320_version_id {
#define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
+#define RT1320_FW_PARAM_ADDR 0x3fc2ab80
+#define RT1320_CMD_ID 0x3fc2ab81
+#define RT1320_CMD_PARAM_ADDR 0x3fc2ab90
+#define RT1320_DSPFW_STATUS_ADDR 0x3fc2bfc4
+
+#define RT1321_FW_PARAM_ADDR 0x3fc2d300
+#define RT1321_CMD_ID 0x3fc2d301
+#define RT1321_CMD_PARAM_ADDR 0x3fc2d310
+#define RT1321_DSPFW_STATUS_ADDR 0x3fc2dfc4
+
+/* FW parameter id 6, 7 */
+struct rt1320_datafixpoint {
+ int silencedetect;
+ int r0;
+ int meanr0;
+ int advancegain;
+ int ts;
+ int re;
+ int t;
+ int invrs;
+};
+
+struct rt1320_paramcmd {
+ unsigned char moudleid;
+ unsigned char commandtype;
+ unsigned short reserved1;
+ unsigned int commandlength;
+ long long reserved2;
+ unsigned int paramid;
+ unsigned int paramlength;
+};
+
+enum rt1320_fw_cmdid {
+ RT1320_FW_READY,
+ RT1320_SET_PARAM,
+ RT1320_GET_PARAM,
+ RT1320_GET_POOLSIZE,
+};
+
+enum rt1320_power_state {
+ RT1320_NORMAL_STATE = 0x18,
+ RT1320_K_R0_STATE = 0x1b,
+};
+
+enum rt1320_rw_type {
+ RT1320_BRA_WRITE = 0,
+ RT1320_BRA_READ = 1,
+ RT1320_PARAM_WRITE = 2,
+ RT1320_PARAM_READ = 3,
+};
+
struct rt1320_sdw_priv {
struct snd_soc_component *component;
struct regmap *regmap;
@@ -108,6 +162,18 @@ struct rt1320_sdw_priv {
unsigned int dev_id;
bool fu_dapm_mute;
bool fu_mixer_mute[4];
+ unsigned long long r0_l_reg;
+ unsigned long long r0_r_reg;
+ unsigned int r0_l_calib;
+ unsigned int r0_r_calib;
+ unsigned int temp_l_calib;
+ unsigned int temp_r_calib;
+ const char *dspfw_name;
+ bool cali_done;
+ bool fw_load_done;
+ bool rae_update_done;
+ struct work_struct load_dspfw_work;
+ struct sdw_bpt_msg bra_msg;
};
#endif /* __RT1320_SDW_H__ */
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 5c33aeaced2f..bba714020c70 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1189,7 +1189,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c)
regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82);
if (rt274->i2c->irq) {
- ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq,
+ ret = devm_request_threaded_irq(&rt274->i2c->dev, rt274->i2c->irq, NULL, rt274_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274);
if (ret != 0) {
dev_err(&i2c->dev,
@@ -1205,15 +1205,6 @@ static int rt274_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt274_i2c_remove(struct i2c_client *i2c)
-{
- struct rt274_priv *rt274 = i2c_get_clientdata(i2c);
-
- if (i2c->irq)
- free_irq(i2c->irq, rt274);
-}
-
-
static struct i2c_driver rt274_i2c_driver = {
.driver = {
.name = "rt274",
@@ -1223,7 +1214,6 @@ static struct i2c_driver rt274_i2c_driver = {
#endif
},
.probe = rt274_i2c_probe,
- .remove = rt274_i2c_remove,
.id_table = rt274_i2c_id,
};
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 2fbb5860c421..195658f626cc 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1236,7 +1236,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c)
}
if (rt286->i2c->irq) {
- ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
+ ret = devm_request_threaded_irq(&rt286->i2c->dev, rt286->i2c->irq, NULL, rt286_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
if (ret != 0) {
dev_err(&i2c->dev,
@@ -1252,22 +1252,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt286_i2c_remove(struct i2c_client *i2c)
-{
- struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
-
- if (i2c->irq)
- free_irq(i2c->irq, rt286);
-}
-
-
static struct i2c_driver rt286_i2c_driver = {
.driver = {
.name = "rt286",
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
.probe = rt286_i2c_probe,
- .remove = rt286_i2c_remove,
.id_table = rt286_i2c_id,
};
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 02247593513a..7d532a5a5f73 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1282,7 +1282,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c)
rt298->is_hp_in = -1;
if (rt298->i2c->irq) {
- ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq,
+ ret = devm_request_threaded_irq(&rt298->i2c->dev, rt298->i2c->irq, NULL, rt298_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298);
if (ret != 0) {
dev_err(&i2c->dev,
@@ -1298,22 +1298,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt298_i2c_remove(struct i2c_client *i2c)
-{
- struct rt298_priv *rt298 = i2c_get_clientdata(i2c);
-
- if (i2c->irq)
- free_irq(i2c->irq, rt298);
-}
-
-
static struct i2c_driver rt298_i2c_driver = {
.driver = {
.name = "rt298",
.acpi_match_table = ACPI_PTR(rt298_acpi_match),
},
.probe = rt298_i2c_probe,
- .remove = rt298_i2c_remove,
.id_table = rt298_i2c_id,
};
diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c
new file mode 100644
index 000000000000..9a349965435b
--- /dev/null
+++ b/sound/soc/codecs/rt5575-spi.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5575-spi.c -- ALC5575 SPI driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+
+#include "rt5575-spi.h"
+
+#define RT5575_SPI_CMD_BURST_WRITE 5
+#define RT5575_SPI_BUF_LEN 240
+
+struct rt5575_spi_burst_write {
+ u8 cmd;
+ u32 addr;
+ u8 data[RT5575_SPI_BUF_LEN];
+ u8 dummy;
+} __packed;
+
+struct spi_device *rt5575_spi_get_device(struct device *dev)
+{
+ struct spi_device *spi;
+ struct spi_controller *ctlr;
+ struct device_node *spi_np;
+ u32 cs;
+
+ spi_np = of_parse_phandle(dev->of_node, "spi-parent", 0);
+ if (!spi_np) {
+ dev_err(dev, "Failed to get spi-parent phandle\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32_index(dev->of_node, "spi-parent", 1, &cs))
+ cs = 0;
+
+ ctlr = of_find_spi_controller_by_node(spi_np);
+ of_node_put(spi_np);
+ if (!ctlr) {
+ dev_err(dev, "Failed to get spi_controller\n");
+ return NULL;
+ }
+
+ if (cs >= ctlr->num_chipselect) {
+ dev_err(dev, "Chip select has wrong number %d\n", cs);
+ spi_controller_put(ctlr);
+ return NULL;
+ }
+
+ spi = spi_new_device(ctlr, &(struct spi_board_info){
+ .modalias = "rt5575",
+ .chip_select = cs,
+ .max_speed_hz = 10000000,
+ });
+
+ spi_controller_put(ctlr);
+ return spi;
+}
+
+/**
+ * rt5575_spi_burst_write - Write data to SPI by rt5575 address.
+ * @spi: SPI device.
+ * @addr: Start address.
+ * @txbuf: Data buffer for writing.
+ * @len: Data length.
+ *
+ */
+static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *txbuf, size_t len)
+{
+ struct rt5575_spi_burst_write buf = {
+ .cmd = RT5575_SPI_CMD_BURST_WRITE,
+ };
+ unsigned int end, offset = 0;
+
+ while (offset < len) {
+ if (offset + RT5575_SPI_BUF_LEN <= len)
+ end = RT5575_SPI_BUF_LEN;
+ else
+ end = len % RT5575_SPI_BUF_LEN;
+
+ buf.addr = cpu_to_le32(addr + offset);
+ memcpy(&buf.data, &txbuf[offset], end);
+ spi_write(spi, &buf, sizeof(buf));
+
+ offset += RT5575_SPI_BUF_LEN;
+ }
+}
+
+int rt5575_spi_fw_load(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct firmware *firmware;
+ int i, ret;
+ static const char * const fw_path[] = {
+ "realtek/rt5575/rt5575_fw1.bin",
+ "realtek/rt5575/rt5575_fw2.bin",
+ "realtek/rt5575/rt5575_fw3.bin",
+ "realtek/rt5575/rt5575_fw4.bin",
+ };
+ static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 };
+
+ for (i = 0; i < ARRAY_SIZE(fw_addr); i++) {
+ ret = request_firmware(&firmware, fw_path[i], dev);
+ if (ret) {
+ dev_err(dev, "Request firmware failure: %d\n", ret);
+ return ret;
+ }
+
+ rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size);
+ release_firmware(firmware);
+ }
+
+ return 0;
+}
diff --git a/sound/soc/codecs/rt5575-spi.h b/sound/soc/codecs/rt5575-spi.h
new file mode 100644
index 000000000000..084638820b6f
--- /dev/null
+++ b/sound/soc/codecs/rt5575-spi.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5575-spi.h -- ALC5575 SPI driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#ifndef __RT5575_SPI_H__
+#define __RT5575_SPI_H__
+
+#if IS_ENABLED(CONFIG_SND_SOC_RT5575_SPI)
+struct spi_device *rt5575_spi_get_device(struct device *dev);
+int rt5575_spi_fw_load(struct spi_device *spi);
+#else
+static inline struct spi_device *rt5575_spi_get_device(struct device *dev)
+{
+ return NULL;
+}
+
+static inline int rt5575_spi_fw_load(struct spi_device *spi)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* __RT5575_SPI_H__ */
diff --git a/sound/soc/codecs/rt5575.c b/sound/soc/codecs/rt5575.c
new file mode 100644
index 000000000000..c5525ad195ee
--- /dev/null
+++ b/sound/soc/codecs/rt5575.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5575.c -- ALC5575 ALSA SoC audio component driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rt5575.h"
+#include "rt5575-spi.h"
+
+static bool rt5575_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5575_BOOT:
+ case RT5575_ID:
+ case RT5575_ID_1:
+ case RT5575_MIXL_VOL:
+ case RT5575_MIXR_VOL:
+ case RT5575_PROMPT_VOL:
+ case RT5575_SPK01_VOL:
+ case RT5575_SPK23_VOL:
+ case RT5575_MIC1_VOL:
+ case RT5575_MIC2_VOL:
+ case RT5575_WNC_CTRL:
+ case RT5575_MODE_CTRL:
+ case RT5575_I2S_RATE_CTRL:
+ case RT5575_SLEEP_CTRL:
+ case RT5575_ALG_BYPASS_CTRL:
+ case RT5575_PINMUX_CTRL_2:
+ case RT5575_GPIO_CTRL_1:
+ case RT5575_DSP_BUS_CTRL:
+ case RT5575_SW_INT:
+ case RT5575_DSP_BOOT_ERR:
+ case RT5575_DSP_READY:
+ case RT5575_DSP_CMD_ADDR:
+ case RT5575_EFUSE_DATA_2:
+ case RT5575_EFUSE_DATA_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const DECLARE_TLV_DB_SCALE(ob_tlv, -9525, 75, 0);
+
+static const struct snd_kcontrol_new rt5575_snd_controls[] = {
+ SOC_DOUBLE("Speaker CH-01 Playback Switch", RT5575_SPK01_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Speaker CH-01 Playback Volume", RT5575_SPK01_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE("Speaker CH-23 Playback Switch", RT5575_SPK23_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Speaker CH-23 Playback Volume", RT5575_SPK23_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE("Mic1 Capture Switch", RT5575_MIC1_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Mic1 Capture Volume", RT5575_MIC1_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE("Mic2 Capture Switch", RT5575_MIC2_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Mic2 Capture Volume", RT5575_MIC2_VOL, 17, 1, 167, 0, ob_tlv),
+ SOC_DOUBLE_R("Mix Playback Switch", RT5575_MIXL_VOL, RT5575_MIXR_VOL, 31, 1, 1),
+ SOC_DOUBLE_R_TLV("Mix Playback Volume", RT5575_MIXL_VOL, RT5575_MIXR_VOL, 1, 127, 0,
+ ob_tlv),
+ SOC_DOUBLE("Prompt Playback Switch", RT5575_PROMPT_VOL, 31, 15, 1, 1),
+ SOC_DOUBLE_TLV("Prompt Playback Volume", RT5575_PROMPT_VOL, 17, 1, 167, 0, ob_tlv),
+};
+
+static const struct snd_soc_dapm_widget rt5575_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF4RX", "AIF4 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_INPUT("INPUT"),
+ SND_SOC_DAPM_OUTPUT("OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route rt5575_dapm_routes[] = {
+ { "AIF1TX", NULL, "INPUT" },
+ { "AIF2TX", NULL, "INPUT" },
+ { "AIF3TX", NULL, "INPUT" },
+ { "AIF4TX", NULL, "INPUT" },
+ { "OUTPUT", NULL, "AIF1RX" },
+ { "OUTPUT", NULL, "AIF2RX" },
+ { "OUTPUT", NULL, "AIF3RX" },
+ { "OUTPUT", NULL, "AIF4RX" },
+};
+
+static long long rt5575_get_priv_id(struct rt5575_priv *rt5575)
+{
+ int priv_id_low, priv_id_high;
+
+ regmap_write(rt5575->regmap, RT5575_EFUSE_PID, 0xa0000000);
+ regmap_read(rt5575->regmap, RT5575_EFUSE_DATA_2, &priv_id_low);
+ regmap_read(rt5575->regmap, RT5575_EFUSE_DATA_3, &priv_id_high);
+ regmap_write(rt5575->regmap, RT5575_EFUSE_PID, 0);
+
+ return ((long long)priv_id_high << 32) | (long long)priv_id_low;
+}
+
+static int rt5575_probe(struct snd_soc_component *component)
+{
+ struct rt5575_priv *rt5575 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ rt5575->component = component;
+
+ dev_info(dev, "Private ID: %llx\n", rt5575_get_priv_id(rt5575));
+
+ return 0;
+}
+
+#define RT5575_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5575_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt5575_dai[] = {
+ {
+ .name = "rt5575-aif1",
+ .id = RT5575_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+ {
+ .name = "rt5575-aif2",
+ .id = RT5575_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+ {
+ .name = "rt5575-aif3",
+ .id = RT5575_AIF3,
+ .playback = {
+ .stream_name = "AIF3 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF3 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+ {
+ .name = "rt5575-aif4",
+ .id = RT5575_AIF4,
+ .playback = {
+ .stream_name = "AIF4 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF4 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = RT5575_STEREO_RATES,
+ .formats = RT5575_FORMATS,
+ },
+ },
+};
+
+static const struct snd_soc_component_driver rt5575_soc_component_dev = {
+ .probe = rt5575_probe,
+ .controls = rt5575_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5575_snd_controls),
+ .dapm_widgets = rt5575_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5575_dapm_widgets),
+ .dapm_routes = rt5575_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5575_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt5575_dsp_regmap = {
+ .name = "dsp",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 2,
+};
+
+static int rt5575_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ struct rt5575_priv *rt5575 = i2c_get_clientdata(client);
+
+ return regmap_read(rt5575->dsp_regmap, reg | RT5575_DSP_MAPPING, val);
+}
+
+static int rt5575_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct i2c_client *client = context;
+ struct rt5575_priv *rt5575 = i2c_get_clientdata(client);
+
+ return regmap_write(rt5575->dsp_regmap, reg | RT5575_DSP_MAPPING, val);
+}
+
+static const struct regmap_config rt5575_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0xfffc,
+ .readable_reg = rt5575_readable_register,
+ .reg_read = rt5575_i2c_read,
+ .reg_write = rt5575_i2c_write,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt5575_fw_load_by_spi(struct rt5575_priv *rt5575)
+{
+ struct i2c_client *i2c = rt5575->i2c;
+ struct spi_device *spi;
+ struct device *dev = &i2c->dev;
+ int ret;
+
+ spi = rt5575_spi_get_device(dev);
+ if (!spi) {
+ dev_err(dev, "Failed to get spi_device\n");
+ return -ENODEV;
+ }
+
+ regmap_write(rt5575->dsp_regmap, 0xfafafafa, 0x00000004);
+ regmap_write(rt5575->dsp_regmap, 0x18008064, 0x00000000);
+ regmap_write(rt5575->dsp_regmap, 0x18008068, 0x0002ffff);
+
+ ret = rt5575_spi_fw_load(spi);
+ if (ret) {
+ dev_err(dev, "Load firmware failure: %d\n", ret);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5575->dsp_regmap, 0x18000000, 0x00000000);
+ regmap_update_bits(rt5575->regmap, RT5575_SW_INT, 1, 1);
+
+ regmap_read_poll_timeout(rt5575->regmap, RT5575_SW_INT, ret, !ret, 100000, 10000000);
+ if (ret) {
+ dev_err(dev, "Run firmware failure: %d\n", ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rt5575_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt5575_priv *rt5575;
+ int ret, val, boot;
+ struct device *dev = &i2c->dev;
+
+ rt5575 = devm_kzalloc(dev, sizeof(struct rt5575_priv), GFP_KERNEL);
+ if (!rt5575)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5575);
+
+ rt5575->i2c = i2c;
+
+ rt5575->dsp_regmap = devm_regmap_init_i2c(i2c, &rt5575_dsp_regmap);
+ if (IS_ERR(rt5575->dsp_regmap)) {
+ ret = PTR_ERR(rt5575->dsp_regmap);
+ dev_err(dev, "Failed to allocate DSP register map: %d\n", ret);
+ return ret;
+ }
+
+ rt5575->regmap = devm_regmap_init(dev, NULL, i2c, &rt5575_regmap);
+ if (IS_ERR(rt5575->regmap)) {
+ ret = PTR_ERR(rt5575->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ regmap_read(rt5575->regmap, RT5575_ID, &val);
+ if (val != RT5575_DEVICE_ID) {
+ dev_err(dev, "Device with ID register %08x is not rt5575\n", val);
+ return -ENODEV;
+ }
+
+ regmap_read(rt5575->regmap, RT5575_BOOT, &boot);
+ if ((boot & RT5575_BOOT_MASK) == RT5575_BOOT_SPI) {
+ if (!IS_ENABLED(CONFIG_SND_SOC_RT5575_SPI)) {
+ dev_err(dev, "Please enable CONFIG_SND_SOC_RT5575_SPI\n");
+ return -ENODEV;
+ }
+
+ if (rt5575_fw_load_by_spi(rt5575))
+ return -ENODEV;
+ }
+
+ return devm_snd_soc_register_component(dev, &rt5575_soc_component_dev, rt5575_dai,
+ ARRAY_SIZE(rt5575_dai));
+}
+
+static const struct i2c_device_id rt5575_i2c_id[] = {
+ { "rt5575" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5575_i2c_id);
+
+static const struct of_device_id rt5575_of_match[] = {
+ { .compatible = "realtek,rt5575" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5575_of_match);
+
+static struct i2c_driver rt5575_i2c_driver = {
+ .driver = {
+ .name = "rt5575",
+ .owner = THIS_MODULE,
+ .of_match_table = rt5575_of_match,
+ },
+ .probe = rt5575_i2c_probe,
+ .id_table = rt5575_i2c_id,
+};
+module_i2c_driver(rt5575_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ALC5575 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt5575.h b/sound/soc/codecs/rt5575.h
new file mode 100644
index 000000000000..752a3c8f5aa9
--- /dev/null
+++ b/sound/soc/codecs/rt5575.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5575.h -- ALC5575 ALSA SoC audio driver
+ *
+ * Copyright(c) 2025 Realtek Semiconductor Corp.
+ *
+ */
+
+#ifndef __RT5575_H__
+#define __RT5575_H__
+
+#define RT5575_DEVICE_ID 0x10ec5575
+#define RT5575_DSP_MAPPING 0x18000000
+
+#define RT5575_BOOT 0x8004
+#define RT5575_ID 0x8008
+#define RT5575_ID_1 0x800c
+#define RT5575_MIXL_VOL 0x8a14
+#define RT5575_MIXR_VOL 0x8a18
+#define RT5575_PROMPT_VOL 0x8a84
+#define RT5575_SPK01_VOL 0x8a88
+#define RT5575_SPK23_VOL 0x8a8c
+#define RT5575_MIC1_VOL 0x8a98
+#define RT5575_MIC2_VOL 0x8a9c
+#define RT5575_WNC_CTRL 0x80ec
+#define RT5575_MODE_CTRL 0x80f0
+#define RT5575_I2S_RATE_CTRL 0x80f4
+#define RT5575_SLEEP_CTRL 0x80f8
+#define RT5575_ALG_BYPASS_CTRL 0x80fc
+#define RT5575_PINMUX_CTRL_2 0x81a4
+#define RT5575_GPIO_CTRL_1 0x8208
+#define RT5575_DSP_BUS_CTRL 0x880c
+#define RT5575_SW_INT 0x0018
+#define RT5575_DSP_BOOT_ERR 0x8e14
+#define RT5575_DSP_READY 0x8e24
+#define RT5575_DSP_CMD_ADDR 0x8e28
+#define RT5575_EFUSE_DATA_2 0xc638
+#define RT5575_EFUSE_DATA_3 0xc63c
+#define RT5575_EFUSE_PID 0xc660
+
+#define RT5575_BOOT_MASK 0x3
+#define RT5575_BOOT_SPI 0x0
+
+enum {
+ RT5575_AIF1,
+ RT5575_AIF2,
+ RT5575_AIF3,
+ RT5575_AIF4,
+ RT5575_AIFS,
+};
+
+struct rt5575_priv {
+ struct i2c_client *i2c;
+ struct snd_soc_component *component;
+ struct regmap *dsp_regmap, *regmap;
+};
+
+#endif /* __RT5575_H__ */
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 4c08c274f50e..db2222e6f2e7 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -2564,7 +2564,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
rt5640->use_platform_clock = jack_data->use_platform_clock;
ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
@@ -2618,7 +2618,7 @@ static void rt5640_enable_hda_jack_detect(
rt5640->jack = jack;
ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ IRQF_TRIGGER_RISING, "rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
rt5640->jack = NULL;
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 9af65a38f0ee..23c4bf3da298 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -2262,7 +2262,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c)
ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651);
+ | IRQF_NO_AUTOEN, "rt5651", rt5651);
if (ret) {
dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n",
rt5651->irq, ret);
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index f5957470652c..3590ebd41c27 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -4118,7 +4118,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
rt5659->gpiod_reset = devm_gpiod_get_optional(&i2c->dev, "reset",
GPIOD_OUT_HIGH);
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
msleep(300);
rt5659->regmap = devm_regmap_init_i2c(i2c, &rt5659_regmap);
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index e4d8785e64c1..eee1c98cc4aa 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3689,8 +3689,8 @@ static int rt5663_i2c_probe(struct i2c_client *i2c)
if (i2c->irq) {
ret = request_irq(i2c->irq, rt5663_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT, "rt5663", rt5663);
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5663", rt5663);
if (ret) {
dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n",
__func__, ret);
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index c7beccd54b16..38fb3a277e26 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4688,7 +4688,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c)
return PTR_ERR(rt5665->gpiod_ldo1_en);
}
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
usleep_range(300000, 350000);
rt5665->regmap = devm_regmap_init_i2c(i2c, &rt5665_regmap);
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 5fcdb50d5184..c551696ae11a 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2458,7 +2458,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c)
return PTR_ERR(rt5668->ldo1_en);
}
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
usleep_range(300000, 350000);
regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1);
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index bba987308e15..e556a365adc8 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -173,7 +173,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c)
if (ret)
return ret;
- /* Sleep for 300 ms miniumum */
+ /* Sleep for 300 ms minimum */
usleep_range(300000, 350000);
regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1);
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
index 391cc03d687f..14a2c0723d33 100644
--- a/sound/soc/codecs/rtq9128.c
+++ b/sound/soc/codecs/rtq9128.c
@@ -40,6 +40,12 @@
#define RTQ9128_REG_EFUSE_DATA 0xE0
#define RTQ9128_REG_VENDOR_ID 0xF9
+#define RTQ9154_REG_CH1_VOL 0x34
+#define RTQ9154_REG_CH2_VOL 0x33
+#define RTQ9154_REG_CH3_VOL 0x32
+#define RTQ9154_REG_CH4_VOL 0x31
+#define RTQ9154_REG_AUTOULQM 0xAD
+
#define RTQ9128_CHSTAT_VAL_MASK GENMASK(1, 0)
#define RTQ9128_DOLEN_MASK GENMASK(7, 6)
#define RTQ9128_TDMSRCIN_MASK GENMASK(5, 4)
@@ -48,6 +54,7 @@
#define RTQ9128_MSMUTE_MASK BIT(0)
#define RTQ9128_DIE_CHECK_MASK GENMASK(4, 0)
#define RTQ9128_VENDOR_ID_MASK GENMASK(19, 8)
+#define RTQ9128_MODEL_ID_MASK GENMASK(7, 4)
#define RTQ9128_SOFT_RESET_VAL 0x80
#define RTQ9128_VENDOR_ID_VAL 0x470
@@ -56,6 +63,15 @@
#define RTQ9128_TKA470B_VAL 0
#define RTQ9128_RTQ9128DH_VAL 0x0F
#define RTQ9128_RTQ9128DL_VAL 0x10
+#define RTQ9154_MODEL_ID 0x08
+
+#define RTQ9154_AUTOULQM_VAL 0x82
+
+enum rtq9128_chip_model {
+ CHIP_MODEL_RTQ9128 = 0,
+ CHIP_MODEL_RTQ9154,
+ CHIP_MODEL_MAX
+};
struct rtq9128_data {
struct gpio_desc *enable;
@@ -63,6 +79,7 @@ struct rtq9128_data {
int tdm_slots;
int tdm_slot_width;
bool tdm_input_data2_select;
+ enum rtq9128_chip_model chip_model;
};
struct rtq9128_init_reg {
@@ -251,6 +268,28 @@ static const struct soc_enum rtq9128_out4_phase_enum =
SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
phase_select_text);
+static const struct soc_enum rtq9154_ch1_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 0, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_ch2_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 2, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_ch3_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 4, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_ch4_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 6, ARRAY_SIZE(source_select_text),
+ source_select_text);
+static const struct soc_enum rtq9154_out1_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9154_out2_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 4, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9154_out3_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+
/*
* In general usage, DVDD could be 1P8V, 3P0V or 3P3V.
* This DVDD undervoltage protection is to prevent from the abnormal power
@@ -283,10 +322,33 @@ static const struct snd_kcontrol_new rtq9128_snd_ctrls[] = {
SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
};
+static const struct snd_kcontrol_new rtq9154_snd_ctrls[] = {
+ SOC_SINGLE_TLV("MS Volume", RTQ9128_REG_MS_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH1 Volume", RTQ9154_REG_CH1_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH2 Volume", RTQ9154_REG_CH2_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH3 Volume", RTQ9154_REG_CH3_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH4 Volume", RTQ9154_REG_CH4_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RTQ9128_REG_MISC, 0, 5, 0, spkgain_tlv),
+ SOC_SINGLE("PBTL12 Switch", RTQ9128_REG_MISC, 4, 1, 0),
+ SOC_SINGLE("PBTL34 Switch", RTQ9128_REG_MISC, 5, 1, 0),
+ SOC_SINGLE("Spread Spectrum Switch", RTQ9128_REG_PWM_SS_OPT, 7, 1, 0),
+ SOC_SINGLE("SDO Select", RTQ9128_REG_SDO_SEL, 0, 15, 0),
+ SOC_ENUM("CH1 SI Select", rtq9154_ch1_si_enum),
+ SOC_ENUM("CH2 SI Select", rtq9154_ch2_si_enum),
+ SOC_ENUM("CH3 SI Select", rtq9154_ch3_si_enum),
+ SOC_ENUM("CH4 SI Select", rtq9154_ch4_si_enum),
+ SOC_ENUM("PWM FREQ Select", rtq9128_pwm_freq_enum),
+ SOC_ENUM("OUT1 Phase Select", rtq9154_out1_phase_enum),
+ SOC_ENUM("OUT2 Phase Select", rtq9154_out2_phase_enum),
+ SOC_ENUM("OUT3 Phase Select", rtq9154_out3_phase_enum),
+ SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
+};
+
static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct rtq9128_data *data = snd_soc_component_get_drvdata(comp);
unsigned int shift, mask;
int ret;
@@ -301,6 +363,10 @@ static int rtq9128_dac_power_event(struct snd_soc_dapm_widget *w, struct snd_kco
else
shift = 0;
+ /* Compared to RTQ9128, RTQ9154 use the reverse order for DACx bitfield location */
+ if (data->chip_model == CHIP_MODEL_RTQ9154)
+ shift = 6 - shift;
+
mask = RTQ9128_CHSTAT_VAL_MASK << shift;
/* Turn channel state to Normal or HiZ */
@@ -352,7 +418,7 @@ static const struct snd_soc_dapm_route rtq9128_dapm_routes[] = {
static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
{ 0xA0, 0xEF },
{ 0x0D, 0x00 },
- { 0x03, 0x05 },
+ { 0x03, 0x45 },
{ 0x05, 0x31 },
{ 0x06, 0x23 },
{ 0x70, 0x11 },
@@ -367,7 +433,7 @@ static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
{ 0x0F, 0x00 },
- { 0x03, 0x0D },
+ { 0x03, 0x4D },
{ 0xB2, 0xFF },
{ 0xB3, 0xFF },
{ 0x30, 0x180 },
@@ -378,7 +444,7 @@ static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
{ 0x0F, 0x00 },
- { 0x03, 0x0D },
+ { 0x03, 0x4D },
{ 0x30, 0x180 },
{ 0x8A, 0x55 },
{ 0x72, 0x00 },
@@ -387,6 +453,7 @@ static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
static int rtq9128_component_probe(struct snd_soc_component *comp)
{
+ struct rtq9128_data *data = snd_soc_component_get_drvdata(comp);
const struct rtq9128_init_reg *table, *curr;
size_t table_size;
unsigned int val;
@@ -421,6 +488,14 @@ static int rtq9128_component_probe(struct snd_soc_component *comp)
return ret;
}
+
+ if (data->chip_model == CHIP_MODEL_RTQ9154) {
+ /* Enable RTQ9154 Specific AUTO ULQM feature */
+ ret = snd_soc_component_write(comp, RTQ9154_REG_AUTOULQM, RTQ9154_AUTOULQM_VAL);
+ if (ret < 0)
+ return ret;
+ }
+
pm_runtime_mark_last_busy(comp->dev);
pm_runtime_put(comp->dev);
@@ -439,6 +514,18 @@ static const struct snd_soc_component_driver rtq9128_comp_driver = {
.endianness = 1,
};
+static const struct snd_soc_component_driver rtq9154_comp_driver = {
+ .probe = rtq9128_component_probe,
+ .controls = rtq9154_snd_ctrls,
+ .num_controls = ARRAY_SIZE(rtq9154_snd_ctrls),
+ .dapm_widgets = rtq9128_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9128_dapm_widgets),
+ .dapm_routes = rtq9128_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9128_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
@@ -679,7 +766,8 @@ static int rtq9128_probe(struct i2c_client *i2c)
struct device *dev = &i2c->dev;
struct rtq9128_data *data;
struct regmap *regmap;
- unsigned int venid;
+ unsigned int veninfo, venid, chip_model;
+ const struct snd_soc_component_driver *comp_drv;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -712,21 +800,33 @@ static int rtq9128_probe(struct i2c_client *i2c)
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
- ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &venid);
+ ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &veninfo);
if (ret)
return dev_err_probe(dev, ret, "Failed to get vendor id\n");
- venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, venid);
+ venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, veninfo);
if (venid != RTQ9128_VENDOR_ID_VAL)
return dev_err_probe(dev, -ENODEV, "Vendor ID not match (0x%x)\n", venid);
+ chip_model = FIELD_GET(RTQ9128_MODEL_ID_MASK, veninfo);
+ switch (chip_model) {
+ case RTQ9154_MODEL_ID:
+ data->chip_model = CHIP_MODEL_RTQ9154;
+ comp_drv = &rtq9154_comp_driver;
+ break;
+ default:
+ data->chip_model = CHIP_MODEL_RTQ9128;
+ comp_drv = &rtq9128_comp_driver;
+ break;
+ }
+
pm_runtime_set_active(dev);
pm_runtime_mark_last_busy(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
- return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1);
+ return devm_snd_soc_register_component(dev, comp_drv, &rtq9128_dai, 1);
}
static int rtq9128_pm_runtime_suspend(struct device *dev)
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index 574c08b14f0c..b7e6546f1b5a 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -237,11 +237,6 @@ static int sdw_mockup_sdw_probe(struct sdw_slave *slave,
return ret;
}
-static int sdw_mockup_sdw_remove(struct sdw_slave *slave)
-{
- return 0;
-}
-
/*
* Intel reserved parts ID with the following mapping expected:
* 0xAAAA: generic full-duplex codec
@@ -264,7 +259,6 @@ static struct sdw_driver sdw_mockup_sdw_driver = {
.name = "sdw-mockup",
},
.probe = sdw_mockup_sdw_probe,
- .remove = sdw_mockup_sdw_remove,
.ops = &sdw_mockup_slave_ops,
.id_table = sdw_mockup_id,
};
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
index a1963415c931..cf3f6abd7e7b 100644
--- a/sound/soc/codecs/tas2780.c
+++ b/sound/soc/codecs/tas2780.c
@@ -319,25 +319,22 @@ static int tas2780_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
goto err;
}
- if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
- || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- == SND_SOC_DAIFMT_DSP_A)){
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
iface = TAS2780_TDM_CFG2_SCFG_I2S;
tdm_rx_start_slot = 1;
- } else {
- if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- == SND_SOC_DAIFMT_DSP_B)
- || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- == SND_SOC_DAIFMT_LEFT_J)) {
- iface = TAS2780_TDM_CFG2_SCFG_LEFT_J;
- tdm_rx_start_slot = 0;
- } else {
- dev_err(tas2780->dev,
- "%s:DAI Format is not found, fmt=0x%x\n",
- __func__, fmt);
- ret = -EINVAL;
- goto err;
- }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_B:
+ iface = TAS2780_TDM_CFG2_SCFG_LEFT_J;
+ tdm_rx_start_slot = 0;
+ break;
+ default:
+ dev_err(tas2780->dev,
+ "%s:DAI Format is not found, fmt=0x%x\n", __func__, fmt);
+ ret = -EINVAL;
+ goto err;
}
ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1,
TAS2780_TDM_CFG1_MASK,
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 78fd0a5dc6f2..0e084c3a162d 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -2,7 +2,7 @@
//
// tas2781-fmwlib.c -- TASDEVICE firmware support
//
-// Copyright 2023 - 2025 Texas Instruments, Inc.
+// Copyright 2023 - 2026 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
// Author: Baojun Xu <baojun.xu@ti.com>
@@ -80,6 +80,14 @@
#define POST_SOFTWARE_RESET_DEVICE_C 0x47
#define POST_SOFTWARE_RESET_DEVICE_D 0x48
+#define COPY_CAL_DATA(i) \
+ do { \
+ calbin_data[i + 1] = data[7]; \
+ calbin_data[i + 2] = data[8]; \
+ calbin_data[i + 3] = data[9]; \
+ calbin_data[i + 4] = data[10]; \
+ } while (0)
+
struct tas_crc {
unsigned char offset;
unsigned char len;
@@ -1952,23 +1960,6 @@ static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
return rc;
}
-static int load_calib_data(struct tasdevice_priv *tas_priv,
- struct tasdevice_data *dev_data)
-{
- struct tasdev_blk *block;
- unsigned int i;
- int ret = 0;
-
- for (i = 0; i < dev_data->nr_blk; i++) {
- block = &(dev_data->dev_blks[i]);
- ret = tasdevice_load_block(tas_priv, block);
- if (ret < 0)
- break;
- }
-
- return ret;
-}
-
static int fw_parse_header(struct tasdevice_priv *tas_priv,
struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
{
@@ -2029,6 +2020,103 @@ out:
return offset;
}
+static inline int check_cal_bin_data(struct device *dev,
+ const unsigned char *data, const char *name)
+{
+ if (data[2] != 0x85 || data[1] != 4) {
+ dev_err(dev, "Invalid cal bin file in %s\n", name);
+ return -1;
+ }
+ return 0;
+}
+
+static void calbin_conversion(struct tasdevice_priv *priv,
+ struct tasdevice_fw *tas_fmw)
+{
+ struct calidata *cali_data = &priv->cali_data;
+ unsigned char *calbin_data = cali_data->data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ struct tasdevice_calibration *calibration;
+ struct tasdevice_data *img_data;
+ struct tasdev_blk *blk;
+ unsigned char *data;
+ int chn, k;
+
+ if (cali_data->total_sz != priv->ndev *
+ (cali_data->cali_dat_sz_per_dev + 1)) {
+ dev_err(priv->dev, "%s: cali_data size err\n",
+ __func__);
+ return;
+ }
+ calibration = &(tas_fmw->calibrations[0]);
+ img_data = &(calibration->dev_data);
+
+ if (img_data->nr_blk != 1) {
+ dev_err(priv->dev, "%s: Invalid nr_blk, wrong cal bin\n",
+ __func__);
+ return;
+ }
+
+ blk = &(img_data->dev_blks[0]);
+ if (blk->nr_cmds != 15) {
+ dev_err(priv->dev, "%s: Invalid nr_cmds, wrong cal bin\n",
+ __func__);
+ return;
+ }
+
+ switch (blk->type) {
+ case COEFF_DEVICE_A:
+ chn = 0;
+ break;
+ case COEFF_DEVICE_B:
+ chn = 1;
+ break;
+ case COEFF_DEVICE_C:
+ chn = 2;
+ break;
+ case COEFF_DEVICE_D:
+ chn = 3;
+ break;
+ default:
+ dev_err(priv->dev, "%s: Other Type = 0x%02x\n",
+ __func__, blk->type);
+ return;
+ }
+ k = chn * (cali_data->cali_dat_sz_per_dev + 1);
+
+ data = blk->data;
+ if (check_cal_bin_data(priv->dev, data, "r0_reg") < 0)
+ return;
+ p->r0_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k);
+
+ data = blk->data + 12;
+ if (check_cal_bin_data(priv->dev, data, "r0_low_reg") < 0)
+ return;
+ p->r0_low_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 4);
+
+ data = blk->data + 24;
+ if (check_cal_bin_data(priv->dev, data, "invr0_reg") < 0)
+ return;
+ p->invr0_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 8);
+
+ data = blk->data + 36;
+ if (check_cal_bin_data(priv->dev, data, "pow_reg") < 0)
+ return;
+ p->pow_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 12);
+
+ data = blk->data + 48;
+ if (check_cal_bin_data(priv->dev, data, "tlimit_reg") < 0)
+ return;
+ p->tlimit_reg = TASDEVICE_REG(data[4], data[5], data[6]);
+ COPY_CAL_DATA(k + 16);
+
+ calbin_data[k] = chn;
+}
+
/* When calibrated data parsing error occurs, DSP can still work with default
* calibrated data, memory resource related to calibrated data will be
* released in the tasdevice_codec_remove.
@@ -2086,6 +2174,7 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
goto out;
}
+ calbin_conversion(tas_priv, tas_fmw);
out:
return offset;
}
@@ -2371,25 +2460,12 @@ static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
static void tasdev_load_calibrated_data(struct tasdevice_priv *priv, int i)
{
- struct tasdevice_fw *cal_fmw = priv->tasdevice[i].cali_data_fmw;
struct calidata *cali_data = &priv->cali_data;
struct cali_reg *p = &cali_data->cali_reg_array;
unsigned char *data = cali_data->data;
- struct tasdevice_calibration *cal;
int k = i * (cali_data->cali_dat_sz_per_dev + 1);
int rc;
- /* Load the calibrated data from cal bin file */
- if (!priv->is_user_space_calidata && cal_fmw) {
- cal = cal_fmw->calibrations;
-
- if (cal)
- load_calib_data(priv, &cal->dev_data);
- return;
- }
- if (!priv->is_user_space_calidata)
- return;
- /* load calibrated data from user space */
if (data[k] != i) {
dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n",
__func__, i);
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index d1c76ab0144d..41b89fcc69c3 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2026 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -255,8 +255,6 @@ static int tasdev_cali_data_get(struct snd_kcontrol *kcontrol,
int rc;
guard(mutex)(&priv->codec_lock);
- if (!priv->is_user_space_calidata)
- return -1;
if (!p->r0_reg)
return -1;
@@ -654,7 +652,6 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
}
}
i += 2;
- priv->is_user_space_calidata = true;
if (priv->dspbin_typ == TASDEV_BASIC) {
p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
@@ -1444,7 +1441,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
GFP_KERNEL);
if (!cali_data->data)
return -ENOMEM;
-
+ /*
+ * Set to an invalid value before the calibrated data is stored into
+ * it, for the default value is 0, which means the first device.
+ */
+ cali_data->data[0] = 0xff;
if (priv->chip_id == TAS2781) {
struct soc_bytes_ext *ext_cali_start;
char *cali_start_name;
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 43b779873b93..3c1fbf523529 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -27,16 +27,21 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw_type.h>
+#if IS_ENABLED(CONFIG_PCI)
+#include <linux/pci.h>
+#endif
#include <sound/sdw.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/tas2781-tlv.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_regmap.h>
#include "tas2783.h"
#define TIMEOUT_FW_DL_MS (3000)
-#define FW_DL_OFFSET 36
-#define FW_FL_HDR 12
+#define FW_DL_OFFSET 84 /* binary file information */
+#define FW_FL_HDR 20 /* minimum number of bytes in one chunk */
#define TAS2783_PROBE_TIMEOUT 5000
#define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \
0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92)
@@ -49,11 +54,22 @@ static const u32 tas2783_cali_reg[] = {
TAS2783_CAL_TLIM,
};
-struct bin_header_t {
- u16 vendor_id;
- u16 version;
+struct tas_fw_hdr {
+ u32 size;
+ u32 version_offset;
+ u32 plt_id;
+ u32 ppc3_ver;
+ u32 timestamp;
+ u8 ddc_name[64];
+};
+
+struct tas_fw_file {
+ u32 vendor_id;
u32 file_id;
+ u32 version;
u32 length;
+ u32 dest_addr;
+ u8 *fw_data;
};
struct calibration_data {
@@ -66,6 +82,7 @@ struct tas2783_prv {
struct snd_soc_component *component;
struct calibration_data cali_data;
struct sdw_slave *sdw_peripheral;
+ struct sdca_function_data *sa_func_data;
enum sdw_slave_status status;
/* calibration */
struct mutex calib_lock;
@@ -286,7 +303,7 @@ static const struct reg_default tas2783_reg_default[] = {
};
static const struct reg_sequence tas2783_init_seq[] = {
- REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x04),
+ REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x01),
REG_SEQ0(0x00800418, 0x00),
REG_SEQ0(0x00800419, 0x00),
REG_SEQ0(0x0080041a, 0x00),
@@ -296,60 +313,19 @@ static const struct reg_sequence tas2783_init_seq[] = {
REG_SEQ0(0x0080042a, 0x00),
REG_SEQ0(0x0080042b, 0x00),
REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x1, 0x00), 0x00),
- REG_SEQ0(0x0080005c, 0xD9),
- REG_SEQ0(0x00800082, 0x20),
- REG_SEQ0(0x008000a1, 0x00),
- REG_SEQ0(0x00800097, 0xc8),
- REG_SEQ0(0x00800099, 0x20),
- REG_SEQ0(0x008000c7, 0xaa),
- REG_SEQ0(0x008000b5, 0x74),
- REG_SEQ0(0x00800082, 0x20),
- REG_SEQ0(0x00807e8d, 0x0d),
- REG_SEQ0(0x00807eb9, 0x53),
- REG_SEQ0(0x00807ebe, 0x42),
- REG_SEQ0(0x00807ec5, 0x37),
- REG_SEQ0(0x00800066, 0x92),
- REG_SEQ0(0x00800003, 0x28),
REG_SEQ0(0x00800004, 0x21),
REG_SEQ0(0x00800005, 0x41),
REG_SEQ0(0x00800006, 0x00),
REG_SEQ0(0x00800007, 0x20),
- REG_SEQ0(0x0080000c, 0x10),
- REG_SEQ0(0x00800013, 0x08),
REG_SEQ0(0x00800015, 0x00),
- REG_SEQ0(0x00800017, 0x80),
- REG_SEQ0(0x0080001a, 0x00),
- REG_SEQ0(0x0080001b, 0x22),
- REG_SEQ0(0x0080001c, 0x36),
- REG_SEQ0(0x0080001d, 0x01),
- REG_SEQ0(0x0080001f, 0x00),
- REG_SEQ0(0x00800020, 0x2e),
- REG_SEQ0(0x00800034, 0x06),
- REG_SEQ0(0x00800035, 0xb9),
REG_SEQ0(0x00800036, 0xad),
REG_SEQ0(0x00800037, 0xa8),
- REG_SEQ0(0x00800038, 0x00),
- REG_SEQ0(0x0080003b, 0xfc),
- REG_SEQ0(0x0080003d, 0xdd),
- REG_SEQ0(0x00800040, 0xf6),
- REG_SEQ0(0x00800041, 0x14),
- REG_SEQ0(0x0080005c, 0x19),
- REG_SEQ0(0x0080005d, 0x80),
- REG_SEQ0(0x00800063, 0x48),
- REG_SEQ0(0x00800065, 0x08),
- REG_SEQ0(0x00800067, 0x00),
- REG_SEQ0(0x0080006a, 0x12),
REG_SEQ0(0x0080006b, 0x7b),
REG_SEQ0(0x0080006c, 0x00),
REG_SEQ0(0x0080006d, 0x00),
REG_SEQ0(0x0080006e, 0x1a),
REG_SEQ0(0x0080006f, 0x00),
- REG_SEQ0(0x00800070, 0x96),
REG_SEQ0(0x00800071, 0x02),
- REG_SEQ0(0x00800073, 0x08),
- REG_SEQ0(0x00800075, 0xe0),
- REG_SEQ0(0x0080007a, 0x60),
- REG_SEQ0(0x008000bd, 0x00),
REG_SEQ0(0x008000be, 0x00),
REG_SEQ0(0x008000bf, 0x00),
REG_SEQ0(0x008000c0, 0x00),
@@ -357,17 +333,6 @@ static const struct reg_sequence tas2783_init_seq[] = {
REG_SEQ0(0x008000c2, 0x00),
REG_SEQ0(0x008000c3, 0x00),
REG_SEQ0(0x008000c4, 0x00),
- REG_SEQ0(0x008000c5, 0x00),
- REG_SEQ0(0x00800008, 0x49),
- REG_SEQ0(0x00800009, 0x02),
- REG_SEQ0(0x0080000a, 0x1a),
- REG_SEQ0(0x0080000d, 0x93),
- REG_SEQ0(0x0080000e, 0x82),
- REG_SEQ0(0x0080000f, 0x42),
- REG_SEQ0(0x00800010, 0x84),
- REG_SEQ0(0x00800014, 0x0a),
- REG_SEQ0(0x00800016, 0x00),
- REG_SEQ0(0x00800060, 0x21),
};
static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
@@ -676,7 +641,8 @@ static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32
}
if (device_num == dev_count)
- dev_err(tas_dev->dev, "device not found\n");
+ dev_err(tas_dev->dev,
+ "unique id not found in the calib data\n");
else
dev_dbg(tas_dev->dev, "calib data update done\n");
}
@@ -735,13 +701,28 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
return ret;
}
-static s32 read_header(const u8 *data, struct bin_header_t *hdr)
+static s32 tas_fw_read_hdr(const u8 *data, struct tas_fw_hdr *hdr)
{
- hdr->vendor_id = get_unaligned_le16(&data[0]);
- hdr->file_id = get_unaligned_le32(&data[2]);
- hdr->version = get_unaligned_le16(&data[6]);
- hdr->length = get_unaligned_le32(&data[8]);
- return 12;
+ hdr->size = get_unaligned_le32(data);
+ hdr->version_offset = get_unaligned_le32(&data[4]);
+ hdr->plt_id = get_unaligned_le32(&data[8]);
+ hdr->ppc3_ver = get_unaligned_le32(&data[12]);
+ memcpy(hdr->ddc_name, &data[16], 64);
+ hdr->timestamp = get_unaligned_le32(&data[80]);
+
+ return 84;
+}
+
+static s32 tas_fw_get_next_file(const u8 *data, struct tas_fw_file *file)
+{
+ file->vendor_id = get_unaligned_le32(&data[0]);
+ file->file_id = get_unaligned_le32(&data[4]);
+ file->version = get_unaligned_le32(&data[8]);
+ file->length = get_unaligned_le32(&data[12]);
+ file->dest_addr = get_unaligned_le32(&data[16]);
+ file->fw_data = (u8 *)&data[20];
+
+ return file->length + sizeof(u32) * 5;
}
static void tas2783_fw_ready(const struct firmware *fmw, void *context)
@@ -749,13 +730,20 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
struct tas2783_prv *tas_dev =
(struct tas2783_prv *)context;
const u8 *buf = NULL;
- s32 offset = 0, img_sz, file_blk_size, ret;
- struct bin_header_t hdr;
+ s32 img_sz, ret = 0, cur_file = 0;
+ s32 offset = 0;
+
+ struct tas_fw_hdr *hdr __free(kfree) = kzalloc(sizeof(*hdr), GFP_KERNEL);
+ struct tas_fw_file *file __free(kfree) = kzalloc(sizeof(*file), GFP_KERNEL);
+ if (!file || !hdr) {
+ ret = -ENOMEM;
+ goto out;
+ }
if (!fmw || !fmw->data) {
- /* No firmware binary, devices will work in ROM mode. */
+ /* firmware binary not found*/
dev_err(tas_dev->dev,
- "Failed to read %s, no side-effect on driver running\n",
+ "Failed to read fw binary %s\n",
tas_dev->rca_binaryname);
ret = -EINVAL;
goto out;
@@ -763,67 +751,47 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
img_sz = fmw->size;
buf = fmw->data;
- offset += FW_DL_OFFSET;
- if (offset >= (img_sz - FW_FL_HDR)) {
- dev_err(tas_dev->dev,
- "firmware is too small");
+ offset += tas_fw_read_hdr(buf, hdr);
+ if (hdr->size != img_sz) {
+ ret = -EINVAL;
+ dev_err(tas_dev->dev, "firmware size mismatch with header");
+ goto out;
+ }
+
+ if (img_sz < FW_DL_OFFSET) {
ret = -EINVAL;
+ dev_err(tas_dev->dev, "unexpected size, size is too small");
goto out;
}
mutex_lock(&tas_dev->pde_lock);
while (offset < (img_sz - FW_FL_HDR)) {
- memset(&hdr, 0, sizeof(hdr));
- offset += read_header(&buf[offset], &hdr);
+ offset += tas_fw_get_next_file(&buf[offset], file);
dev_dbg(tas_dev->dev,
- "vndr=%d, file=%d, version=%d, len=%d, off=%d\n",
- hdr.vendor_id, hdr.file_id, hdr.version,
- hdr.length, offset);
- /* size also includes the header */
- file_blk_size = hdr.length - FW_FL_HDR;
-
- /* make sure that enough data is there */
- if (offset + file_blk_size > img_sz) {
- ret = -EINVAL;
+ "v=%d, fid=%d, ver=%d, len=%d, daddr=0x%x, fw=%p",
+ file->vendor_id, file->file_id,
+ file->version, file->length,
+ file->dest_addr, file->fw_data);
+
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ file->dest_addr,
+ file->length,
+ file->fw_data);
+ if (ret < 0) {
dev_err(tas_dev->dev,
- "corrupt firmware file");
- break;
- }
-
- switch (hdr.file_id) {
- case 0:
- ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
- PRAM_ADDR_START, file_blk_size,
- &buf[offset]);
- if (ret < 0)
- dev_err(tas_dev->dev,
- "PRAM update failed: %d", ret);
- break;
-
- case 1:
- ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
- YRAM_ADDR_START, file_blk_size,
- &buf[offset]);
- if (ret < 0)
- dev_err(tas_dev->dev,
- "YRAM update failed: %d", ret);
-
- break;
-
- default:
- ret = -EINVAL;
- dev_err(tas_dev->dev, "Unsupported file");
+ "FW download failed: %d", ret);
break;
}
-
- if (ret == 0)
- offset += file_blk_size;
- else
- break;
+ cur_file++;
}
mutex_unlock(&tas_dev->pde_lock);
- if (!ret)
+
+ if (cur_file == 0) {
+ dev_err(tas_dev->dev, "fw with no files");
+ ret = -EINVAL;
+ } else {
tas2783_update_calibdata(tas_dev);
+ }
out:
if (!ret)
@@ -1094,66 +1062,6 @@ static s32 tas_init(struct tas2783_prv *tas_dev)
return ret;
}
-static s32 tas_read_prop(struct sdw_slave *slave)
-{
- struct sdw_slave_prop *prop = &slave->prop;
- s32 nval;
- s32 i, j;
- u32 bit;
- unsigned long addr;
- struct sdw_dpn_prop *dpn;
-
- prop->scp_int1_mask =
- SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
- prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
-
- prop->paging_support = true;
-
- /* first we need to allocate memory for set bits in port lists */
- prop->source_ports = 0x04; /* BITMAP: 00000100 */
- prop->sink_ports = 0x2; /* BITMAP: 00000010 */
-
- nval = hweight32(prop->source_ports);
- prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
- sizeof(*prop->src_dpn_prop), GFP_KERNEL);
- if (!prop->src_dpn_prop)
- return -ENOMEM;
-
- i = 0;
- dpn = prop->src_dpn_prop;
- addr = prop->source_ports;
- for_each_set_bit(bit, &addr, 32) {
- dpn[i].num = bit;
- dpn[i].type = SDW_DPN_FULL;
- dpn[i].simple_ch_prep_sm = false;
- dpn[i].ch_prep_timeout = 10;
- i++;
- }
-
- /* do this again for sink now */
- nval = hweight32(prop->sink_ports);
- prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
- sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
- if (!prop->sink_dpn_prop)
- return -ENOMEM;
-
- j = 0;
- dpn = prop->sink_dpn_prop;
- addr = prop->sink_ports;
- for_each_set_bit(bit, &addr, 32) {
- dpn[j].num = bit;
- dpn[j].type = SDW_DPN_FULL;
- dpn[j].simple_ch_prep_sm = false;
- dpn[j].ch_prep_timeout = 10;
- j++;
- }
-
- /* set the timeout values */
- prop->clk_stop_timeout = 200;
-
- return 0;
-}
-
static s32 tas2783_sdca_dev_suspend(struct device *dev)
{
struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
@@ -1200,6 +1108,31 @@ static const struct dev_pm_ops tas2783_sdca_pm = {
RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL)
};
+static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t size)
+{
+ struct sdw_bus *bus = slave->bus;
+ u8 unique_id = slave->id.unique_id;
+ bool pci_found = false;
+#if IS_ENABLED(CONFIG_PCI)
+ struct device *dev = &slave->dev;
+ struct pci_dev *pci = NULL;
+
+ for (; dev; dev = dev->parent) {
+ if (dev->bus == &pci_bus_type) {
+ pci = to_pci_dev(dev);
+ scnprintf(name, size, "%04X-%1X-%1X.bin",
+ pci->subsystem_device, bus->link_id, unique_id);
+ pci_found = true;
+ break;
+ }
+ }
+#endif
+
+ if (!pci_found)
+ scnprintf(name, size, "tas2783-%1X-%1X.bin",
+ bus->link_id, unique_id);
+}
+
static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
{
struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
@@ -1211,8 +1144,16 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
tas_dev->fw_dl_task_done = false;
tas_dev->fw_dl_success = false;
- scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname),
- "tas2783-%01x.bin", unique_id);
+
+ ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
+ if (ret) {
+ dev_err(dev, "sw reset failed, err=%d", ret);
+ return ret;
+ }
+ usleep_range(2000, 2200);
+
+ tas_generate_fw_name(slave, tas_dev->rca_binaryname,
+ sizeof(tas_dev->rca_binaryname));
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
tas_dev->rca_binaryname, tas_dev->dev,
@@ -1230,9 +1171,18 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
dev_err(tas_dev->dev, "fw request, wait_event timeout\n");
ret = -EAGAIN;
} else {
- ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
- ARRAY_SIZE(tas2783_init_seq));
- tas_dev->hw_init = true;
+ if (tas_dev->sa_func_data)
+ ret = sdca_regmap_write_init(dev, tas_dev->regmap,
+ tas_dev->sa_func_data);
+ else
+ ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
+ ARRAY_SIZE(tas2783_init_seq));
+
+ if (ret)
+ dev_err(tas_dev->dev,
+ "init writes failed, err=%d", ret);
+ else
+ tas_dev->hw_init = true;
}
return ret;
@@ -1267,7 +1217,6 @@ static s32 tas_update_status(struct sdw_slave *slave,
}
static const struct sdw_slave_ops tas_sdw_ops = {
- .read_prop = tas_read_prop,
.update_status = tas_update_status,
};
@@ -1282,12 +1231,52 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral,
struct regmap *regmap;
struct device *dev = &peripheral->dev;
struct tas2783_prv *tas_dev;
+ struct sdca_function_data *function_data = NULL;
+ int ret, i;
+
+ ret = sdw_slave_read_prop(peripheral);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "slave property read failed");
tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL);
if (!tas_dev)
return dev_err_probe(dev, -ENOMEM,
"Failed devm_kzalloc");
+ i = -1;
+ /* check if we have any SDCA function data available */
+ if (peripheral->sdca_data.num_functions > 0) {
+ dev_dbg(dev, "SDCA functions found: %d", peripheral->sdca_data.num_functions);
+
+ /* Look for Smart Amp function type */
+ for (i = 0; i < peripheral->sdca_data.num_functions; i++) {
+ if (peripheral->sdca_data.function[i].type ==
+ SDCA_FUNCTION_TYPE_SMART_AMP) {
+ dev_info(dev, "Found Smart Amp function at index %d", i);
+ break;
+ }
+ }
+ }
+
+ if (i >= 0 && i < peripheral->sdca_data.num_functions) {
+ /* Allocate memory for function data */
+ function_data = devm_kzalloc(dev, sizeof(*function_data),
+ GFP_KERNEL);
+ if (!function_data)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to parse sdca functions");
+
+ /* Parse the function */
+ ret = sdca_parse_function(dev, peripheral,
+ &peripheral->sdca_data.function[i],
+ function_data);
+ if (!ret)
+ tas_dev->sa_func_data = function_data;
+ else
+ dev_warn(dev, "smartamp function parse failed:err%d, using defaults", ret);
+ }
+
tas_dev->dev = dev;
tas_dev->sdw_peripheral = peripheral;
tas_dev->hw_init = false;
@@ -1342,6 +1331,7 @@ static struct sdw_driver tas_sdw_driver = {
};
module_sdw_driver(tas_sdw_driver);
+MODULE_IMPORT_NS("SND_SOC_SDCA");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
index 794333e0a350..bf34319c9a9f 100644
--- a/sound/soc/codecs/tas2783.h
+++ b/sound/soc/codecs/tas2783.h
@@ -28,6 +28,7 @@
#define TASDEV_REG_SDW(book, page, reg) (((book) * 256 * 128) + \
0x800000 + ((page) * 128) + (reg))
+#define TAS2783_SW_RESET TASDEV_REG_SDW(0x0, 0x00, 0x01)
/* Volume control */
#define TAS2783_DVC_LVL TASDEV_REG_SDW(0x0, 0x00, 0x1A)
#define TAS2783_AMP_LEVEL TASDEV_REG_SDW(0x0, 0x00, 0x03)
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index fdf4a9add852..e4f27a734501 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -22,8 +22,16 @@
#include "tlv320adcx140.h"
+static const char *const adcx140_supply_names[] = {
+ "avdd",
+ "iovdd",
+};
+
+#define ADCX140_NUM_SUPPLIES ARRAY_SIZE(adcx140_supply_names)
+
struct adcx140_priv {
struct regulator *supply_areg;
+ struct regulator_bulk_data supplies[ADCX140_NUM_SUPPLIES];
struct gpio_desc *gpio_reset;
struct regmap *regmap;
struct device *dev;
@@ -121,6 +129,34 @@ static const struct reg_default adcx140_reg_defaults[] = {
{ ADCX140_DEV_STS1, 0x80 },
};
+static const struct regmap_range adcx140_wr_ranges[] = {
+ regmap_reg_range(ADCX140_PAGE_SELECT, ADCX140_SLEEP_CFG),
+ regmap_reg_range(ADCX140_SHDN_CFG, ADCX140_SHDN_CFG),
+ regmap_reg_range(ADCX140_ASI_CFG0, ADCX140_ASI_CFG2),
+ regmap_reg_range(ADCX140_ASI_CH1, ADCX140_MST_CFG1),
+ regmap_reg_range(ADCX140_CLK_SRC, ADCX140_CLK_SRC),
+ regmap_reg_range(ADCX140_PDMCLK_CFG, ADCX140_GPO_CFG3),
+ regmap_reg_range(ADCX140_GPO_VAL, ADCX140_GPO_VAL),
+ regmap_reg_range(ADCX140_GPI_CFG0, ADCX140_GPI_CFG1),
+ regmap_reg_range(ADCX140_GPI_MON, ADCX140_GPI_MON),
+ regmap_reg_range(ADCX140_INT_CFG, ADCX140_INT_MASK0),
+ regmap_reg_range(ADCX140_BIAS_CFG, ADCX140_CH4_CFG4),
+ regmap_reg_range(ADCX140_CH5_CFG2, ADCX140_CH5_CFG4),
+ regmap_reg_range(ADCX140_CH6_CFG2, ADCX140_CH6_CFG4),
+ regmap_reg_range(ADCX140_CH7_CFG2, ADCX140_CH7_CFG4),
+ regmap_reg_range(ADCX140_CH8_CFG2, ADCX140_CH8_CFG4),
+ regmap_reg_range(ADCX140_DSP_CFG0, ADCX140_DRE_CFG0),
+ regmap_reg_range(ADCX140_AGC_CFG0, ADCX140_AGC_CFG0),
+ regmap_reg_range(ADCX140_IN_CH_EN, ADCX140_PWR_CFG),
+ regmap_reg_range(ADCX140_PHASE_CALIB, ADCX140_PHASE_CALIB),
+ regmap_reg_range(0x7e, 0x7e),
+};
+
+static const struct regmap_access_table adcx140_wr_table = {
+ .yes_ranges = adcx140_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(adcx140_wr_ranges),
+};
+
static const struct regmap_range_cfg adcx140_ranges[] = {
{
.range_min = 0,
@@ -156,6 +192,7 @@ static const struct regmap_config adcx140_i2c_regmap = {
.num_ranges = ARRAY_SIZE(adcx140_ranges),
.max_register = 12 * 128,
.volatile_reg = adcx140_volatile,
+ .wr_table = &adcx140_wr_table,
};
/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */
@@ -185,6 +222,13 @@ static const struct snd_kcontrol_new decimation_filter_controls[] = {
SOC_DAPM_ENUM("Decimation Filter", decimation_filter_enum),
};
+static const char * const channel_summation_text[] = {
+ "Disabled", "2 Channel", "4 Channel"
+};
+
+static SOC_ENUM_SINGLE_DECL(channel_summation_enum, ADCX140_DSP_CFG0, 2,
+ channel_summation_text);
+
static const char * const pdmclk_text[] = {
"2.8224 MHz", "1.4112 MHz", "705.6 kHz", "5.6448 MHz"
};
@@ -672,6 +716,10 @@ static const struct snd_kcontrol_new adcx140_snd_controls[] = {
SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2,
0, 0xff, 0, dig_vol_tlv),
ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"),
+
+ SOC_SINGLE("Biquads Per Channel", ADCX140_DSP_CFG1, 5, 3, 0),
+
+ SOC_ENUM("Channel Summation", channel_summation_enum),
};
static int adcx140_reset(struct adcx140_priv *adcx140)
@@ -1073,19 +1121,91 @@ out:
return ret;
}
+static int adcx140_pwr_off(struct adcx140_priv *adcx140)
+{
+ int ret;
+
+ regcache_cache_only(adcx140->regmap, true);
+ regcache_mark_dirty(adcx140->regmap);
+
+ /* Assert the reset GPIO */
+ gpiod_set_value_cansleep(adcx140->gpio_reset, 0);
+
+ /*
+ * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A,
+ * TLV320ADC6140 Rev. A 8.4.1:
+ * wait for hw shutdown (25ms) + >= 1ms
+ */
+ usleep_range(30000, 100000);
+
+ /* Power off the regulators, `avdd` and `iovdd` */
+ ret = regulator_bulk_disable(ARRAY_SIZE(adcx140->supplies),
+ adcx140->supplies);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adcx140_pwr_on(struct adcx140_priv *adcx140)
+{
+ int ret;
+
+ /* Power on the regulators, `avdd` and `iovdd` */
+ ret = regulator_bulk_enable(ARRAY_SIZE(adcx140->supplies),
+ adcx140->supplies);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* De-assert the reset GPIO */
+ gpiod_set_value_cansleep(adcx140->gpio_reset, 1);
+
+ /*
+ * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A,
+ * TLV320ADC6140 Rev. A 8.4.2:
+ * wait >= 10 ms after entering sleep mode.
+ */
+ usleep_range(10000, 100000);
+
+ regcache_cache_only(adcx140->regmap, false);
+
+ /* Flush the regcache */
+ ret = regcache_sync(adcx140->regmap);
+ if (ret) {
+ dev_err(adcx140->dev, "Failed to restore register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int adcx140_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ enum snd_soc_bias_level prev_level = snd_soc_dapm_get_bias_level(dapm);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
+ if (prev_level == SND_SOC_BIAS_STANDBY)
+ adcx140_pwr_ctrl(adcx140, true);
+ break;
case SND_SOC_BIAS_STANDBY:
- adcx140_pwr_ctrl(adcx140, true);
+ if (prev_level == SND_SOC_BIAS_PREPARE)
+ adcx140_pwr_ctrl(adcx140, false);
+ if (prev_level == SND_SOC_BIAS_OFF)
+ return adcx140_pwr_on(adcx140);
break;
case SND_SOC_BIAS_OFF:
- adcx140_pwr_ctrl(adcx140, false);
+ if (prev_level == SND_SOC_BIAS_STANDBY)
+ return adcx140_pwr_off(adcx140);
break;
}
@@ -1151,6 +1271,16 @@ static int adcx140_i2c_probe(struct i2c_client *i2c)
adcx140->phase_calib_on = false;
adcx140->dev = &i2c->dev;
+ for (int i = 0; i < ADCX140_NUM_SUPPLIES; i++)
+ adcx140->supplies[i].supply = adcx140_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ADCX140_NUM_SUPPLIES,
+ adcx140->supplies);
+ if (ret) {
+ dev_err_probe(&i2c->dev, ret, "Failed to request supplies\n");
+ return ret;
+ }
+
adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(adcx140->gpio_reset))
@@ -1186,6 +1316,8 @@ static int adcx140_i2c_probe(struct i2c_client *i2c)
return ret;
}
+ regcache_cache_only(adcx140->regmap, true);
+
i2c_set_clientdata(i2c, adcx140);
return devm_snd_soc_register_component(&i2c->dev,
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 26ebcdadeb7d..0c842aaa7eec 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1631,17 +1631,5 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
}
EXPORT_SYMBOL(wcd_mbhc_deinit);
-static int __init mbhc_init(void)
-{
- return 0;
-}
-
-static void __exit mbhc_exit(void)
-{
-}
-
-module_init(mbhc_init);
-module_exit(mbhc_exit);
-
MODULE_DESCRIPTION("wcd MBHC v2 module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c
index da342a0c95a5..399dfba79aa2 100644
--- a/sound/soc/codecs/wcd939x-sdw.c
+++ b/sound/soc/codecs/wcd939x-sdw.c
@@ -1384,12 +1384,7 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
}
if (wcd->is_tx) {
- /*
- * Do not use devres here since devres_release_group() could
- * be called by component_unbind() id the aggregate device
- * fails to bind.
- */
- wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config);
+ wcd->regmap = devm_regmap_init_sdw(pdev, &wcd939x_regmap_config);
if (IS_ERR(wcd->regmap))
return dev_err_probe(dev, PTR_ERR(wcd->regmap),
"Regmap init failed\n");
@@ -1400,30 +1395,20 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
ret = component_add(dev, &wcd_sdw_component_ops);
if (ret)
- goto err_free_regmap;
+ return ret;
/* Set suspended until aggregate device is bind */
pm_runtime_set_suspended(dev);
return 0;
-
-err_free_regmap:
- if (wcd->regmap)
- regmap_exit(wcd->regmap);
-
- return ret;
}
static int wcd9390_remove(struct sdw_slave *pdev)
{
struct device *dev = &pdev->dev;
- struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev);
component_del(dev, &wcd_sdw_component_ops);
- if (wcd->regmap)
- regmap_exit(wcd->regmap);
-
return 0;
}
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 7511c71695c6..6e097d8ed288 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -326,7 +326,7 @@ static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len)
int i;
for (i = 0; i < len / 8; i++)
- data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i]));
+ data_out[i] = swab64(data_in[i]);
}
static int wm0010_firmware_load(const char *name, struct snd_soc_component *component)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index a03bbde5d852..a2f0e2f5c407 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -471,11 +471,9 @@ static int wm8731_set_bias_level(struct snd_soc_component *component,
switch (level) {
case SND_SOC_BIAS_ON:
- if (wm8731->mclk) {
- ret = clk_prepare_enable(wm8731->mclk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(wm8731->mclk);
+ if (ret)
+ return ret;
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -494,8 +492,7 @@ static int wm8731_set_bias_level(struct snd_soc_component *component,
snd_soc_component_write(component, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- if (wm8731->mclk)
- clk_disable_unprepare(wm8731->mclk);
+ clk_disable_unprepare(wm8731->mclk);
snd_soc_component_write(component, WM8731_PWR, 0xffff);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index e9e317ce6898..8d2435bf44ea 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -67,6 +67,8 @@ struct wm8962_priv {
struct mutex dsp2_ena_lock;
u16 dsp2_ena;
+ int mic_status;
+
struct delayed_work mic_work;
struct snd_soc_jack *jack;
@@ -83,6 +85,8 @@ struct wm8962_priv {
int irq;
bool master_flag;
+ int tdm_width;
+ int tdm_slots;
};
/* We can't use the same notifier block for more than one supply and
@@ -1760,7 +1764,7 @@ SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18),
SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0),
-SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA),
+SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA | WM8962_ADC_MONOMIX),
SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0),
SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA),
@@ -2610,6 +2614,19 @@ static int wm8962_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static int wm8962_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
+
+ wm8962->tdm_width = slot_width;
+ /* External is one slot one channel, but internal is one slot two channels */
+ wm8962->tdm_slots = slots / 2;
+
+ return 0;
+}
+
static const struct {
int rate;
int reg;
@@ -2637,10 +2654,21 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
int i;
int aif0 = 0;
int adctl3 = 0;
+ int width;
+
+ if (wm8962->tdm_width && wm8962->tdm_slots) {
+ wm8962->bclk = snd_soc_calc_bclk(params_rate(params),
+ wm8962->tdm_width,
+ params_channels(params),
+ wm8962->tdm_slots);
+ width = wm8962->tdm_width;
+ } else {
+ wm8962->bclk = snd_soc_params_to_bclk(params);
+ width = params_width(params);
- wm8962->bclk = snd_soc_params_to_bclk(params);
- if (params_channels(params) == 1)
- wm8962->bclk *= 2;
+ if (params_channels(params) == 1)
+ wm8962->bclk *= 2;
+ }
wm8962->lrclk = params_rate(params);
@@ -2658,7 +2686,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
if (wm8962->lrclk % 8000 == 0)
adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
- switch (params_width(params)) {
+ switch (width) {
case 16:
break;
case 20:
@@ -3037,6 +3065,7 @@ static const struct snd_soc_dai_ops wm8962_dai_ops = {
.hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk,
.set_fmt = wm8962_set_dai_fmt,
+ .set_tdm_slot = wm8962_set_tdm_slot,
.mute_stream = wm8962_mute,
.no_capture_mute = 1,
};
@@ -3081,8 +3110,16 @@ static void wm8962_mic_work(struct work_struct *work)
if (reg & WM8962_MICSHORT_STS) {
status |= SND_JACK_BTN_0;
irq_pol |= WM8962_MICSCD_IRQ_POL;
+
+ /* Don't report a microphone if it's shorted right after
+ * plugging in, as this may be a TRS plug in a TRRS socket.
+ */
+ if (!(wm8962->mic_status & WM8962_MICDET_STS))
+ status = 0;
}
+ wm8962->mic_status = status;
+
snd_soc_jack_report(wm8962->jack, status,
SND_JACK_MICROPHONE | SND_JACK_BTN_0);
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index c4a00b22bc2a..828524c90f17 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -19,6 +19,7 @@ config SND_SOC_FSL_ASRC
config SND_SOC_FSL_SAI
tristate "Synchronous Audio Interface (SAI) module support"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select REGMAP_MMIO
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -32,7 +33,6 @@ config SND_SOC_FSL_SAI
config SND_SOC_FSL_MQS
tristate "Medium Quality Sound (MQS) module support"
depends on SND_SOC_FSL_SAI
- depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select REGMAP_MMIO
help
Say Y if you want to add Medium Quality Sound (MQS)
@@ -309,6 +309,7 @@ config SND_SOC_IMX_SGTL5000
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
depends on OF && I2C
# enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m:
depends on SND_AC97_CODEC || SND_AC97_CODEC=n
@@ -330,6 +331,7 @@ config SND_SOC_FSL_ASOC_CARD
config SND_SOC_IMX_AUDMIX
tristate "SoC Audio support for i.MX boards with AUDMIX"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select SND_SOC_FSL_AUDMIX
select SND_SOC_FSL_SAI
help
@@ -339,6 +341,7 @@ config SND_SOC_IMX_AUDMIX
config SND_SOC_IMX_HDMI
tristate "SoC Audio support for i.MX boards with HDMI port"
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select SND_SOC_FSL_SAI
select SND_SOC_FSL_AUD2HTX
select SND_SOC_HDMI_CODEC
@@ -364,6 +367,7 @@ config SND_SOC_IMX_RPMSG
config SND_SOC_IMX_CARD
tristate "SoC Audio Graph Sound Card support for i.MX boards"
depends on OF && I2C
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select SND_SOC_AK4458
select SND_SOC_AK5558
select SND_SOC_IMX_PCM_DMA
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 92fb16f7be45..5fda9b647c70 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -106,6 +106,12 @@ static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
},
};
+static unsigned char clk_map_imx952[ASRC_CLK_MAP_LEN] = {
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
+ 0x0, 0x1, 0x2, 0x3, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x4, 0x5, 0x6, 0x8, 0xf, 0xf,
+ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0x9, 0xa, 0xb, 0xc, 0xd, 0xf, 0xf, 0xf, 0xf,
+};
+
/*
* According to RM, the divider range is 1 ~ 8,
* prescaler is power of 2 from 1 ~ 128.
@@ -1078,6 +1084,26 @@ static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
}
+static bool fsl_asrc_m2m_output_ready(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+ int ret;
+
+ /* Check output fifo status if it exceeds the watermark. */
+ ret = regmap_read_poll_timeout(asrc->regmap, REG_ASRFST(index), val,
+ (ASRFSTi_OUTPUT_FIFO_FILL(val) >= ASRC_M2M_OUTPUTFIFO_WML),
+ 1, 1000);
+
+ if (ret) {
+ pair_warn("output is not ready\n");
+ return false;
+ }
+
+ return true;
+}
+
static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
{
struct fsl_asrc_pair_priv *pair_priv = pair->private;
@@ -1275,6 +1301,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc_priv->soc = of_device_get_match_data(&pdev->dev);
asrc->use_edma = asrc_priv->soc->use_edma;
+ asrc->start_before_dma = asrc_priv->soc->start_before_dma;
asrc->get_dma_channel = fsl_asrc_get_dma_channel;
asrc->request_pair = fsl_asrc_request_pair;
asrc->release_pair = fsl_asrc_release_pair;
@@ -1289,6 +1316,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
+ asrc->m2m_output_ready = fsl_asrc_m2m_output_ready;
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
asrc_priv->clk_map[IN] = input_clk_map_imx35;
@@ -1315,6 +1343,9 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
}
+ } else if (of_device_is_compatible(np, "fsl,imx952-asrc")) {
+ asrc_priv->clk_map[IN] = clk_map_imx952;
+ asrc_priv->clk_map[OUT] = clk_map_imx952;
}
asrc->channel_avail = 10;
@@ -1553,11 +1584,18 @@ static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
.channel_bits = 4,
};
+static const struct fsl_asrc_soc_data fsl_asrc_imx952_data = {
+ .use_edma = true,
+ .channel_bits = 4,
+ .start_before_dma = true,
+};
+
static const struct of_device_id fsl_asrc_ids[] = {
{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
+ { .compatible = "fsl,imx952-asrc", .data = &fsl_asrc_imx952_data },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 1c492eb237f5..7a81366a0ee4 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -257,6 +257,8 @@
#define ASRFSTi_OUTPUT_FIFO_WIDTH 7
#define ASRFSTi_OUTPUT_FIFO_SHIFT 12
#define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
+#define ASRFSTi_OUTPUT_FIFO_FILL(v) \
+ (((v) & ASRFSTi_OUTPUT_FIFO_MASK) >> ASRFSTi_OUTPUT_FIFO_SHIFT)
#define ASRFSTi_IAEi_SHIFT 11
#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_IAEi_SHIFT)
#define ASRFSTi_IAEi (1 << ASRFSTi_IAEi_SHIFT)
@@ -324,6 +326,13 @@ enum asrc_inclk {
INCLK_SAI6_TX_BCLK = 0x22,
INCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
INCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
+
+ INCLK_SAI2_TX_BCLK = 0x26,
+ INCLK_SAI3_TX_BCLK = 0x27,
+ INCLK_SAI4_RX_BCLK = 0x28,
+ INCLK_SAI4_TX_BCLK = 0x29,
+ INCLK_SAI5_RX_BCLK = 0x2a,
+ INCLK_SAI5_TX_BCLK = 0x2b,
};
enum asrc_outclk {
@@ -364,6 +373,13 @@ enum asrc_outclk {
OUTCLK_SAI6_TX_BCLK = 0x22,
OUTCLK_HDMI_RX_SAI0_RX_BCLK = 0x24,
OUTCLK_HDMI_TX_SAI0_TX_BCLK = 0x25,
+
+ OUTCLK_SAI2_TX_BCLK = 0x26,
+ OUTCLK_SAI3_TX_BCLK = 0x27,
+ OUTCLK_SAI4_RX_BCLK = 0x28,
+ OUTCLK_SAI4_TX_BCLK = 0x29,
+ OUTCLK_SAI5_RX_BCLK = 0x2a,
+ OUTCLK_SAI5_TX_BCLK = 0x2b,
};
#define ASRC_CLK_MAX_NUM 16
@@ -432,10 +448,12 @@ struct dma_block {
*
* @use_edma: using edma as dma device or not
* @channel_bits: width of ASRCNCR register for each pair
+ * @start_before_dma: start asrc before dma
*/
struct fsl_asrc_soc_data {
bool use_edma;
unsigned int channel_bits;
+ bool start_before_dma;
};
/**
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
index 0cd595b0f629..c8a1a2b5915d 100644
--- a/sound/soc/fsl/fsl_asrc_common.h
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -107,6 +107,7 @@ struct fsl_asrc_pair {
* @asrc_rate: default sample rate for ASoC Back-Ends
* @asrc_format: default sample format for ASoC Back-Ends
* @use_edma: edma is used
+ * @start_before_dma: start asrc before dma
* @get_dma_channel: function pointer
* @request_pair: function pointer
* @release_pair: function pointer
@@ -116,6 +117,7 @@ struct fsl_asrc_pair {
* @m2m_start: function pointer
* @m2m_unprepare: function pointer
* @m2m_stop: function pointer
+ * @m2m_output_ready: function pointer, check output fifo ready or not
* @m2m_calc_out_len: function pointer
* @m2m_get_maxburst: function pointer
* @m2m_pair_suspend: function pointer
@@ -143,6 +145,7 @@ struct fsl_asrc {
int asrc_rate;
snd_pcm_format_t asrc_format;
bool use_edma;
+ bool start_before_dma;
struct dma_chan *(*get_dma_channel)(struct fsl_asrc_pair *pair, bool dir);
int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
@@ -154,6 +157,7 @@ struct fsl_asrc {
int (*m2m_start)(struct fsl_asrc_pair *pair);
int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
int (*m2m_stop)(struct fsl_asrc_pair *pair);
+ bool (*m2m_output_ready)(struct fsl_asrc_pair *pair);
int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 7dacc06b2f02..348b0aabfa68 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -449,18 +449,52 @@ fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component,
static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
- struct snd_card *card = rtd->card->snd_card;
+ struct device *dev = component->dev;
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_pair *pair;
struct snd_pcm *pcm = rtd->pcm;
+ struct dma_chan *chan;
int ret;
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret) {
- dev_err(card->dev, "failed to set DMA mask\n");
- return ret;
+ pair = kzalloc(size_add(sizeof(*pair), asrc->pair_priv_size), GFP_KERNEL);
+ if (!pair)
+ return -ENOMEM;
+
+ pair->asrc = asrc;
+ pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
+
+ /* Request a pair, which will be released later.
+ * Request pair function needs channel num as input, for this
+ * pair, we just request "1" channel temporarily.
+ */
+ ret = asrc->request_pair(1, pair);
+ if (ret < 0) {
+ dev_err(dev, "failed to request asrc pair\n");
+ goto req_pair_err;
+ }
+
+ /* Request a dma channel, which will be released later. */
+ chan = asrc->get_dma_channel(pair, IN);
+ if (!chan) {
+ dev_err(dev, "failed to get dma channel\n");
+ ret = -EINVAL;
+ goto dma_chan_err;
}
- return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
- card->dev, FSL_ASRC_DMABUF_SIZE);
+ ret = snd_pcm_set_fixed_buffer_all(pcm,
+ SNDRV_DMA_TYPE_DEV,
+ chan->device->dev,
+ FSL_ASRC_DMABUF_SIZE);
+
+ dma_release_channel(chan);
+
+dma_chan_err:
+ asrc->release_pair(pair);
+
+req_pair_err:
+ kfree(pair);
+
+ return ret;
}
struct snd_soc_component_driver fsl_asrc_component = {
diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c
index f46881f71e43..77999526dd9e 100644
--- a/sound/soc/fsl/fsl_asrc_m2m.c
+++ b/sound/soc/fsl/fsl_asrc_m2m.c
@@ -253,15 +253,21 @@ static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task
reinit_completion(&pair->complete[IN]);
reinit_completion(&pair->complete[OUT]);
+ if (asrc->start_before_dma)
+ asrc->m2m_start(pair);
+
/* Submit DMA request */
dmaengine_submit(pair->desc[IN]);
dma_async_issue_pending(pair->desc[IN]->chan);
if (out_dma_len > 0) {
+ if (asrc->start_before_dma && asrc->m2m_output_ready)
+ asrc->m2m_output_ready(pair);
dmaengine_submit(pair->desc[OUT]);
dma_async_issue_pending(pair->desc[OUT]->chan);
}
- asrc->m2m_start(pair);
+ if (!asrc->start_before_dma)
+ asrc->m2m_start(pair);
if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
dev_err(dev, "out DMA task timeout\n");
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index 7981d598ba13..40a3b7432174 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -444,6 +444,9 @@ static const struct of_device_id fsl_audmix_ids[] = {
{
.compatible = "fsl,imx8qm-audmix",
},
+ {
+ .compatible = "fsl,imx952-audmix",
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 2fa14fbdfe1a..148e09e58dfa 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
+#include <linux/firmware/imx/sm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
@@ -1425,10 +1426,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
struct fsl_sai *sai;
struct regmap *gpr;
void __iomem *base;
+ const char *str = NULL;
char tmp[8];
int irq, ret, i;
int index;
u32 dmas[4];
+ u32 val;
sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
@@ -1598,6 +1601,24 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (ret < 0 && ret != -ENOSYS)
goto err_pm_get_sync;
+ if (of_device_is_compatible(np, "fsl,imx952-sai") &&
+ !of_property_read_string(np, "fsl,sai-amix-mode", &str)) {
+ if (!strcmp(str, "bypass"))
+ val = FSL_SAI_AMIX_BYPASS;
+ else if (!strcmp(str, "audmix"))
+ val = FSL_SAI_AMIX_AUDMIX;
+ else
+ val = FSL_SAI_AMIX_NONE;
+
+ if (val < FSL_SAI_AMIX_NONE) {
+ ret = scmi_imx_misc_ctrl_set(SCMI_IMX952_CTRL_BYPASS_AUDMIX, val);
+ if (ret) {
+ dev_err_probe(dev, ret, "Error setting audmix mode\n");
+ goto err_pm_get_sync;
+ }
+ }
+ }
+
/*
* Register platform component before registering cpu dai for there
* is not defer probe for platform component in snd_soc_add_pcm_runtime().
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 6c917f79c6b0..7605cbaca3d8 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -230,6 +230,10 @@
#define FSL_SAI_DL_I2S BIT(0)
#define FSL_SAI_DL_PDM BIT(1)
+#define FSL_SAI_AMIX_BYPASS 0
+#define FSL_SAI_AMIX_AUDMIX 1
+#define FSL_SAI_AMIX_NONE 2
+
struct fsl_sai_soc_data {
bool use_imx_pcm;
bool use_edma;
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 320108bebf30..b2e1da1781ae 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1447,7 +1447,7 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
dev_err(dev, "failed to get SSI index property\n");
return -EINVAL;
}
- strcpy(ssi->card_name, "ac97-codec");
+ strscpy(ssi->card_name, "ac97-codec");
} else if (!of_property_read_bool(np, "fsl,ssi-asynchronous")) {
/*
* In synchronous mode, STCK and STFS ports are used by RX
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index 53f04d1f3280..76a8e68c1b62 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -145,7 +145,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
data->dai.ignore_pmdown_time = 1;
data->dai.cpus->dai_name = pdev->dev.platform_data;
- cpu_dai = snd_soc_find_dai(data->dai.cpus);
+ cpu_dai = snd_soc_find_dai_with_mutex(data->dai.cpus);
if (!cpu_dai) {
ret = -EPROBE_DEFER;
goto fail;
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 52e6266a7cb8..8a46285181fa 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -367,7 +367,7 @@ static const struct avs_acpi_boards i2s_boards[] = {
AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
- AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
+ AVS_MACH_ENTRY(HDA_GLK, avs_gml_i2s_machines),
AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines),
AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines),
AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines),
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 6e0e65584c7f..1a53856c2ffb 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -897,7 +897,7 @@ static const struct pci_device_id avs_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_KBL_H, &skl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) },
- { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GLK, &apl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) },
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index c8b586aced20..899906e0bcb4 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -134,7 +134,7 @@ static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev,
static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
struct avs_tplg_path_template_id *id2)
{
- return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
+ return id->id == id2->id && !sysfs_streq(id->tplg_name, id2->tplg_name);
}
static struct avs_path *avs_condpath_find_match(struct avs_dev *adev,
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index c23fdb6aad4c..c5942b5655d3 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -41,6 +41,9 @@ config SND_SOC_INTEL_SOF_CIRRUS_COMMON
config SND_SOC_INTEL_SOF_NUVOTON_COMMON
tristate
+config SND_SOC_INTEL_SOF_TI_COMMON
+ tristate
+
config SND_SOC_INTEL_SOF_BOARD_HELPERS
select SND_SOC_ACPI_INTEL_MATCH
tristate
@@ -327,11 +330,13 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
select SND_SOC_RT5645
select SND_SOC_RT5682_I2C
select SND_SOC_RT5682S
+ select SND_SOC_TAS2781_I2C
select SND_SOC_DMIC
select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_INTEL_SOF_BOARD_HELPERS
select SND_SOC_INTEL_SOF_MAXIM_COMMON
select SND_SOC_INTEL_SOF_REALTEK_COMMON
+ select SND_SOC_INTEL_SOF_TI_COMMON
select SND_SOC_ACPI_INTEL_MATCH
help
This adds support for ASoC machine driver for SOF platforms
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index fcd517d6c279..25a1a9066cbf 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -69,5 +69,8 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_CIRRUS_COMMON) += snd-soc-intel-sof-cirrus-common
snd-soc-intel-sof-nuvoton-common-y += sof_nuvoton_common.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_NUVOTON_COMMON) += snd-soc-intel-sof-nuvoton-common.o
+snd-soc-intel-sof-ti-common-y += sof_ti_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_TI_COMMON) += snd-soc-intel-sof-ti-common.o
+
snd-soc-intel-sof-board-helpers-y += sof_board_helpers.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_BOARD_HELPERS) += snd-soc-intel-sof-board-helpers.o
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 3d9d8a97d153..649378957b20 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -27,6 +27,7 @@
#include "sof_board_helpers.h"
#include "sof_maxim_common.h"
#include "sof_realtek_common.h"
+#include "sof_ti_common.h"
/* Driver-specific board quirks: from bit 0 to 7 */
#define SOF_RT5682_MCLK_EN BIT(0)
@@ -620,6 +621,9 @@ sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
ctx->amp_link->init = rt5650_spk_init;
ctx->amp_link->ops = &sof_rt5682_ops;
break;
+ case CODEC_TAS2563:
+ sof_tas2563_dai_link(ctx->amp_link);
+ break;
default:
dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
return -EINVAL;
@@ -767,6 +771,7 @@ static int sof_audio_probe(struct platform_device *pdev)
case CODEC_MAX98360A:
case CODEC_RT1019P:
case CODEC_RT5650:
+ case CODEC_TAS2563:
case CODEC_NONE:
/* no codec conf required */
break;
@@ -934,3 +939,4 @@ MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_BOARD_HELPERS");
MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_MAXIM_COMMON");
MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_REALTEK_COMMON");
+MODULE_IMPORT_NS("SND_SOC_INTEL_SOF_TI_COMMON");
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 50b838be24e9..ee34282828e4 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -1186,6 +1186,34 @@ static int create_bt_dailinks(struct snd_soc_card *card,
return 0;
}
+static int create_echoref_dailink(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id)
+{
+ struct device *dev = card->dev;
+ int ret;
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "Loopback_Virtual");
+
+ if (!name)
+ return -ENOMEM;
+
+ /*
+ * use dummy DAI names as this won't be connected to an actual DAI but just to establish a
+ * fe <-> be connection for loopback capture for echo reference
+ */
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
+ 0, 1, "Loopback Virtual Pin", "dummy",
+ snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name,
+ 1, NULL, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ dev_dbg(dev, "Added echo reference DAI link\n");
+
+ return 0;
+}
+
static int sof_card_dai_links_create(struct snd_soc_card *card)
{
struct device *dev = card->dev;
@@ -1294,8 +1322,12 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
goto err_end;
}
- /* allocate BE dailinks */
- num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num;
+ /*
+ * allocate BE dailinks, add an extra DAI link for echo reference capture.
+ * This should be the last DAI link and it is expected both for monolithic
+ * and functional SOF topologies to support echo reference.
+ */
+ num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + 1;
dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
if (!dai_links) {
ret = -ENOMEM;
@@ -1344,6 +1376,13 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
goto err_end;
}
+ /* dummy echo ref link. keep this as the last DAI link. The DAI link ID does not matter */
+ ret = create_echoref_dailink(card, &dai_links, &be_id);
+ if (ret) {
+ dev_err(dev, "failed to create echo ref dai link: %d\n", ret);
+ goto err_end;
+ }
+
WARN_ON(codec_conf != card->codec_conf + card->num_configs);
WARN_ON(dai_links != card->dai_link + card->num_links);
diff --git a/sound/soc/intel/boards/sof_ti_common.c b/sound/soc/intel/boards/sof_ti_common.c
new file mode 100644
index 000000000000..e527bdeb787e
--- /dev/null
+++ b/sound/soc/intel/boards/sof_ti_common.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2025 Intel Corporation
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/sof.h>
+#include <uapi/sound/asound.h>
+#include "../common/soc-intel-quirks.h"
+#include "sof_ti_common.h"
+
+/*
+ * Texas Instruments TAS2563 just mount one device to manage multiple devices,
+ * so the kcontrols, widgets and routes just keep one item, respectively.
+ */
+static const struct snd_kcontrol_new tas2563_spk_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget tas2563_spk_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route tas2563_spk_dapm_routes[] = {
+ { "Spk", NULL, "OUT" },
+};
+
+static struct snd_soc_dai_link_component tas2563_dai_link_components[] = {
+ {
+ .name = TAS2563_DEV0_NAME,
+ .dai_name = TAS2563_CODEC_DAI,
+ },
+};
+
+static int tas2563_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, tas2563_spk_dapm_widgets,
+ ARRAY_SIZE(tas2563_spk_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm widgets, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, tas2563_spk_kcontrols,
+ ARRAY_SIZE(tas2563_spk_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, tas2563_spk_dapm_routes,
+ ARRAY_SIZE(tas2563_spk_dapm_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+void sof_tas2563_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = tas2563_dai_link_components;
+ link->num_codecs = ARRAY_SIZE(tas2563_dai_link_components);
+ link->init = tas2563_init;
+}
+EXPORT_SYMBOL_NS(sof_tas2563_dai_link, "SND_SOC_INTEL_SOF_TI_COMMON");
+
+MODULE_DESCRIPTION("ASoC Intel SOF Texas Instruments helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_ti_common.h b/sound/soc/intel/boards/sof_ti_common.h
new file mode 100644
index 000000000000..de15845aff0c
--- /dev/null
+++ b/sound/soc/intel/boards/sof_ti_common.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2025 Intel Corporation.
+ */
+
+/*
+ * This file defines data structures used in Machine Driver for Intel
+ * platforms with Texas Instruments Codecs.
+ */
+#ifndef __SOF_TI_COMMON_H
+#define __SOF_TI_COMMON_H
+
+#include <sound/soc.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
+
+/*
+ * Texas Instruments TAS2563
+ */
+#define TAS2563_CODEC_DAI "tasdev_codec"
+#define TAS2563_DEV0_NAME "i2c-" TAS2563_ACPI_HID ":00"
+
+void sof_tas2563_dai_link(struct snd_soc_dai_link *link);
+
+#endif /* __SOF_TI_COMMON_H */
diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h
index c01d27e9fd88..df8a5fd95e13 100644
--- a/sound/soc/intel/catpt/core.h
+++ b/sound/soc/intel/catpt/core.h
@@ -62,6 +62,7 @@ struct catpt_module_type {
struct catpt_spec {
struct snd_soc_acpi_mach *machines;
u8 core_id;
+ const char *fw_name;
u32 host_dram_offset;
u32 host_iram_offset;
u32 host_shim_offset;
@@ -129,13 +130,13 @@ irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id);
* HOST <-> DSP communication yet failure to process specific request.
* Use below macro to convert returned non-zero values appropriately
*/
-#define CATPT_IPC_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO)
+#define CATPT_IPC_RET(ret) (((ret) <= 0) ? (ret) : -EREMOTEIO)
int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply, int timeout);
+ struct catpt_ipc_msg *reply, int timeout, const char *name);
int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply);
+ struct catpt_ipc_msg *reply, const char *name);
int catpt_first_boot_firmware(struct catpt_dev *cdev);
int catpt_boot_firmware(struct catpt_dev *cdev, bool restore);
diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c
index d13062c8e907..0638aecba40d 100644
--- a/sound/soc/intel/catpt/device.c
+++ b/sound/soc/intel/catpt/device.c
@@ -41,7 +41,7 @@ static int catpt_do_suspend(struct device *dev)
memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
if (ret) {
- ret = CATPT_IPC_ERROR(ret);
+ ret = CATPT_IPC_RET(ret);
goto release_dma_chan;
}
@@ -107,7 +107,7 @@ static int catpt_resume(struct device *dev)
ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
}
return 0;
@@ -348,6 +348,7 @@ static struct snd_soc_acpi_mach wpt_machines[] = {
static struct catpt_spec lpt_desc = {
.machines = lpt_machines,
.core_id = 0x01,
+ .fw_name = "intel/IntcSST1.bin",
.host_dram_offset = 0x000000,
.host_iram_offset = 0x080000,
.host_shim_offset = 0x0E7000,
@@ -363,6 +364,7 @@ static struct catpt_spec lpt_desc = {
static struct catpt_spec wpt_desc = {
.machines = wpt_machines,
.core_id = 0x02,
+ .fw_name = "intel/IntcSST2.bin",
.host_dram_offset = 0x000000,
.host_iram_offset = 0x0A0000,
.host_shim_offset = 0x0FB000,
diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c
index d26863249097..5a01a9afb26e 100644
--- a/sound/soc/intel/catpt/ipc.c
+++ b/sound/soc/intel/catpt/ipc.c
@@ -84,7 +84,7 @@ static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply, int timeout)
+ struct catpt_ipc_msg *reply, int timeout, const char *name)
{
struct catpt_ipc *ipc = &cdev->ipc;
unsigned long flags;
@@ -111,6 +111,8 @@ static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
}
ret = ipc->rx.rsp.status;
+ if (ret)
+ dev_err(cdev->dev, "%s (0x%08x) failed: %d\n", name, request.header, ret);
if (reply) {
reply->header = ipc->rx.header;
@@ -123,23 +125,23 @@ static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply, int timeout)
+ struct catpt_ipc_msg *reply, int timeout, const char *name)
{
struct catpt_ipc *ipc = &cdev->ipc;
int ret;
mutex_lock(&ipc->mutex);
- ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout);
+ ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout, name);
mutex_unlock(&ipc->mutex);
return ret;
}
int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
- struct catpt_ipc_msg *reply)
+ struct catpt_ipc_msg *reply, const char *name)
{
return catpt_dsp_send_msg_timeout(cdev, request, reply,
- cdev->ipc.default_timeout);
+ cdev->ipc.default_timeout, name);
}
static void
diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c
index f5705cd2c1e1..dc7afe587e6f 100644
--- a/sound/soc/intel/catpt/loader.c
+++ b/sound/soc/intel/catpt/loader.c
@@ -580,10 +580,6 @@ release_fw:
static int catpt_load_images(struct catpt_dev *cdev, bool restore)
{
- static const char *const names[] = {
- "intel/IntcSST1.bin",
- "intel/IntcSST2.bin",
- };
struct dma_chan *chan;
int ret;
@@ -591,7 +587,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore)
if (IS_ERR(chan))
return PTR_ERR(chan);
- ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1],
+ ret = catpt_load_image(cdev, chan, cdev->spec->fw_name,
FW_SIGNATURE, restore);
if (ret)
goto release_dma_chan;
@@ -656,7 +652,7 @@ int catpt_first_boot_firmware(struct catpt_dev *cdev)
ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
ret = catpt_arm_stream_templates(cdev);
if (ret) {
diff --git a/sound/soc/intel/catpt/messages.c b/sound/soc/intel/catpt/messages.c
index 30eec2de4dc1..688a2d79500d 100644
--- a/sound/soc/intel/catpt/messages.c
+++ b/sound/soc/intel/catpt/messages.c
@@ -15,17 +15,12 @@ int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_FW_VERSION);
struct catpt_ipc_msg request = {{0}}, reply;
- int ret;
request.header = msg.val;
reply.size = sizeof(*version);
reply.data = version;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "get fw version failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, &reply, "get fw version");
}
struct catpt_alloc_stream_input {
@@ -94,11 +89,7 @@ int catpt_ipc_alloc_stream(struct catpt_dev *cdev,
reply.size = sizeof(*sinfo);
reply.data = sinfo;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "alloc stream type %d failed: %d\n",
- type, ret);
-
+ ret = catpt_dsp_send_msg(cdev, request, &reply, "alloc stream");
kfree(payload);
return ret;
}
@@ -107,18 +98,12 @@ int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(FREE_STREAM);
struct catpt_ipc_msg request;
- int ret;
request.header = msg.val;
request.size = sizeof(stream_hw_id);
request.data = &stream_hw_id;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "free stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "free stream");
}
int catpt_ipc_set_device_format(struct catpt_dev *cdev,
@@ -126,17 +111,12 @@ int catpt_ipc_set_device_format(struct catpt_dev *cdev,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(SET_DEVICE_FORMATS);
struct catpt_ipc_msg request;
- int ret;
request.header = msg.val;
request.size = sizeof(*devfmt);
request.data = devfmt;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "set device format failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "set device format");
}
int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
@@ -144,7 +124,6 @@ int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(ENTER_DX_STATE);
struct catpt_ipc_msg request, reply;
- int ret;
request.header = msg.val;
request.size = sizeof(state);
@@ -152,11 +131,7 @@ int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
reply.size = sizeof(*context);
reply.data = context;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "enter dx state failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, &reply, "enter dx state");
}
int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
@@ -164,68 +139,45 @@ int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_MIXER_STREAM_INFO);
struct catpt_ipc_msg request = {{0}}, reply;
- int ret;
request.header = msg.val;
reply.size = sizeof(*info);
reply.data = info;
- ret = catpt_dsp_send_msg(cdev, request, &reply);
- if (ret)
- dev_err(cdev->dev, "get mixer info failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, &reply, "get mixer info");
}
int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(RESET_STREAM);
struct catpt_ipc_msg request = {{0}};
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "reset stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "reset stream");
}
int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(PAUSE_STREAM);
struct catpt_ipc_msg request = {{0}};
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "pause stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "pause stream");
}
int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(RESUME_STREAM);
struct catpt_ipc_msg request = {{0}};
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "resume stream %d failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "resume stream");
}
struct catpt_set_volume_input {
@@ -243,7 +195,6 @@ int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_VOLUME);
struct catpt_ipc_msg request;
struct catpt_set_volume_input input;
- int ret;
msg.stream_hw_id = stream_hw_id;
input.channel = channel;
@@ -255,12 +206,7 @@ int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
request.size = sizeof(input);
request.data = &input;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "set stream %d volume failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "set stream volume");
}
struct catpt_set_write_pos_input {
@@ -275,7 +221,6 @@ int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_WRITE_POSITION);
struct catpt_ipc_msg request;
struct catpt_set_write_pos_input input;
- int ret;
msg.stream_hw_id = stream_hw_id;
input.new_write_pos = pos;
@@ -286,28 +231,18 @@ int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
request.size = sizeof(input);
request.data = &input;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "set stream %d write pos failed: %d\n",
- stream_hw_id, ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "set stream write pos");
}
int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute)
{
union catpt_stream_msg msg = CATPT_STAGE_MSG(MUTE_LOOPBACK);
struct catpt_ipc_msg request;
- int ret;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
request.size = sizeof(mute);
request.data = &mute;
- ret = catpt_dsp_send_msg(cdev, request, NULL);
- if (ret)
- dev_err(cdev->dev, "mute loopback failed: %d\n", ret);
-
- return ret;
+ return catpt_dsp_send_msg(cdev, request, NULL, "mute loopback");
}
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
index abd1cb07c60c..2c3405686f79 100644
--- a/sound/soc/intel/catpt/pcm.c
+++ b/sound/soc/intel/catpt/pcm.c
@@ -114,14 +114,10 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
return result;
}
-static u32 catpt_stream_read_position(struct catpt_dev *cdev,
- struct catpt_stream_runtime *stream)
+static void catpt_stream_read_position(struct catpt_dev *cdev,
+ struct catpt_stream_runtime *stream, u32 *pos)
{
- u32 pos;
-
- memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr,
- sizeof(pos));
- return pos;
+ memcpy_fromio(pos, cdev->lpe_ba + stream->info.read_pos_regaddr, sizeof(*pos));
}
static u32 catpt_stream_volume(struct catpt_dev *cdev,
@@ -365,9 +361,7 @@ static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)
return catpt_set_dspvol(cdev, id, (long *)pos->private_value);
ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value);
- if (ret)
- return CATPT_IPC_ERROR(ret);
- return 0;
+ return CATPT_IPC_RET(ret);
}
static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
@@ -414,7 +408,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
cdev->scratch,
&stream->info);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
ret = catpt_dai_apply_usettings(dai, stream);
if (ret) {
@@ -456,11 +450,11 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream,
ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
stream->prepared = true;
return 0;
@@ -491,7 +485,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
pos, false, false);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
fallthrough;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -499,7 +493,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
catpt_dsp_update_lpclock(cdev);
ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -510,7 +504,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
catpt_dsp_update_lpclock(cdev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
break;
default:
@@ -617,7 +611,7 @@ catpt_component_pointer(struct snd_soc_component *component,
return 0;
stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- pos = catpt_stream_read_position(cdev, stream);
+ catpt_stream_read_position(cdev, stream, &pos);
return bytes_to_frames(substream->runtime, pos);
}
@@ -679,7 +673,7 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
/* store device format set for given SSP */
memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt));
@@ -693,7 +687,7 @@ static const struct snd_soc_dai_ops catpt_dai_ops = {
static struct snd_soc_dai_driver dai_drivers[] = {
/* FE DAIs */
{
- .name = "System Pin",
+ .name = "System Pin",
.id = CATPT_STRM_TYPE_SYSTEM,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -716,7 +710,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Offload0 Pin",
+ .name = "Offload0 Pin",
.id = CATPT_STRM_TYPE_RENDER,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -730,7 +724,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Offload1 Pin",
+ .name = "Offload1 Pin",
.id = CATPT_STRM_TYPE_RENDER,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -744,7 +738,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Loopback Pin",
+ .name = "Loopback Pin",
.id = CATPT_STRM_TYPE_LOOPBACK,
.ops = &catpt_fe_dai_ops,
.capture = {
@@ -758,7 +752,7 @@ static struct snd_soc_dai_driver dai_drivers[] = {
},
},
{
- .name = "Bluetooth Pin",
+ .name = "Bluetooth Pin",
.id = CATPT_STRM_TYPE_BLUETOOTH_RENDER,
.ops = &catpt_fe_dai_ops,
.playback = {
@@ -849,9 +843,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol)
}
}
- if (ret)
- return CATPT_IPC_ERROR(ret);
- return 0;
+ return CATPT_IPC_RET(ret);
}
static int catpt_volume_info(struct snd_kcontrol *kcontrol,
@@ -1041,7 +1033,7 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
*(bool *)kcontrol->private_value = mute;
return 0;
diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c
index e961e172f9b7..0cb122a4dfd2 100644
--- a/sound/soc/intel/catpt/sysfs.c
+++ b/sound/soc/intel/catpt/sysfs.c
@@ -24,7 +24,7 @@ static ssize_t fw_version_show(struct device *dev,
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
- return CATPT_IPC_ERROR(ret);
+ return CATPT_IPC_RET(ret);
return sysfs_emit(buf, "%d.%d.%d.%d\n", version.type, version.major,
version.minor, version.build);
diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
index 6bf7a6250ddc..c952f7d2b2c0 100644
--- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
@@ -45,23 +45,22 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.group_id = 1,
};
-/*
- * RT722 is a multi-function codec, three endpoints are created for
- * its headset, amp and dmic functions.
- */
-static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
+ /* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
+ /* Amp Endpoint, work as spk_l_endpoint */
{
.num = 1,
- .aggregated = 0,
+ .aggregated = 1,
.group_position = 0,
- .group_id = 0,
+ .group_id = 1,
},
+ /* DMIC Endpoint */
{
.num = 2,
.aggregated = 0,
@@ -229,11 +228,11 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
}
};
-static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = {
{
.adr = 0x000030025D072201ull,
- .num_endpoints = ARRAY_SIZE(rt722_endpoints),
- .endpoints = rt722_endpoints,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
.name_prefix = "rt722"
}
};
@@ -394,8 +393,8 @@ static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = {
static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = {
{
.mask = BIT(0),
- .num_adr = ARRAY_SIZE(rt722_0_single_adr),
- .adr_d = rt722_0_single_adr,
+ .num_adr = ARRAY_SIZE(rt722_0_agg_adr),
+ .adr_d = rt722_0_agg_adr,
},
{
.mask = BIT(2),
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
index 1270ee21ee72..72c35e73078e 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -1013,7 +1013,7 @@ static const struct snd_soc_acpi_adr_device cs42l42_0_adr[] = {
static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = {
{
- .adr = 0x0000380102000001ull,
+ .adr = 0x00003c0102000001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "tas2783-1"
@@ -1023,6 +1023,18 @@ static const struct snd_soc_acpi_adr_device tas2783_0_adr[] = {
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "tas2783-2"
+ },
+ {
+ .adr = 0x00003d0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "tas2783-3"
+ },
+ {
+ .adr = 0x00003a0102000001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "tas2783-4"
}
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
index e297c8ecedb7..ddd919847c1f 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
@@ -356,33 +356,15 @@ static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = {
}
};
-static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = {
+static const struct snd_soc_acpi_adr_device rt722_0_agg_adr[] = {
{
- .adr = 0x000030025d072101ull,
- .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
- .endpoints = rt_mf_endpoints,
- .name_prefix = "rt721"
- }
-};
-
-static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = {
- {
- .adr = 0x000330025d072101ull,
- .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
- .endpoints = rt_mf_endpoints,
- .name_prefix = "rt721"
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
+ .name_prefix = "rt722"
}
};
-static const struct snd_soc_acpi_link_adr ptl_rt721_l3[] = {
- {
- .mask = BIT(3),
- .num_adr = ARRAY_SIZE(rt721_3_single_adr),
- .adr_d = rt721_3_single_adr,
- },
- {},
-};
-
static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
{
.adr = 0x000030025d072201ull,
@@ -497,15 +479,6 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l2_cs35l56x6_l13[] = {
{}
};
-static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = {
- {
- .mask = BIT(0),
- .num_adr = ARRAY_SIZE(rt721_0_single_adr),
- .adr_d = rt721_0_single_adr,
- },
- {}
-};
-
static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = {
{
.mask = BIT(0),
@@ -536,8 +509,8 @@ static const struct snd_soc_acpi_link_adr ptl_rt722_l3[] = {
static const struct snd_soc_acpi_link_adr ptl_rt722_l0_rt1320_l23[] = {
{
.mask = BIT(0),
- .num_adr = ARRAY_SIZE(rt722_0_single_adr),
- .adr_d = rt722_0_single_adr,
+ .num_adr = ARRAY_SIZE(rt722_0_agg_adr),
+ .adr_d = rt722_0_agg_adr,
},
{
.mask = BIT(2),
@@ -727,13 +700,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
},
{
.link_mask = BIT(0),
- .links = ptl_rt721_l0,
- .drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt721.tplg",
- .get_function_tplg_files = sof_sdw_get_tplg_files,
- },
- {
- .link_mask = BIT(0),
.links = ptl_rt722_only,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
@@ -756,13 +722,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
},
{
.link_mask = BIT(3),
- .links = ptl_rt721_l3,
- .drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt721.tplg",
- .get_function_tplg_files = sof_sdw_get_tplg_files,
- },
- {
- .link_mask = BIT(3),
.links = ptl_rt722_l3,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
diff --git a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
index f56f4bfa5187..a12b11f2cd7a 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
@@ -65,6 +65,9 @@ static const struct codec_map amps[] = {
CODEC_MAP_ENTRY("RT1019P", "rt1019", RT1019P_ACPI_HID, CODEC_RT1019P),
CODEC_MAP_ENTRY("RT1308", "rt1308", RT1308_ACPI_HID, CODEC_RT1308),
+ /* Texas Instruments */
+ CODEC_MAP_ENTRY("TAS2563", "tas2563", TAS2563_ACPI_HID, CODEC_TAS2563),
+
/*
* Monolithic components
*
diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c
index 5ef15ec988be..e849e7a649bc 100644
--- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c
+++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c
@@ -342,9 +342,10 @@ static const struct snd_soc_ops mt8189_es8326_ops = {
static int mt8189_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
- ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_dumb_spk_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, mt8189_dumb_spk_widgets,
ARRAY_SIZE(mt8189_dumb_spk_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
@@ -418,10 +419,11 @@ static int mt8189_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8189_JACK_HEADSET];
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct mtk_platform_card_data *card_data = soc_card_data->card_data;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
int ret;
int type;
- ret = snd_soc_dapm_new_controls(&card->dapm, mt8189_headset_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, mt8189_headset_widgets,
ARRAY_SIZE(mt8189_headset_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
index 723cab01e72e..5dcc8ed26e00 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c
@@ -2651,14 +2651,9 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
etdm_data = afe_priv->dai_priv[dai_id];
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-mclk-always-on-rate",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-mclk-always-on-rate",
+ of_afe_etdms[i].name);
ret = of_property_read_u32(of_node, prop, &sel);
if (ret == 0) {
etdm_data->mclk_dir = SND_SOC_CLOCK_OUT;
@@ -2667,24 +2662,14 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
__func__, sel);
}
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-multi-pin-mode",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-multi-pin-mode",
+ of_afe_etdms[i].name);
etdm_data->data_mode = of_property_read_bool(of_node, prop);
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-cowork-source",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-cowork-source",
+ of_afe_etdms[i].name);
ret = of_property_read_u32(of_node, prop, &sel);
if (ret == 0) {
if (sel >= MT8195_AFE_IO_ETDM_NUM) {
@@ -2706,14 +2691,9 @@ static void mt8195_dai_etdm_parse_of(struct mtk_base_afe *afe)
dai_id = ETDM_TO_DAI_ID(i);
etdm_data = afe_priv->dai_priv[dai_id];
- ret = snprintf(prop, sizeof(prop),
- "mediatek,%s-chn-disabled",
- of_afe_etdms[i].name);
- if (ret < 0) {
- dev_info(afe->dev, "%s snprintf err=%d\n",
- __func__, ret);
- return;
- }
+ scnprintf(prop, sizeof(prop),
+ "mediatek,%s-chn-disabled",
+ of_afe_etdms[i].name);
ret = of_property_read_variable_u8_array(of_node, prop,
disable_chn,
1, max_chn);
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index e54abcd39f79..4bd14ae330d5 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -18,7 +18,6 @@ config SND_PXA2XX_SOC_AC97
select AC97_BUS_NEW
select SND_PXA2XX_LIB
select SND_PXA2XX_LIB_AC97
- select SND_SOC_AC97_BUS_NEW
config SND_PXA2XX_SOC_I2S
select SND_PXA2XX_LIB
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 78f50032afc5..109a4958d9c0 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -222,9 +222,7 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
{
int ret;
struct ac97_controller *ctrl;
- pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
struct resource *regs;
- void **codecs_pdata;
if (pdev->id != -1) {
dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
@@ -247,10 +245,9 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
return ret;
}
- codecs_pdata = pdata ? pdata->codec_pdata : NULL;
ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev,
AC97_SLOTS_AVAILABLE_ALL,
- codecs_pdata);
+ NULL);
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index ded49124581b..241c3b4479c6 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -202,6 +202,31 @@ struct apm_display_port_module_intf_cfg {
} __packed;
#define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8)
+struct apm_module_sp_vi_op_mode_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_sp_vi_op_mode_cfg cfg;
+} __packed;
+
+#define APM_SP_VI_OP_MODE_CFG_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_module_sp_vi_op_mode_cfg) + \
+ (ch) * sizeof(uint32_t), 8)
+
+struct apm_module_sp_vi_ex_mode_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_sp_vi_ex_mode_cfg cfg;
+} __packed;
+
+#define APM_SP_VI_EX_MODE_CFG_PSIZE ALIGN(sizeof(struct apm_module_sp_vi_ex_mode_cfg), 8)
+
+struct apm_module_sp_vi_channel_map_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_sp_vi_channel_map_cfg cfg;
+} __packed;
+
+#define APM_SP_VI_CH_MAP_CFG_PSIZE(ch) ALIGN( \
+ sizeof(struct apm_module_sp_vi_channel_map_cfg) + \
+ (ch) * sizeof(uint32_t), 8)
+
static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token,
uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr)
{
@@ -284,7 +309,7 @@ void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels)
EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping);
static void apm_populate_container_config(struct apm_container_obj *cfg,
- struct audioreach_container *cont)
+ const struct audioreach_container *cont)
{
/* Container Config */
@@ -314,7 +339,7 @@ static void apm_populate_container_config(struct apm_container_obj *cfg,
}
static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
- struct audioreach_sub_graph *sg)
+ const struct audioreach_sub_graph *sg)
{
cfg->sub_graph_cfg.sub_graph_id = sg->sub_graph_id;
cfg->sub_graph_cfg.num_sub_graph_prop = APM_SUB_GRAPH_CFG_NPROP;
@@ -336,7 +361,7 @@ static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
}
static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
- struct audioreach_module *module)
+ const struct audioreach_module *module)
{
obj->instance_id = module->instance_id;
@@ -348,7 +373,7 @@ static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
}
static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
- struct audioreach_container *container,
+ const struct audioreach_container *container,
int sub_graph_id)
{
struct audioreach_module *module;
@@ -365,9 +390,10 @@ static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
}
}
-static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph_info *info,
+static void audioreach_populate_graph(struct q6apm *apm,
+ const struct audioreach_graph_info *info,
struct apm_graph_open_params *open,
- struct list_head *sg_list,
+ const struct list_head *sg_list,
int num_sub_graphs)
{
struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data;
@@ -439,7 +465,8 @@ static void audioreach_populate_graph(struct q6apm *apm, struct audioreach_graph
}
}
-void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info *info)
+void *audioreach_alloc_graph_pkt(struct q6apm *apm,
+ const struct audioreach_graph_info *info)
{
int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz;
struct apm_module_param_data *param_data;
@@ -452,7 +479,7 @@ void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info
struct audioreach_module *module;
struct audioreach_sub_graph *sgs;
struct apm_mod_list_obj *mlobj;
- struct list_head *sg_list;
+ const struct list_head *sg_list;
int num_connections = 0;
int num_containers = 0;
int num_sub_graphs = 0;
@@ -605,8 +632,8 @@ int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pk
EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync);
static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_display_port_module_intf_cfg *intf_cfg;
struct apm_module_frame_size_factor_cfg *fs_cfg;
@@ -617,6 +644,7 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
int fs_sz = APM_FS_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -661,8 +689,8 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph,
/* LPASS Codec DMA port Module Media Format Setup */
static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_codec_dma_module_intf_cfg *intf_cfg;
struct apm_module_frame_size_factor_cfg *fs_cfg;
@@ -675,6 +703,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz + pm_sz;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -726,19 +755,20 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
-int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+int audioreach_send_u32_param(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
uint32_t param_id, uint32_t param_val)
{
struct apm_module_param_data *param_data;
- struct gpr_pkt *pkt __free(kfree) = NULL;
uint32_t *param;
int payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE;
- void *p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
- if (IS_ERR(p))
+ void *p;
+
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
return -ENOMEM;
- pkt = p;
- p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = p;
param_data->module_instance_id = module->instance_id;
@@ -755,39 +785,41 @@ int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_modul
EXPORT_SYMBOL_GPL(audioreach_send_u32_param);
static int audioreach_sal_limiter_enable(struct q6apm_graph *graph,
- struct audioreach_module *module, bool enable)
+ const struct audioreach_module *module,
+ bool enable)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_LIMITER_ENABLE, enable);
}
static int audioreach_sal_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_SAL_OUTPUT_CFG, cfg->bit_width);
}
static int audioreach_module_enable(struct q6apm_graph *graph,
- struct audioreach_module *module,
+ const struct audioreach_module *module,
bool enable)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, enable);
}
static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
return audioreach_send_u32_param(graph, module, PARAM_ID_EARLY_EOS_DELAY,
EARLY_EOS_DELAY_MS);
}
static int audioreach_set_module_config(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
int size = le32_to_cpu(module->data->size);
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -800,8 +832,8 @@ static int audioreach_set_module_config(struct q6apm_graph *graph,
}
static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_module_param_data *param_data;
struct param_id_mfc_media_format *media_format;
@@ -810,6 +842,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
APM_MODULE_PARAM_DATA_SIZE;
int i;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -834,7 +867,8 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
- void *p, struct audioreach_module_config *mcfg)
+ void *p,
+ const struct audioreach_module_config *mcfg)
{
struct payload_media_fmt_aac_t *aac_cfg;
struct payload_media_fmt_pcm *mp3_cfg;
@@ -915,20 +949,21 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
return 0;
}
-int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg)
+int audioreach_compr_set_param(struct q6apm_graph *graph,
+ const struct audioreach_module_config *mcfg)
{
struct media_format *header;
int rc;
void *p;
int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
int payload_size = sizeof(struct apm_sh_module_media_fmt_cmd);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(payload_size,
DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT,
0, graph->port->id, iid);
if (IS_ERR(pkt))
return -ENOMEM;
-
p = (void *)pkt + GPR_HDR_SIZE;
header = p;
rc = audioreach_set_compr_media_format(header, p, mcfg);
@@ -940,8 +975,8 @@ int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_modu
EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
struct apm_module_frame_size_factor_cfg *fs_cfg;
struct apm_module_param_data *param_data;
@@ -952,6 +987,7 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
int fs_sz = APM_FS_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1007,12 +1043,13 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module)
+ const struct audioreach_module *module)
{
struct apm_module_param_data *param_data;
struct data_logging_config *cfg;
int size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
void *p;
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1035,15 +1072,14 @@ static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *mcfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *mcfg)
{
struct payload_pcm_output_format_cfg *media_cfg;
uint32_t num_channels = mcfg->num_channels;
struct apm_pcm_module_media_fmt_cmd *cfg;
struct apm_module_param_data *param_data;
int payload_size;
- struct gpr_pkt *pkt __free(kfree) = NULL;
if (num_channels > 4) {
dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
@@ -1052,7 +1088,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels);
- pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1082,15 +1119,14 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
}
static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *mcfg)
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *mcfg)
{
uint32_t num_channels = mcfg->num_channels;
struct apm_module_param_data *param_data;
struct payload_media_fmt_pcm *cfg;
struct media_format *header;
int rc, payload_size;
- struct gpr_pkt *pkt __free(kfree) = NULL;
void *p;
if (num_channels > 4) {
@@ -1100,8 +1136,9 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels) + APM_MODULE_PARAM_DATA_SIZE;
- pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0,
- graph->port->id, module->instance_id);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0,
+ graph->port->id, module->instance_id);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -1139,7 +1176,8 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
return audioreach_graph_send_cmd_sync(graph, pkt, 0);
}
-int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol)
+int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
+ const struct audioreach_module *module, int vol)
{
struct param_id_vol_ctrl_master_gain *cfg;
struct apm_module_param_data *param_data;
@@ -1164,7 +1202,8 @@ int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *mo
}
EXPORT_SYMBOL_GPL(audioreach_gain_set_vol_ctrl);
-static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_module *module)
+static int audioreach_gain_set(struct q6apm_graph *graph,
+ const struct audioreach_module *module)
{
struct apm_module_param_data *param_data;
struct apm_gain_module_cfg *cfg;
@@ -1186,8 +1225,96 @@ static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_modu
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
-int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module,
- struct audioreach_module_config *cfg)
+static int audioreach_speaker_protection(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ uint32_t operation_mode)
+{
+ return audioreach_send_u32_param(graph, module, PARAM_ID_SP_OP_MODE,
+ operation_mode);
+}
+
+static int audioreach_speaker_protection_vi(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *mcfg)
+{
+ u32 num_channels = mcfg->num_channels;
+ struct apm_module_sp_vi_op_mode_cfg *op_cfg;
+ struct apm_module_sp_vi_channel_map_cfg *cm_cfg;
+ struct apm_module_sp_vi_ex_mode_cfg *ex_cfg;
+ int op_sz, cm_sz, ex_sz;
+ struct apm_module_param_data *param_data;
+ int rc, i, payload_size;
+ struct gpr_pkt *pkt;
+ void *p;
+
+ if (num_channels > 2) {
+ dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
+ return -EINVAL;
+ }
+
+ op_sz = APM_SP_VI_OP_MODE_CFG_PSIZE(num_channels);
+ /* Channel mapping for Isense and Vsense, thus twice number of speakers. */
+ cm_sz = APM_SP_VI_CH_MAP_CFG_PSIZE(num_channels * 2);
+ ex_sz = APM_SP_VI_EX_MODE_CFG_PSIZE;
+
+ payload_size = op_sz + cm_sz + ex_sz;
+
+ pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ op_cfg = p;
+ param_data = &op_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_SP_VI_OP_MODE_CFG;
+ param_data->param_size = op_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ op_cfg->cfg.num_channels = num_channels;
+ op_cfg->cfg.operation_mode = PARAM_ID_SP_VI_OP_MODE_NORMAL;
+ p += op_sz;
+
+ cm_cfg = p;
+ param_data = &cm_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_SP_VI_CHANNEL_MAP_CFG;
+ param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ cm_cfg->cfg.num_channels = num_channels * 2;
+ for (i = 0; i < num_channels; i++) {
+ /*
+ * Map speakers into Vsense and then Isense of each channel.
+ * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to:
+ * [1, 2, 3, 4]
+ */
+ cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1;
+ cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2;
+ }
+
+ p += cm_sz;
+
+ ex_cfg = p;
+ param_data = &ex_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_SP_VI_EX_MODE_CFG;
+ param_data->param_size = ex_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ ex_cfg->cfg.factory_mode = 0;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audioreach_set_media_format(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
{
int rc;
@@ -1235,6 +1362,14 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
case MODULE_ID_GAPLESS:
rc = audioreach_gapless_set_media_format(graph, module, cfg);
break;
+ case MODULE_ID_SPEAKER_PROTECTION:
+ rc = audioreach_speaker_protection(graph, module,
+ PARAM_ID_SP_OP_MODE_NORMAL);
+ break;
+ case MODULE_ID_SPEAKER_PROTECTION_VI:
+ rc = audioreach_speaker_protection_vi(graph, module, cfg);
+ break;
+
default:
rc = 0;
}
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index d1b60b36468a..89f172aab8c0 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -31,6 +31,8 @@ struct q6apm_graph;
#define MODULE_ID_MP3_DECODE 0x0700103B
#define MODULE_ID_GAPLESS 0x0700104D
#define MODULE_ID_DISPLAY_PORT_SINK 0x07001069
+#define MODULE_ID_SPEAKER_PROTECTION 0x070010E2
+#define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3
#define MODULE_ID_OPUS_DEC 0x07001174
#define APM_CMD_GET_SPF_STATE 0x01001021
@@ -559,6 +561,43 @@ struct data_logging_config {
uint32_t mode;
} __packed;
+/* Speaker Protection */
+#define PARAM_ID_SP_OP_MODE 0x080011e9
+#define PARAM_ID_SP_OP_MODE_NORMAL 0
+#define PARAM_ID_SP_OP_MODE_CALIBRATION 1
+#define PARAM_ID_SP_OP_MODE_FACTORY_TEST 2
+#define PARAM_ID_SP_OP_MODE_VALIDATION 3
+
+struct param_id_sp_op_mode {
+ uint32_t operation_mode;
+} __packed;
+
+/* Speaker Protection VI */
+
+#define PARAM_ID_SP_VI_OP_MODE_CFG 0x080011f4
+#define PARAM_ID_SP_VI_OP_MODE_NORMAL 0
+#define PARAM_ID_SP_VI_OP_MODE_CALIBRATION 1
+#define PARAM_ID_SP_VI_OP_MODE_FACTORY_TEST 2
+#define PARAM_ID_SP_VI_OP_MODE_VALIDATION 3
+struct param_id_sp_vi_op_mode_cfg {
+ uint32_t num_channels;
+ uint32_t operation_mode;
+ uint32_t quick_calibration;
+ uint32_t r0_t0_selection[];
+} __packed;
+
+#define PARAM_ID_SP_VI_EX_MODE_CFG 0x080011ff
+struct param_id_sp_vi_ex_mode_cfg {
+ uint32_t factory_mode;
+} __packed;
+
+#define PARAM_ID_SP_VI_CHANNEL_MAP_CFG 0x08001203
+struct param_id_sp_vi_channel_map_cfg {
+ uint32_t num_channels;
+ /* [ Vsense of ch 1, Isense of ch 1, Vsense of ch 2, Isense of ch 2, ... ] */
+ uint32_t channel_mapping[];
+} __packed;
+
#define PARAM_ID_SAL_OUTPUT_CFG 0x08001016
struct param_id_sal_output_config {
uint32_t bits_per_sample;
@@ -792,8 +831,8 @@ void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
uint32_t token, uint32_t src_port,
uint32_t dest_port);
-void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info
- *info);
+void *audioreach_alloc_graph_pkt(struct q6apm *apm,
+ const struct audioreach_graph_info *info);
/* Topology specific */
int audioreach_tplg_init(struct snd_soc_component *component);
@@ -809,13 +848,15 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_
int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
uint32_t rsp_opcode);
int audioreach_set_media_format(struct q6apm_graph *graph,
- struct audioreach_module *module,
- struct audioreach_module_config *cfg);
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg);
int audioreach_shared_memory_send_eos(struct q6apm_graph *graph);
int audioreach_gain_set_vol_ctrl(struct q6apm *apm,
- struct audioreach_module *module, int vol);
-int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_module *module,
+ const struct audioreach_module *module, int vol);
+int audioreach_send_u32_param(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
uint32_t param_id, uint32_t param_val);
-int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg);
+int audioreach_compr_set_param(struct q6apm_graph *graph,
+ const struct audioreach_module_config *mcfg);
#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c
index 0b8d06ec8b26..40bc44003453 100644
--- a/sound/soc/qcom/qdsp6/q6adm.c
+++ b/sound/soc/qcom/qdsp6/q6adm.c
@@ -186,11 +186,11 @@ static void q6adm_free_copp(struct kref *ref)
kfree(c);
}
-static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+static int q6adm_callback(struct apr_device *adev, const struct apr_resp_pkt *data)
{
- struct aprv2_ibasic_rsp_result_t *result = data->payload;
+ const struct aprv2_ibasic_rsp_result_t *result = data->payload;
int port_idx, copp_idx;
- struct apr_hdr *hdr = &data->hdr;
+ const struct apr_hdr *hdr = &data->hdr;
struct q6copp *copp;
struct q6adm *adm = dev_get_drvdata(&adev->dev);
@@ -331,6 +331,7 @@ static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp,
int afe_port = q6afe_get_port_id(port_id);
struct apr_pkt *pkt;
int ret, pkt_size = APR_HDR_SIZE + sizeof(*open);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -466,6 +467,7 @@ int q6adm_matrix_map(struct device *dev, int path,
struct q6copp *copp;
int pkt_size = (APR_HDR_SIZE + sizeof(*route) + sizeof(*node) +
(sizeof(uint32_t) * payload_map.num_copps));
+
void *matrix_map __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!matrix_map)
return -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index 0b01fc9e13a7..76e14fc1b2b5 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -931,13 +931,11 @@ static void q6afe_port_free(struct kref *ref)
{
struct q6afe_port *port;
struct q6afe *afe;
- unsigned long flags;
port = container_of(ref, struct q6afe_port, refcount);
afe = port->afe;
- spin_lock_irqsave(&afe->port_list_lock, flags);
- list_del(&port->node);
- spin_unlock_irqrestore(&afe->port_list_lock, flags);
+ scoped_guard(spinlock_irqsave, &afe->port_list_lock)
+ list_del(&port->node);
kfree(port->scfg);
kfree(port);
}
@@ -958,11 +956,11 @@ static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token)
return ret;
}
-static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *data)
{
struct q6afe *afe = dev_get_drvdata(&adev->dev);
- struct aprv2_ibasic_rsp_result_t *res;
- struct apr_hdr *hdr = &data->hdr;
+ const struct aprv2_ibasic_rsp_result_t *res;
+ const struct apr_hdr *hdr = &data->hdr;
struct q6afe_port *port;
if (!data->payload_size)
@@ -1077,6 +1075,7 @@ static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port,
struct apr_pkt *pkt;
int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize;
void *pl;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1128,6 +1127,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data,
u16 port_id = port->id;
int ret, pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize;
void *pl;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1277,7 +1277,6 @@ int q6afe_port_stop(struct q6afe_port *port)
int port_id = port->id;
int ret = 0;
int index, pkt_size;
- void *p __free(kfree) = NULL;
index = port->token;
if (index < 0 || index >= AFE_PORT_MAX) {
@@ -1286,7 +1285,7 @@ int q6afe_port_stop(struct q6afe_port *port)
}
pkt_size = APR_HDR_SIZE + sizeof(*stop);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1667,7 +1666,6 @@ int q6afe_port_start(struct q6afe_port *port)
int ret, param_id = port->cfg_type;
struct apr_pkt *pkt;
int pkt_size;
- void *p __free(kfree) = NULL;
ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id,
AFE_MODULE_AUDIO_DEV_INTERFACE,
@@ -1690,7 +1688,7 @@ int q6afe_port_start(struct q6afe_port *port)
}
pkt_size = APR_HDR_SIZE + sizeof(*start);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1807,8 +1805,8 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
port->cfg_type = cfg_type;
kref_init(&port->refcount);
- guard(spinlock_irqsave)(&afe->port_list_lock);
- list_add_tail(&port->node, &afe->port_list);
+ scoped_guard(spinlock_irqsave, &afe->port_list_lock)
+ list_add_tail(&port->node, &afe->port_list);
return port;
@@ -1834,6 +1832,7 @@ int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
struct apr_pkt *pkt;
int ret = 0;
int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1868,6 +1867,7 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
struct apr_pkt *pkt;
int ret = 0;
int pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 94cc6376a367..1d5edf285793 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -100,6 +100,7 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op
struct audioreach_sub_graph *sg;
struct q6apm *apm = graph->apm;
int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -259,7 +260,6 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
{
struct apm_cmd_shared_mem_unmap_regions *cmd;
struct audioreach_graph_data *data;
- struct gpr_pkt *pkt __free(kfree) = NULL;
int rc;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
@@ -270,8 +270,9 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
if (!data->mem_map_handle)
return 0;
- pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
- graph->port->id);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS,
+ dir, graph->port->id);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -409,6 +410,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
struct audio_buffer *ab;
int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer),
DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
@@ -446,6 +448,7 @@ int q6apm_read(struct q6apm_graph *graph)
struct audioreach_graph_data *port;
struct audio_buffer *ab;
int iid = q6apm_graph_get_tx_shmem_module_iid(graph);
+
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer),
DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
graph->tx_data.dsp_buf, graph->port->id, iid);
@@ -487,14 +490,14 @@ int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir)
}
EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer);
-static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
+static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
struct apm_cmd_rsp_shared_mem_map_regions *rsp;
- struct gpr_ibasic_rsp_result_t *result;
+ const struct gpr_ibasic_rsp_result_t *result;
struct q6apm_graph *graph = priv;
- struct gpr_hdr *hdr = &data->hdr;
+ const struct gpr_hdr *hdr = &data->hdr;
struct device *dev = graph->dev;
uint32_t client_event;
phys_addr_t phys;
@@ -761,13 +764,13 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui
}
-static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
gpr_device_t *gdev = priv;
struct q6apm *apm = dev_get_drvdata(&gdev->dev);
struct device *dev = &gdev->dev;
struct gpr_ibasic_rsp_result_t *result;
- struct gpr_hdr *hdr = &data->hdr;
+ const struct gpr_hdr *hdr = &data->hdr;
result = data->payload;
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index e7295b7b2461..1bb295407c60 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -335,7 +335,6 @@ static int __q6asm_memory_unmap(struct audio_client *ac,
struct q6asm *a = dev_get_drvdata(ac->dev->parent);
struct apr_pkt *pkt;
int rc, pkt_size;
- void *p __free(kfree) = NULL;
if (ac->port[dir].mem_map_handle == 0) {
dev_err(ac->dev, "invalid mem handle\n");
@@ -343,7 +342,7 @@ static int __q6asm_memory_unmap(struct audio_client *ac,
}
pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -428,7 +427,6 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
struct audio_port_data *port = NULL;
struct audio_buffer *ab = NULL;
struct apr_pkt *pkt;
- void *p __free(kfree) = NULL;
unsigned long flags;
uint32_t num_regions, buf_sz;
int i, pkt_size;
@@ -447,7 +445,7 @@ static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
pkt_size = APR_HDR_SIZE + sizeof(*cmd) +
(sizeof(*mregions) * num_regions);
- p = kzalloc(pkt_size, GFP_KERNEL);
+ void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -599,12 +597,12 @@ int q6asm_get_hw_pointer(struct audio_client *ac, unsigned int dir)
EXPORT_SYMBOL_GPL(q6asm_get_hw_pointer);
static int32_t q6asm_stream_callback(struct apr_device *adev,
- struct apr_resp_pkt *data,
+ const struct apr_resp_pkt *data,
int session_id)
{
struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
- struct aprv2_ibasic_rsp_result_t *result;
- struct apr_hdr *hdr = &data->hdr;
+ const struct aprv2_ibasic_rsp_result_t *result;
+ const struct apr_hdr *hdr = &data->hdr;
struct audio_port_data *port;
struct audio_client *ac;
uint32_t client_event = 0;
@@ -638,7 +636,6 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE;
break;
case ASM_STREAM_CMD_OPEN_WRITE_V3:
- case ASM_DATA_CMD_WRITE_V2:
case ASM_STREAM_CMD_OPEN_READ_V3:
case ASM_STREAM_CMD_OPEN_READWRITE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
@@ -657,8 +654,9 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
break;
case ASM_DATA_CMD_EOS:
case ASM_DATA_CMD_READ_V2:
+ case ASM_DATA_CMD_WRITE_V2:
/* response as result of close stream */
- break;
+ goto done;
default:
dev_err(ac->dev, "command[0x%x] not expecting rsp\n",
result->opcode);
@@ -744,13 +742,13 @@ done:
}
static int q6asm_srvc_callback(struct apr_device *adev,
- struct apr_resp_pkt *data)
+ const struct apr_resp_pkt *data)
{
struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
struct aprv2_ibasic_rsp_result_t *result;
struct audio_port_data *port;
struct audio_client *ac = NULL;
- struct apr_hdr *hdr = &data->hdr;
+ const struct apr_hdr *hdr = &data->hdr;
struct q6asm *a;
uint32_t sid = 0;
uint32_t dir = 0;
@@ -930,6 +928,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t stream_id,
struct asm_stream_cmd_open_write_v3 *open;
struct apr_pkt *pkt;
int rc, pkt_size = APR_HDR_SIZE + sizeof(*open);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1007,6 +1006,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t stream_id,
struct asm_session_cmd_run_v2 *run;
struct apr_pkt *pkt;
int rc, pkt_size = APR_HDR_SIZE + sizeof(*run);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1089,6 +1089,7 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
struct apr_pkt *pkt;
u8 *channel_mapping;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1127,6 +1128,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac,
struct asm_flac_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1158,6 +1160,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac,
struct asm_wmastdv9_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1190,6 +1193,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac,
struct asm_wmaprov10_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1223,6 +1227,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac,
struct asm_alac_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1259,6 +1264,7 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac,
struct asm_ape_fmt_blk_v2 *fmt;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*fmt);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1293,6 +1299,7 @@ static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_
uint32_t *samples;
struct apr_pkt *pkt;
int rc, pkt_size = APR_HDR_SIZE + sizeof(uint32_t);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1351,6 +1358,7 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
u8 *channel_mapping;
u32 frames_per_buf = 0;
int pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1397,6 +1405,7 @@ int q6asm_read(struct audio_client *ac, uint32_t stream_id)
unsigned long flags;
int pkt_size = APR_HDR_SIZE + sizeof(*read);
int rc = 0;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
@@ -1439,6 +1448,7 @@ static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id,
struct asm_stream_cmd_open_read_v3 *open;
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*open);
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -1509,6 +1519,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len,
struct apr_pkt *pkt;
int pkt_size = APR_HDR_SIZE + sizeof(*write);
int rc = 0;
+
void *p __free(kfree) = kzalloc(pkt_size, GFP_ATOMIC);
if (!p)
return -ENOMEM;
diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c
index 49cfb32cd209..f4939302b88a 100644
--- a/sound/soc/qcom/qdsp6/q6core.c
+++ b/sound/soc/qcom/qdsp6/q6core.c
@@ -67,11 +67,11 @@ struct q6core {
static struct q6core *g_core;
-static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
+static int q6core_callback(struct apr_device *adev, const struct apr_resp_pkt *data)
{
struct q6core *core = dev_get_drvdata(&adev->dev);
- struct aprv2_ibasic_rsp_result_t *result;
- struct apr_hdr *hdr = &data->hdr;
+ const struct aprv2_ibasic_rsp_result_t *result;
+ const struct apr_hdr *hdr = &data->hdr;
result = data->payload;
switch (hdr->opcode) {
diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c
index 0b8fad0bc832..04892fb4423f 100644
--- a/sound/soc/qcom/qdsp6/q6prm.c
+++ b/sound/soc/qcom/qdsp6/q6prm.c
@@ -62,7 +62,6 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool
struct prm_cmd_request_hw_core *req;
gpr_device_t *gdev = prm->gdev;
uint32_t opcode, rsp_opcode;
- struct gpr_pkt *pkt __free(kfree) = NULL;
if (enable) {
opcode = PRM_CMD_REQUEST_HW_RSC;
@@ -72,7 +71,8 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool
rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;
}
- pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -111,10 +111,10 @@ static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_att
struct apm_module_param_data *param_data;
struct prm_cmd_request_rsc *req;
gpr_device_t *gdev = prm->gdev;
- struct gpr_pkt *pkt __free(kfree) = NULL;
- pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,
- GPR_PRM_MODULE_IID);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0,
+ gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -143,10 +143,10 @@ static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_att
struct apm_module_param_data *param_data;
struct prm_cmd_release_rsc *rel;
gpr_device_t *gdev = prm->gdev;
- struct gpr_pkt *pkt __free(kfree) = NULL;
- pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,
- GPR_PRM_MODULE_IID);
+ struct gpr_pkt *pkt __free(kfree) =
+ audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0,
+ gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@@ -175,12 +175,12 @@ int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_
}
EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
-static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
+static int prm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
gpr_device_t *gdev = priv;
struct q6prm *prm = dev_get_drvdata(&gdev->dev);
- struct gpr_ibasic_rsp_result_t *result;
- struct gpr_hdr *hdr = &data->hdr;
+ const struct gpr_ibasic_rsp_result_t *result;
+ const struct gpr_hdr *hdr = &data->hdr;
switch (hdr->opcode) {
case PRM_CMD_RSP_REQUEST_HW_RSC:
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index 5ce6edf3305e..2e71eaa90441 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -206,15 +206,15 @@ static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm,
return mod;
}
-static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(
- struct snd_soc_tplg_private *private)
+static const struct snd_soc_tplg_vendor_array *
+audioreach_get_sg_array(const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_array *sg_array = NULL;
+ const struct snd_soc_tplg_vendor_array *sg_array = NULL;
bool found = false;
int sz;
for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
- struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ const struct snd_soc_tplg_vendor_value_elem *sg_elem;
int tkn_count = 0;
sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
@@ -239,15 +239,15 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(
return NULL;
}
-static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(
- struct snd_soc_tplg_private *private)
+static const struct snd_soc_tplg_vendor_array *
+audioreach_get_cont_array(const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_array *cont_array = NULL;
+ const struct snd_soc_tplg_vendor_array *cont_array = NULL;
bool found = false;
int sz;
for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
- struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ const struct snd_soc_tplg_vendor_value_elem *cont_elem;
int tkn_count = 0;
cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
@@ -272,15 +272,15 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(
return NULL;
}
-static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
- struct snd_soc_tplg_private *private)
+static const struct snd_soc_tplg_vendor_array *
+audioreach_get_module_array(const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_array *mod_array = NULL;
+ const struct snd_soc_tplg_vendor_array *mod_array = NULL;
bool found = false;
int sz = 0;
for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
@@ -305,13 +305,13 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
return NULL;
}
-static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
- struct snd_soc_tplg_private *private)
+static struct audioreach_module_priv_data *
+audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private)
{
int sz;
for (sz = 0; sz < le32_to_cpu(private->size); ) {
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_array *mod_array;
mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
@@ -334,10 +334,10 @@ static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
}
static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,
- struct snd_soc_tplg_private *private)
+ const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_value_elem *sg_elem;
- struct snd_soc_tplg_vendor_array *sg_array;
+ const struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ const struct snd_soc_tplg_vendor_array *sg_array;
struct audioreach_graph_info *info = NULL;
int graph_id, sub_graph_id, tkn_count = 0;
struct audioreach_sub_graph *sg;
@@ -392,10 +392,10 @@ static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm
static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm,
struct audioreach_sub_graph *sg,
- struct snd_soc_tplg_private *private)
+ const struct snd_soc_tplg_private *private)
{
- struct snd_soc_tplg_vendor_value_elem *cont_elem;
- struct snd_soc_tplg_vendor_array *cont_array;
+ const struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ const struct snd_soc_tplg_vendor_array *cont_array;
struct audioreach_container *cont;
int container_id, tkn_count = 0;
bool found = false;
@@ -437,7 +437,7 @@ static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *a
static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm,
struct audioreach_container *cont,
- struct snd_soc_tplg_private *private,
+ const struct snd_soc_tplg_private *private,
struct snd_soc_dapm_widget *w)
{
uint32_t max_ip_port = 0, max_op_port = 0;
@@ -447,8 +447,8 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
uint32_t src_mod_inst_id = 0;
int module_id = 0, instance_id = 0, tkn_count = 0;
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_array *mod_array;
struct audioreach_module *mod = NULL;
uint32_t token;
bool found;
@@ -590,7 +590,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
static int audioreach_widget_load_module_common(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
struct q6apm *apm = dev_get_drvdata(component->dev);
struct audioreach_container *cont;
@@ -620,10 +620,10 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon
static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_array *mod_array;
struct audioreach_module *mod;
struct snd_soc_dobj *dobj;
int tkn_count = 0;
@@ -660,9 +660,9 @@ static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *componen
}
static int audioreach_widget_log_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -690,9 +690,9 @@ static int audioreach_widget_log_module_load(struct audioreach_module *mod,
}
static int audioreach_widget_dma_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -719,9 +719,9 @@ static int audioreach_widget_dma_module_load(struct audioreach_module *mod,
}
static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -754,9 +754,9 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
}
static int audioreach_widget_dp_module_load(struct audioreach_module *mod,
- struct snd_soc_tplg_vendor_array *mod_array)
+ const struct snd_soc_tplg_vendor_array *mod_array)
{
- struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
int tkn_count = 0;
mod_elem = mod_array->value;
@@ -778,9 +778,9 @@ static int audioreach_widget_dp_module_load(struct audioreach_module *mod,
static int audioreach_widget_load_buffer(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
- struct snd_soc_tplg_vendor_array *mod_array;
+ const struct snd_soc_tplg_vendor_array *mod_array;
struct audioreach_module *mod;
struct snd_soc_dobj *dobj;
int ret;
@@ -818,10 +818,10 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component,
static int audioreach_widget_load_mixer(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
- struct snd_soc_tplg_vendor_value_elem *w_elem;
- struct snd_soc_tplg_vendor_array *w_array;
+ const struct snd_soc_tplg_vendor_value_elem *w_elem;
+ const struct snd_soc_tplg_vendor_array *w_array;
struct snd_ar_control *scontrol;
struct q6apm *data = dev_get_drvdata(component->dev);
struct snd_soc_dobj *dobj;
@@ -886,7 +886,7 @@ static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = {
static int audioreach_widget_load_pga(struct snd_soc_component *component,
int index, struct snd_soc_dapm_widget *w,
- struct snd_soc_tplg_dapm_widget *tplg_w)
+ const struct snd_soc_tplg_dapm_widget *tplg_w)
{
struct audioreach_module *mod;
struct snd_soc_dobj *dobj;
@@ -947,7 +947,7 @@ static int audioreach_widget_ready(struct snd_soc_component *component,
static int audioreach_widget_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
- struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+ const struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
struct q6apm *apm = dev_get_drvdata(scomp->dev);
struct audioreach_container *cont;
struct audioreach_module *mod;
@@ -1032,7 +1032,7 @@ static struct audioreach_module *audioreach_find_module(struct snd_soc_component
static int audioreach_route_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_route *route)
{
- struct audioreach_module *src_module, *sink_module;
+ const struct audioreach_module *src_module, *sink_module;
struct snd_ar_control *control;
struct snd_soc_dapm_widget *w;
int i;
@@ -1098,8 +1098,8 @@ static int audioreach_link_load(struct snd_soc_component *component, int index,
}
static void audioreach_connect_sub_graphs(struct q6apm *apm,
- struct snd_ar_control *m1,
- struct snd_ar_control *m2,
+ const struct snd_ar_control *m1,
+ const struct snd_ar_control *m2,
bool connect)
{
struct audioreach_graph_info *info;
@@ -1123,10 +1123,10 @@ static void audioreach_connect_sub_graphs(struct q6apm *apm,
}
static bool audioreach_is_vmixer_connected(struct q6apm *apm,
- struct snd_ar_control *m1,
- struct snd_ar_control *m2)
+ const struct snd_ar_control *m1,
+ const struct snd_ar_control *m2)
{
- struct audioreach_graph_info *info;
+ const struct audioreach_graph_info *info;
mutex_lock(&apm->lock);
info = idr_find(&apm->graph_info_idr, m2->graph_id);
@@ -1144,10 +1144,10 @@ static int audioreach_get_audio_mixer(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
- struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
+ const struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
- struct snd_ar_control *dapm_scontrol = dw->dobj.private;
- struct snd_ar_control *scontrol = mc->dobj.private;
+ const struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ const struct snd_ar_control *scontrol = mc->dobj.private;
struct q6apm *data = dev_get_drvdata(c->dev);
bool connected;
@@ -1167,8 +1167,8 @@ static int audioreach_put_audio_mixer(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_to_widget(kcontrol);
struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
- struct snd_ar_control *dapm_scontrol = dw->dobj.private;
- struct snd_ar_control *scontrol = mc->dobj.private;
+ const struct snd_ar_control *dapm_scontrol = dw->dobj.private;
+ const struct snd_ar_control *scontrol = mc->dobj.private;
struct q6apm *data = dev_get_drvdata(c->dev);
if (ucontrol->value.integer.value[0]) {
@@ -1206,14 +1206,14 @@ static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,
static int audioreach_control_load_mix(struct snd_soc_component *scomp,
struct snd_ar_control *scontrol,
struct snd_kcontrol_new *kc,
- struct snd_soc_tplg_ctl_hdr *hdr)
+ const struct snd_soc_tplg_ctl_hdr *hdr)
{
- struct snd_soc_tplg_vendor_value_elem *c_elem;
- struct snd_soc_tplg_vendor_array *c_array;
- struct snd_soc_tplg_mixer_control *mc;
+ const struct snd_soc_tplg_vendor_value_elem *c_elem;
+ const struct snd_soc_tplg_vendor_array *c_array;
+ const struct snd_soc_tplg_mixer_control *mc;
int tkn_count = 0;
- mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
+ mc = container_of_const(hdr, struct snd_soc_tplg_mixer_control, hdr);
c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data;
c_elem = c_array->value;
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
index f4dc2f68dead..39aa865bdca3 100644
--- a/sound/soc/renesas/rz-ssi.c
+++ b/sound/soc/renesas/rz-ssi.c
@@ -38,6 +38,7 @@
#define SSICR_MST BIT(14)
#define SSICR_BCKP BIT(13)
#define SSICR_LRCKP BIT(12)
+#define SSICR_PDTA BIT(9)
#define SSICR_CKDV(x) (((x) & 0xf) << 4)
#define SSICR_TEN BIT(1)
#define SSICR_REN BIT(0)
@@ -74,7 +75,8 @@
#define PREALLOC_BUFFER_MAX (SZ_32K)
#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */
-#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE
+#define SSI_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
#define SSI_CHAN_MIN 2
#define SSI_CHAN_MAX 2
#define SSI_FIFO_DEPTH 32
@@ -178,12 +180,7 @@ static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream)
static inline struct rz_ssi_stream *
rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream)
{
- struct rz_ssi_stream *stream = &ssi->playback;
-
- if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
- stream = &ssi->capture;
-
- return stream;
+ return (ssi->playback.substream == substream) ? &ssi->playback : &ssi->capture;
}
static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi)
@@ -294,11 +291,27 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
}
/*
- * DWL: Data Word Length = 16 bits
+ * DWL: Data Word Length = {16, 24, 32} bits
* SWL: System Word Length = 32 bits
*/
ssicr |= SSICR_CKDV(clk_ckdv);
- ssicr |= SSICR_DWL(1) | SSICR_SWL(3);
+ switch (ssi->hw_params_cache.sample_width) {
+ case 16:
+ ssicr |= SSICR_DWL(1);
+ break;
+ case 24:
+ ssicr |= SSICR_DWL(5) | SSICR_PDTA;
+ break;
+ case 32:
+ ssicr |= SSICR_DWL(6);
+ break;
+ default:
+ dev_err(ssi->dev, "Not support %u data width",
+ ssi->hw_params_cache.sample_width);
+ return -EINVAL;
+ }
+
+ ssicr |= SSICR_SWL(3);
rz_ssi_reg_writel(ssi, SSICR, ssicr);
rz_ssi_reg_writel(ssi, SSIFCR, SSIFCR_AUCKE | SSIFCR_FIFO_RST);
@@ -455,7 +468,6 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
{
struct snd_pcm_substream *substream = strm->substream;
struct snd_pcm_runtime *runtime;
- u16 *buf;
int fifo_samples;
int frames_left;
int samples;
@@ -490,12 +502,23 @@ static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
break;
/* calculate new buffer index */
- buf = (u16 *)runtime->dma_area;
- buf += strm->buffer_pos * runtime->channels;
+ if (ssi->hw_params_cache.sample_width == 16) {
+ u16 *buf;
- /* Note, only supports 16-bit samples */
- for (i = 0; i < samples; i++)
- *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16);
+ buf = (u16 *)runtime->dma_area;
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16);
+ } else {
+ u32 *buf;
+
+ buf = (u32 *)runtime->dma_area;
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ *buf++ = rz_ssi_reg_readl(ssi, SSIFRDR);
+ }
rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0);
rz_ssi_pointer_update(strm, samples / runtime->channels);
@@ -513,7 +536,6 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
int frames_left;
int i;
u32 ssifsr;
- u16 *buf;
if (!rz_ssi_stream_is_valid(ssi, strm))
return -EINVAL;
@@ -542,12 +564,23 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
return 0;
/* calculate new buffer index */
- buf = (u16 *)(runtime->dma_area);
- buf += strm->buffer_pos * runtime->channels;
+ if (ssi->hw_params_cache.sample_width == 16) {
+ u16 *buf;
- /* Note, only supports 16-bit samples */
- for (i = 0; i < samples; i++)
- rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16));
+ buf = (u16 *)(runtime->dma_area);
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16));
+ } else {
+ u32 *buf;
+
+ buf = (u32 *)(runtime->dma_area);
+ buf += strm->buffer_pos * runtime->channels;
+
+ for (i = 0; i < samples; i++)
+ rz_ssi_reg_writel(ssi, SSIFTDR, *buf++);
+ }
rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_TDE, 0);
rz_ssi_pointer_update(strm, samples / runtime->channels);
@@ -571,7 +604,8 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
return IRQ_HANDLED; /* Left over TX/RX interrupt */
if (irq == ssi->irq_int) { /* error or idle */
- bool is_stopped = false;
+ bool is_stopped = !!(ssisr & (SSISR_RUIRQ | SSISR_ROIRQ |
+ SSISR_TUIRQ | SSISR_TOIRQ));
int i, count;
if (rz_ssi_is_dma_enabled(ssi))
@@ -579,9 +613,6 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
else
count = 1;
- if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ))
- is_stopped = true;
-
if (ssi->capture.substream && is_stopped) {
if (ssisr & SSISR_RUIRQ)
strm_capture->uerr_num++;
@@ -658,8 +689,13 @@ static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi,
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
cfg.dst_addr = ssi->phys + SSIFTDR;
cfg.src_addr = ssi->phys + SSIFRDR;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ if (ssi->hw_params_cache.sample_width == 16) {
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ } else {
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
return dmaengine_slave_config(dma_ch, &cfg);
}
@@ -774,14 +810,6 @@ static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev)
if (!rz_ssi_is_dma_enabled(ssi))
goto no_dma;
- if (ssi->playback.dma_ch &&
- (rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, true) < 0))
- goto no_dma;
-
- if (ssi->capture.dma_ch &&
- (rz_ssi_dma_slave_config(ssi, ssi->capture.dma_ch, false) < 0))
- goto no_dma;
-
return 0;
no_dma:
@@ -829,28 +857,31 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
if (cmd == SNDRV_PCM_TRIGGER_START)
rz_ssi_stream_init(strm, substream);
- if (ssi->dma_rt) {
- bool is_playback;
+ if (rz_ssi_is_dma_enabled(ssi)) {
+ bool is_playback = rz_ssi_stream_is_play(substream);
+
+ if (ssi->dma_rt)
+ ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
+ is_playback);
+ else
+ ret = rz_ssi_dma_slave_config(ssi, strm->dma_ch,
+ is_playback);
- is_playback = rz_ssi_stream_is_play(substream);
- ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
- is_playback);
/* Fallback to pio */
if (ret < 0) {
ssi->playback.transfer = rz_ssi_pio_send;
ssi->capture.transfer = rz_ssi_pio_recv;
rz_ssi_release_dma_channels(ssi);
+ } else {
+ /* For DMA, queue up multiple DMA descriptors */
+ num_transfer = 4;
}
}
- /* For DMA, queue up multiple DMA descriptors */
- if (rz_ssi_is_dma_enabled(ssi))
- num_transfer = 4;
-
for (i = 0; i < num_transfer; i++) {
ret = strm->transfer(ssi, strm);
if (ret)
- goto done;
+ return ret;
}
ret = rz_ssi_start(ssi, strm);
@@ -866,7 +897,6 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
break;
}
-done:
return ret;
}
@@ -982,7 +1012,7 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
unsigned int rate = params_rate(params);
int ret;
- if (sample_bits != 16) {
+ if (!(sample_bits == 16 || sample_bits == 24 || sample_bits == 32)) {
dev_err(ssi->dev, "Unsupported sample width: %d\n",
sample_bits);
return -EINVAL;
@@ -1119,19 +1149,16 @@ static int rz_ssi_probe(struct platform_device *pdev)
audio_clk = devm_clk_get(dev, "audio_clk1");
if (IS_ERR(audio_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
- "no audio clk1");
+ return dev_err_probe(dev, PTR_ERR(audio_clk), "no audio clk1");
ssi->audio_clk_1 = clk_get_rate(audio_clk);
audio_clk = devm_clk_get(dev, "audio_clk2");
if (IS_ERR(audio_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
- "no audio clk2");
+ return dev_err_probe(dev, PTR_ERR(audio_clk), "no audio clk2");
ssi->audio_clk_2 = clk_get_rate(audio_clk);
if (!(ssi->audio_clk_1 || ssi->audio_clk_2))
- return dev_err_probe(&pdev->dev, -EINVAL,
- "no audio clk1 or audio clk2");
+ return dev_err_probe(dev, -EINVAL, "no audio clk1 or audio clk2");
ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2;
@@ -1160,7 +1187,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
goto err_release_dma_chs;
}
- ret = devm_request_irq(dev, ssi->irq_int, &rz_ssi_interrupt,
+ ret = devm_request_irq(dev, ssi->irq_int, rz_ssi_interrupt,
0, dev_name(dev), ssi);
if (ret < 0) {
dev_err_probe(dev, ret, "irq request error (int_req)\n");
@@ -1177,7 +1204,7 @@ static int rz_ssi_probe(struct platform_device *pdev)
return ssi->irq_rt;
ret = devm_request_irq(dev, ssi->irq_rt,
- &rz_ssi_interrupt, 0,
+ rz_ssi_interrupt, 0,
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(dev, ret,
@@ -1190,14 +1217,14 @@ static int rz_ssi_probe(struct platform_device *pdev)
return ssi->irq_rx;
ret = devm_request_irq(dev, ssi->irq_tx,
- &rz_ssi_interrupt, 0,
+ rz_ssi_interrupt, 0,
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(dev, ret,
"irq request error (dma_tx)\n");
ret = devm_request_irq(dev, ssi->irq_rx,
- &rz_ssi_interrupt, 0,
+ rz_ssi_interrupt, 0,
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(dev, ret,
@@ -1247,7 +1274,7 @@ static void rz_ssi_remove(struct platform_device *pdev)
static const struct of_device_id rz_ssi_of_match[] = {
{ .compatible = "renesas,rz-ssi", },
- {/* Sentinel */},
+ { /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rz_ssi_of_match);
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index bd210fafe9fe..391ce2225fde 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -41,6 +41,7 @@ config SND_SOC_ROCKCHIP_SAI
config SND_SOC_ROCKCHIP_SPDIF
tristate "Rockchip SPDIF Device Driver"
+ select SND_PCM_IEC958
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF driver for
diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c
index ebdf0056065b..1bf614dbdf4d 100644
--- a/sound/soc/rockchip/rockchip_sai.c
+++ b/sound/soc/rockchip/rockchip_sai.c
@@ -1487,8 +1487,9 @@ static int rockchip_sai_probe(struct platform_device *pdev)
return 0;
err_runtime_suspend:
- /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */
- if (pm_runtime_put(&pdev->dev))
+ if (IS_ENABLED(CONFIG_PM))
+ pm_runtime_put(&pdev->dev);
+ else
rockchip_sai_runtime_suspend(&pdev->dev);
return ret;
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index d365168934dc..581624f2682e 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -5,10 +5,11 @@
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun <jay.xu@rock-chips.com>
- * Copyright (c) 2015 Collabora Ltd.
+ * Copyright (c) 2015-2026 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
*/
+#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/clk.h>
@@ -16,6 +17,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
#include <sound/dmaengine_pcm.h>
#include "rockchip_spdif.h"
@@ -27,7 +29,25 @@ enum rk_spdif_type {
RK_SPDIF_RK3366,
};
-#define RK3288_GRF_SOC_CON2 0x24c
+/*
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * CS0: | Mode | d | c | b | a |
+ * CS1: | Category Code |
+ * CS2: | Channel Number | Source Number |
+ * CS3: | Clock Accuracy | Sample Freq |
+ * CS4: | Ori Sample Freq | Word Length |
+ * CS5: | | CGMS-A |
+ * CS6~CS23: Reserved
+ *
+ * a: use of channel status block
+ * b: linear PCM identification: 0 for lpcm, 1 for nlpcm
+ * c: copyright information
+ * d: additional format information
+ */
+#define CS_BYTE 6
+#define CS_FRAME(c) ((c) << 16 | (c))
+
+#define RK3288_GRF_SOC_CON2 0x24c
struct rk_spdif_dev {
struct device *dev;
@@ -40,29 +60,6 @@ struct rk_spdif_dev {
struct regmap *regmap;
};
-static const struct of_device_id rk_spdif_match[] __maybe_unused = {
- { .compatible = "rockchip,rk3066-spdif",
- .data = (void *)RK_SPDIF_RK3066 },
- { .compatible = "rockchip,rk3188-spdif",
- .data = (void *)RK_SPDIF_RK3188 },
- { .compatible = "rockchip,rk3228-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3288-spdif",
- .data = (void *)RK_SPDIF_RK3288 },
- { .compatible = "rockchip,rk3328-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3366-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3368-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3399-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- { .compatible = "rockchip,rk3568-spdif",
- .data = (void *)RK_SPDIF_RK3366 },
- {},
-};
-MODULE_DEVICE_TABLE(of, rk_spdif_match);
-
static int rk_spdif_runtime_suspend(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
@@ -109,39 +106,63 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int mclk_rate = clk_get_rate(spdif->mclk);
unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
- int srate, mclk;
- int ret;
+ int bmc, div, ret, i;
+ u16 *fc;
+ u8 cs[CS_BYTE];
+
+ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, sizeof(cs));
+ if (ret < 0)
+ return ret;
- srate = params_rate(params);
- mclk = srate * 128;
+ fc = (u16 *)cs;
+ for (i = 0; i < CS_BYTE / 2; i++)
+ regmap_write(spdif->regmap, SPDIF_CHNSRn(i), CS_FRAME(fc[i]));
+
+ regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CSE_MASK,
+ SPDIF_CFGR_CSE_EN);
+
+ /* bmc = 128fs */
+ bmc = 128 * params_rate(params);
+ div = DIV_ROUND_CLOSEST(mclk_rate, bmc);
+ val |= SPDIF_CFGR_CLK_DIV(div);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val |= SPDIF_CFGR_VDW_16;
+ val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= SPDIF_CFGR_VDW_20;
+ val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= SPDIF_CFGR_VDW_24;
+ val |= SPDIF_CFGR_ADJ_RIGHT_J;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val |= SPDIF_CFGR_VDW_24;
+ val |= SPDIF_CFGR_ADJ_LEFT_J;
break;
default:
return -EINVAL;
}
- /* Set clock and calculate divider */
- ret = clk_set_rate(spdif->mclk, mclk);
- if (ret != 0) {
- dev_err(spdif->dev, "Failed to set module clock rate: %d\n",
- ret);
- return ret;
- }
+ /*
+ * clear MCLK domain logic before setting Fmclk and Fsdo to ensure
+ * that switching between S16_LE and S32_LE audio does not result
+ * in accidential channels swap.
+ */
+ regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLR_MASK,
+ SPDIF_CFGR_CLR_EN);
+ udelay(1);
ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
SPDIF_CFGR_CLK_DIV_MASK |
- SPDIF_CFGR_HALFWORD_ENABLE |
- SDPIF_CFGR_VDW_MASK, val);
+ SPDIF_CFGR_HALFWORD_MASK |
+ SDPIF_CFGR_VDW_MASK |
+ SPDIF_CFGR_ADJ_MASK, val);
return ret;
}
@@ -157,7 +178,7 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDE_MASK |
SPDIF_DMACR_TDL_MASK,
SPDIF_DMACR_TDE_ENABLE |
SPDIF_DMACR_TDL(16));
@@ -166,21 +187,21 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
- SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_MASK,
SPDIF_XFER_TXS_START);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE,
+ SPDIF_DMACR_TDE_MASK,
SPDIF_DMACR_TDE_DISABLE);
if (ret != 0)
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
- SPDIF_XFER_TXS_START,
+ SPDIF_XFER_TXS_MASK,
SPDIF_XFER_TXS_STOP);
break;
default:
@@ -200,7 +221,24 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static int rk_spdif_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ if (!freq)
+ return 0;
+
+ ret = clk_set_rate(spdif->mclk, freq);
+ if (ret)
+ dev_err(spdif->dev, "Failed to set mclk: %d\n", ret);
+
+ return ret;
+}
+
static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+ .set_sysclk = rk_spdif_set_sysclk,
.probe = rk_spdif_dai_probe,
.hw_params = rk_spdif_hw_params,
.trigger = rk_spdif_trigger,
@@ -211,14 +249,11 @@ static struct snd_soc_dai_driver rk_spdif_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = (SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000),
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
- SNDRV_PCM_FMTBIT_S24_LE),
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
},
.ops = &rk_spdif_dai_ops,
};
@@ -236,6 +271,9 @@ static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
case SPDIF_INTCR:
case SPDIF_XFER:
case SPDIF_SMPDR:
+ case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11):
+ case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11):
+ case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11):
return true;
default:
return false;
@@ -251,6 +289,9 @@ static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
case SPDIF_INTSR:
case SPDIF_XFER:
case SPDIF_SMPDR:
+ case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11):
+ case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11):
+ case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11):
return true;
default:
return false;
@@ -273,32 +314,38 @@ static const struct regmap_config rk_spdif_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = SPDIF_SMPDR,
+ .max_register = SPDIF_VERSION,
.writeable_reg = rk_spdif_wr_reg,
.readable_reg = rk_spdif_rd_reg,
.volatile_reg = rk_spdif_volatile_reg,
.cache_type = REGCACHE_FLAT,
};
+static void rk_spdif_suspend(void *data)
+{
+ struct device *dev = data;
+
+ if (!pm_runtime_status_suspended(dev))
+ rk_spdif_runtime_suspend(dev);
+}
+
static int rk_spdif_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ enum rk_spdif_type spdif_type;
struct rk_spdif_dev *spdif;
- const struct of_device_id *match;
struct resource *res;
void __iomem *regs;
int ret;
- match = of_match_node(rk_spdif_match, np);
- if (match->data == (void *)RK_SPDIF_RK3288) {
+ spdif_type = (uintptr_t) device_get_match_data(&pdev->dev);
+ if (spdif_type == RK_SPDIF_RK3288) {
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
- if (IS_ERR(grf)) {
- dev_err(&pdev->dev,
+ if (IS_ERR(grf))
+ return dev_err_probe(&pdev->dev, PTR_ERR(grf),
"rockchip_spdif missing 'rockchip,grf'\n");
- return PTR_ERR(grf);
- }
/* Select the 8 channel SPDIF solution on RK3288 as
* the 2 channel one does not appear to work
@@ -334,55 +381,63 @@ static int rk_spdif_probe(struct platform_device *pdev)
spdif->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, spdif);
- pm_runtime_enable(&pdev->dev);
+ ret = devm_add_action_or_reset(&pdev->dev, rk_spdif_suspend, &pdev->dev);
+ if (ret)
+ return ret;
+
+ devm_pm_runtime_enable(&pdev->dev);
+
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rk_spdif_runtime_resume(&pdev->dev);
if (ret)
- goto err_pm_runtime;
+ return ret;
}
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n");
+
ret = devm_snd_soc_register_component(&pdev->dev,
&rk_spdif_component,
&rk_spdif_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Could not register DAI\n");
- goto err_pm_suspend;
- }
-
- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
- if (ret) {
- dev_err(&pdev->dev, "Could not register PCM\n");
- goto err_pm_suspend;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n");
return 0;
-
-err_pm_suspend:
- if (!pm_runtime_status_suspended(&pdev->dev))
- rk_spdif_runtime_suspend(&pdev->dev);
-err_pm_runtime:
- pm_runtime_disable(&pdev->dev);
-
- return ret;
-}
-
-static void rk_spdif_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- rk_spdif_runtime_suspend(&pdev->dev);
}
static const struct dev_pm_ops rk_spdif_pm_ops = {
RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL)
};
+static const struct of_device_id rk_spdif_match[] = {
+ { .compatible = "rockchip,rk3066-spdif",
+ .data = (void *)RK_SPDIF_RK3066 },
+ { .compatible = "rockchip,rk3188-spdif",
+ .data = (void *)RK_SPDIF_RK3188 },
+ { .compatible = "rockchip,rk3228-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3288-spdif",
+ .data = (void *)RK_SPDIF_RK3288 },
+ { .compatible = "rockchip,rk3328-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3366-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3368-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3399-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3568-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk_spdif_match);
+
static struct platform_driver rk_spdif_driver = {
.probe = rk_spdif_probe,
- .remove = rk_spdif_remove,
.driver = {
.name = "rockchip-spdif",
- .of_match_table = of_match_ptr(rk_spdif_match),
+ .of_match_table = rk_spdif_match,
.pm = pm_ptr(&rk_spdif_pm_ops),
},
};
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
index d8be9aae5b19..ec33295e2512 100644
--- a/sound/soc/rockchip/rockchip_spdif.h
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -2,7 +2,7 @@
/*
* ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
*
- * Copyright (c) 2015 Collabora Ltd.
+ * Copyright (c) 2015-2026 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
*/
@@ -13,41 +13,50 @@
* CFGR
* transfer configuration register
*/
-#define SPDIF_CFGR_CLK_DIV_SHIFT (16)
-#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT)
-#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT)
+#define SPDIF_CFGR_CLK_DIV_MASK GENMASK(23, 16)
+#define SPDIF_CFGR_CLK_DIV(x) FIELD_PREP(SPDIF_CFGR_CLK_DIV_MASK, x-1)
-#define SPDIF_CFGR_HALFWORD_SHIFT 2
-#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT)
-#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT)
+#define SPDIF_CFGR_CLR_MASK BIT(7)
+#define SPDIF_CFGR_CLR_EN FIELD_PREP(SPDIF_CFGR_CLR_MASK, 1)
+#define SPDIF_CFGR_CLR_DIS FIELD_PREP(SPDIF_CFGR_CLR_MASK, 0)
-#define SPDIF_CFGR_VDW_SHIFT 0
-#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT)
-#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT)
+#define SPDIF_CFGR_CSE_MASK BIT(6)
+#define SPDIF_CFGR_CSE_EN FIELD_PREP(SPDIF_CFGR_CSE_MASK, 1)
+#define SPDIF_CFGR_CSE_DIS FIELD_PREP(SPDIF_CFGR_CSE_MASK, 0)
-#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
-#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
-#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
+#define SPDIF_CFGR_ADJ_MASK BIT(3)
+#define SPDIF_CFGR_ADJ_LEFT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 1)
+#define SPDIF_CFGR_ADJ_RIGHT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 0)
+
+#define SPDIF_CFGR_HALFWORD_MASK BIT(2)
+#define SPDIF_CFGR_HALFWORD_DISABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 0)
+#define SPDIF_CFGR_HALFWORD_ENABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 1)
+
+#define SDPIF_CFGR_VDW_MASK GENMASK(1, 0)
+#define SPDIF_CFGR_VDW(x) FIELD_PREP(SDPIF_CFGR_VDW_MASK, x)
+
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
/*
* DMACR
* DMA control register
*/
-#define SPDIF_DMACR_TDE_SHIFT 5
-#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT)
-#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT)
+#define SPDIF_DMACR_TDE_MASK BIT(5)
+#define SPDIF_DMACR_TDE_DISABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 0)
+#define SPDIF_DMACR_TDE_ENABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 1)
-#define SPDIF_DMACR_TDL_SHIFT 0
-#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT)
-#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK GENMASK(4, 0)
+#define SPDIF_DMACR_TDL(x) FIELD_PREP(SPDIF_DMACR_TDL_MASK, x)
/*
* XFER
* Transfer control register
*/
-#define SPDIF_XFER_TXS_SHIFT 0
-#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT)
-#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT)
+#define SPDIF_XFER_TXS_MASK BIT(0)
+#define SPDIF_XFER_TXS_STOP FIELD_PREP(SPDIF_XFER_TXS_MASK, 0)
+#define SPDIF_XFER_TXS_START FIELD_PREP(SPDIF_XFER_TXS_MASK, 1)
#define SPDIF_CFGR (0x0000)
#define SPDIF_SDBLR (0x0004)
@@ -56,5 +65,9 @@
#define SPDIF_INTSR (0x0010)
#define SPDIF_XFER (0x0018)
#define SPDIF_SMPDR (0x0020)
+#define SPDIF_VLDFRn(x) (0x0060 + (x) * 4)
+#define SPDIF_USRDRn(x) (0x0090 + (x) * 4)
+#define SPDIF_CHNSRn(x) (0x00c0 + (x) * 4)
+#define SPDIF_VERSION (0x01c0)
#endif /* _ROCKCHIP_SPDIF_H */
diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig
index fabb69a3450d..87ab2895096c 100644
--- a/sound/soc/sdca/Kconfig
+++ b/sound/soc/sdca/Kconfig
@@ -46,12 +46,14 @@ config SND_SOC_SDCA_CLASS
select SND_SOC_SDCA_FDL
select SND_SOC_SDCA_HID
select SND_SOC_SDCA_IRQ
+ select REGMAP_SOUNDWIRE
help
This option enables support for the SDCA Class driver which should
support any class compliant SDCA part.
config SND_SOC_SDCA_CLASS_FUNCTION
tristate
+ select REGMAP_SOUNDWIRE_MBQ
help
This option enables support for the SDCA Class Function drivers,
these implement the individual functions of the SDCA Class driver.
diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile
index f6b73275d964..b3b0f5d94c8d 100644
--- a/sound/soc/sdca/Makefile
+++ b/sound/soc/sdca/Makefile
@@ -3,7 +3,7 @@
snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_function_device.o \
sdca_regmap.o sdca_asoc.o sdca_ump.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o
-snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o sdca_jack.o
snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_FDL) += sdca_fdl.o
snd-soc-sdca-class-y := sdca_class.o
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
index 2d328bbb95b9..bb6e74e80a3e 100644
--- a/sound/soc/sdca/sdca_asoc.c
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -16,6 +16,7 @@
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/overflow.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/string_helpers.h>
@@ -115,6 +116,41 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
}
EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA");
+static int ge_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct device *dev = component->dev;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int reg = e->reg;
+ int ret;
+
+ reg &= ~SDW_SDCA_CTL_CSEL(0x3F);
+ reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume writing %s: %d\n",
+ kcontrol->id.name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_read(component, reg);
+ pm_runtime_put(dev);
+ if (ret < 0)
+ return ret;
+ else if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS)
+ return -EBUSY;
+
+ ret = snd_soc_enum_item_to_val(e, item[0]);
+ if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS)
+ return -EINVAL;
+
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
static int entity_early_parse_ge(struct device *dev,
struct sdca_function_data *function,
struct sdca_entity *entity)
@@ -191,7 +227,7 @@ static int entity_early_parse_ge(struct device *dev,
kctl->name = control_name;
kctl->info = snd_soc_info_enum_double;
kctl->get = snd_soc_dapm_get_enum_double;
- kctl->put = snd_soc_dapm_put_enum_double;
+ kctl->put = ge_put_enum_double;
kctl->private_value = (unsigned long)soc_enum;
entity->ge.kctl = kctl;
@@ -792,6 +828,48 @@ static int control_limit_kctl(struct device *dev,
return 0;
}
+static int volatile_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct device *dev = component->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume reading %s: %d\n",
+ kcontrol->id.name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static int volatile_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct device *dev = component->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume writing %s: %d\n",
+ kcontrol->id.name, ret);
+ return ret;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
static int populate_control(struct device *dev,
struct sdca_function_data *function,
struct sdca_entity *entity,
@@ -849,8 +927,13 @@ static int populate_control(struct device *dev,
(*kctl)->private_value = (unsigned long)mc;
(*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
(*kctl)->info = snd_soc_info_volsw;
- (*kctl)->get = snd_soc_get_volsw;
- (*kctl)->put = snd_soc_put_volsw;
+ if (control->is_volatile) {
+ (*kctl)->get = volatile_get_volsw;
+ (*kctl)->put = volatile_put_volsw;
+ } else {
+ (*kctl)->get = snd_soc_get_volsw;
+ (*kctl)->put = snd_soc_put_volsw;
+ }
if (readonly_control(control))
(*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READ;
@@ -1478,7 +1561,7 @@ static int set_usage(struct device *dev, struct regmap *regmap,
unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
- if ((!rate || rate == target_rate) && width == target_width) {
+ if ((!rate || rate == target_rate) && (!width || width == target_width)) {
unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i);
unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
entity->id, sel, 0);
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
index 349d32933ba8..918b638acb57 100644
--- a/sound/soc/sdca/sdca_class.c
+++ b/sound/soc/sdca/sdca_class.c
@@ -205,6 +205,7 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
drv->dev = dev;
drv->sdw = sdw;
mutex_init(&drv->regmap_lock);
+ mutex_init(&drv->init_lock);
dev_set_drvdata(drv->dev, drv);
@@ -238,6 +239,38 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
return 0;
}
+static int class_suspend(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ int ret;
+
+ disable_irq(drv->sdw->irq);
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to force suspend: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int class_resume(struct device *dev)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "failed to force resume: %d\n", ret);
+ return ret;
+ }
+
+ enable_irq(drv->sdw->irq);
+
+ return 0;
+}
+
static int class_runtime_suspend(struct device *dev)
{
struct sdca_class_drv *drv = dev_get_drvdata(dev);
@@ -278,6 +311,7 @@ err:
}
static const struct dev_pm_ops class_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(class_suspend, class_resume)
RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
};
diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h
index bb4c9dd12429..6f24ea2bbd38 100644
--- a/sound/soc/sdca/sdca_class.h
+++ b/sound/soc/sdca/sdca_class.h
@@ -28,6 +28,8 @@ struct sdca_class_drv {
struct sdca_interrupt_info *irq_info;
struct mutex regmap_lock;
+ /* Serialise function initialisations */
+ struct mutex init_lock;
struct work_struct boot_work;
struct completion device_attach;
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 0028482a1e75..98fd3fd1052b 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -8,6 +8,7 @@
*/
#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/pm.h>
@@ -19,6 +20,7 @@
#include <sound/sdca_fdl.h>
#include <sound/sdca_function.h>
#include <sound/sdca_interrupts.h>
+#include <sound/sdca_jack.h>
#include <sound/sdca_regmap.h>
#include <sound/sdw.h>
#include <sound/soc-component.h>
@@ -32,6 +34,7 @@ struct class_function_drv {
struct sdca_class_drv *core;
struct sdca_function_data *function;
+ bool suspended;
};
static void class_function_regmap_lock(void *data)
@@ -195,26 +198,26 @@ static int class_function_component_probe(struct snd_soc_component *component)
return sdca_irq_populate(drv->function, component, core->irq_info);
}
+static int class_function_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
+ struct sdca_class_drv *core = drv->core;
+
+ return sdca_jack_set_jack(core->irq_info, jack);
+}
+
static const struct snd_soc_component_driver class_function_component_drv = {
.probe = class_function_component_probe,
.endianness = 1,
};
-static int class_function_boot(struct class_function_drv *drv)
+static int class_function_init_device(struct class_function_drv *drv,
+ unsigned int status)
{
- unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
- SDCA_ENTITY_TYPE_ENTITY_0,
- SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
- unsigned int val;
int ret;
- ret = regmap_read(drv->regmap, reg, &val);
- if (ret < 0) {
- dev_err(drv->dev, "failed to read function status: %d\n", ret);
- return ret;
- }
-
- if (!(val & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {
+ if (!(status & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {
dev_dbg(drv->dev, "reset function device\n");
ret = sdca_reset_function(drv->dev, drv->function, drv->regmap);
@@ -222,24 +225,38 @@ static int class_function_boot(struct class_function_drv *drv)
return ret;
}
- if (val & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {
+ if (status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {
dev_dbg(drv->dev, "write initialisation\n");
ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap,
drv->function);
if (ret)
return ret;
+ }
- ret = regmap_write(drv->regmap, reg,
- SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION);
- if (ret < 0) {
- dev_err(drv->dev,
- "failed to clear function init status: %d\n",
- ret);
- return ret;
- }
+ return 0;
+}
+
+static int class_function_boot(struct class_function_drv *drv)
+{
+ unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+ unsigned int val;
+ int ret;
+
+ guard(mutex)(&drv->core->init_lock);
+
+ ret = regmap_read(drv->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to read function status: %d\n", ret);
+ return ret;
}
+ ret = class_function_init_device(drv, val);
+ if (ret)
+ return ret;
+
/* Start FDL process */
ret = sdca_irq_populate_early(drv->dev, drv->regmap, drv->function,
drv->core->irq_info);
@@ -351,12 +368,17 @@ static int class_function_probe(struct auxiliary_device *auxdev,
return dev_err_probe(dev, PTR_ERR(drv->regmap),
"failed to create regmap");
+ if (desc->type == SDCA_FUNCTION_TYPE_UAJ)
+ cmp_drv->set_jack = class_function_set_jack;
+
ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv,
&dais, &num_dais,
&class_function_sdw_ops);
if (ret)
return ret;
+ dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
+
pm_runtime_set_autosuspend_delay(dev, 200);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
@@ -401,9 +423,44 @@ static int class_function_runtime_resume(struct device *dev)
struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
int ret;
+ guard(mutex)(&drv->core->init_lock);
+
regcache_mark_dirty(drv->regmap);
regcache_cache_only(drv->regmap, false);
+ if (drv->suspended) {
+ unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,
+ SDCA_ENTITY_TYPE_ENTITY_0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+ unsigned int val;
+
+ ret = regmap_read(drv->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to read function status: %d\n", ret);
+ goto err;
+ }
+
+ ret = class_function_init_device(drv, val);
+ if (ret)
+ goto err;
+
+ sdca_irq_enable_early(drv->function, drv->core->irq_info);
+
+ ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info);
+ if (ret)
+ goto err;
+
+ sdca_irq_enable(drv->function, drv->core->irq_info);
+
+ ret = regmap_write(drv->regmap, reg, 0xFF);
+ if (ret < 0) {
+ dev_err(drv->dev, "failed to clear function status: %d\n", ret);
+ goto err;
+ }
+
+ drv->suspended = false;
+ }
+
ret = regcache_sync(drv->regmap);
if (ret) {
dev_err(drv->dev, "failed to restore register cache: %d\n", ret);
@@ -418,7 +475,49 @@ err:
return ret;
}
+static int class_function_suspend(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
+ int ret;
+
+ drv->suspended = true;
+
+ /* Ensure runtime resume runs on resume */
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "failed to resume for suspend: %d\n", ret);
+ return ret;
+ }
+
+ sdca_irq_disable(drv->function, drv->core->irq_info);
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to force suspend: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_put_noidle(dev);
+
+ return 0;
+}
+
+static int class_function_resume(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "failed to force resume: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct dev_pm_ops class_function_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(class_function_suspend, class_function_resume)
RUNTIME_PM_OPS(class_function_runtime_suspend,
class_function_runtime_resume, NULL)
};
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index 3180ebd07c40..07892bc3a44e 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -256,8 +256,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
tmp->file_length != firmware->size) {
dev_err(dev, "bad disk SWF size\n");
} else if (!swf || swf->file_version <= tmp->file_version) {
- dev_dbg(dev, "using SWF from disk: %x-%x-%x\n",
- tmp->vendor_id, tmp->file_id, tmp->file_version);
+ dev_dbg(dev, "using SWF from disk\n");
swf = tmp;
}
}
@@ -267,6 +266,9 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
return -ENOENT;
}
+ dev_info(dev, "loading SWF: %x-%x-%x\n",
+ swf->vendor_id, swf->file_id, swf->file_version);
+
ret = sdca_ump_write_message(dev, interrupt->device_regmap,
interrupt->function_regmap,
interrupt->function, interrupt->entity,
@@ -487,7 +489,7 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
struct device *dev = interrupt->dev;
struct fdl_state *fdl_state;
- fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL);
+ fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL);
if (!fdl_state)
return -ENOMEM;
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 5a1f120487ef..95b67bb904c3 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -911,10 +911,38 @@ static int find_sdca_control_value(struct device *dev, struct sdca_entity *entit
return 0;
}
-/*
- * TODO: Add support for -cn- properties, allowing different channels to have
- * different defaults etc.
- */
+static int find_sdca_control_reset(const struct sdca_entity *entity,
+ struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE):
+ case SDCA_CTL_TYPE_S(GE, SELECTED_MODE):
+ case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER):
+ case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK):
+ control->has_reset = true;
+ control->reset = 0;
+ break;
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT):
+ control->has_reset = true;
+ control->reset = 1;
+ break;
+ case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS):
+ control->has_reset = true;
+ control->reset = 3;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity,
struct fwnode_handle *control_node,
struct sdca_control *control)
@@ -952,7 +980,7 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
}
control->values = devm_kcalloc(dev, hweight64(control->cn_list),
- sizeof(int), GFP_KERNEL);
+ sizeof(*control->values), GFP_KERNEL);
if (!control->values)
return -ENOMEM;
@@ -990,6 +1018,10 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
control->is_volatile = find_sdca_control_volatile(entity, control);
+ ret = find_sdca_control_reset(entity, control);
+ if (ret)
+ return ret;
+
ret = find_sdca_control_range(dev, control_node, &control->range);
if (ret) {
dev_err(dev, "%s: control %#x: range missing: %d\n",
@@ -1184,7 +1216,6 @@ static int find_sdca_entity_pde(struct device *dev,
{
static const int mult_delay = 3;
struct sdca_entity_pde *power = &entity->pde;
- u32 *delay_list __free(kfree) = NULL;
struct sdca_pde_delay *delays;
int num_delays;
int i, j;
@@ -1205,7 +1236,8 @@ static int find_sdca_entity_pde(struct device *dev,
return -EINVAL;
}
- delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL);
+ u32 *delay_list __free(kfree) = kcalloc(num_delays, sizeof(*delay_list),
+ GFP_KERNEL);
if (!delay_list)
return -ENOMEM;
@@ -1250,7 +1282,6 @@ static int find_sdca_entity_ge(struct device *dev,
struct sdca_entity *entity)
{
struct sdca_entity_ge *group = &entity->ge;
- u8 *affected_list __free(kfree) = NULL;
u8 *affected_iter;
int num_affected;
int i, j;
@@ -1269,7 +1300,8 @@ static int find_sdca_entity_ge(struct device *dev,
return -EINVAL;
}
- affected_list = kcalloc(num_affected, sizeof(*affected_list), GFP_KERNEL);
+ u8 *affected_list __free(kfree) = kcalloc(num_affected, sizeof(*affected_list),
+ GFP_KERNEL);
if (!affected_list)
return -ENOMEM;
@@ -1495,7 +1527,6 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw,
struct fwnode_handle *function_node,
struct sdca_function_data *function)
{
- u32 *entity_list __free(kfree) = NULL;
struct sdca_entity *entities;
int num_entities;
int i, ret;
@@ -1517,7 +1548,8 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw,
if (!entities)
return -ENOMEM;
- entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL);
+ u32 *entity_list __free(kfree) = kcalloc(num_entities, sizeof(*entity_list),
+ GFP_KERNEL);
if (!entity_list)
return -ENOMEM;
@@ -1642,7 +1674,6 @@ static int find_sdca_entity_connection_pde(struct device *dev,
struct sdca_entity *entity)
{
struct sdca_entity_pde *power = &entity->pde;
- u32 *managed_list __free(kfree) = NULL;
struct sdca_entity **managed;
int num_managed;
int i;
@@ -1664,7 +1695,8 @@ static int find_sdca_entity_connection_pde(struct device *dev,
if (!managed)
return -ENOMEM;
- managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL);
+ u32 *managed_list __free(kfree) = kcalloc(num_managed, sizeof(*managed_list),
+ GFP_KERNEL);
if (!managed_list)
return -ENOMEM;
@@ -1961,7 +1993,6 @@ static int find_sdca_clusters(struct device *dev,
struct fwnode_handle *function_node,
struct sdca_function_data *function)
{
- u32 *cluster_list __free(kfree) = NULL;
struct sdca_cluster *clusters;
int num_clusters;
int i, ret;
@@ -1982,7 +2013,8 @@ static int find_sdca_clusters(struct device *dev,
if (!clusters)
return -ENOMEM;
- cluster_list = kcalloc(num_clusters, sizeof(*cluster_list), GFP_KERNEL);
+ u32 *cluster_list __free(kfree) = kcalloc(num_clusters, sizeof(*cluster_list),
+ GFP_KERNEL);
if (!cluster_list)
return -ENOMEM;
@@ -2026,7 +2058,6 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
{
static const int mult_fileset = 3;
char fileset_name[SDCA_PROPERTY_LENGTH];
- u32 *filesets_list __free(kfree) = NULL;
struct sdca_fdl_set *sets;
int num_sets;
int i, j;
@@ -2034,6 +2065,7 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
num_sets = fwnode_property_count_u32(function_node,
"mipi-sdca-file-set-id-list");
if (num_sets == 0 || num_sets == -EINVAL) {
+ dev_dbg(dev, "%pfwP: file set id list missing\n", function_node);
return 0;
} else if (num_sets < 0) {
dev_err(dev, "%pfwP: failed to read file set list: %d\n",
@@ -2041,19 +2073,19 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
return num_sets;
}
- filesets_list = kcalloc(num_sets, sizeof(u32), GFP_KERNEL);
+ u32 *filesets_list __free(kfree) = kcalloc(num_sets, sizeof(u32),
+ GFP_KERNEL);
if (!filesets_list)
return -ENOMEM;
fwnode_property_read_u32_array(function_node, "mipi-sdca-file-set-id-list",
filesets_list, num_sets);
- sets = devm_kcalloc(dev, num_sets, sizeof(struct sdca_fdl_set), GFP_KERNEL);
+ sets = devm_kcalloc(dev, num_sets, sizeof(*sets), GFP_KERNEL);
if (!sets)
return -ENOMEM;
for (i = 0; i < num_sets; i++) {
- u32 *fileset_entries __free(kfree) = NULL;
struct sdca_fdl_set *set = &sets[i];
struct sdca_fdl_file *files;
int num_files, num_entries;
@@ -2075,11 +2107,12 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw,
dev_dbg(dev, "fileset: %#x\n", filesets_list[i]);
files = devm_kcalloc(dev, num_entries / mult_fileset,
- sizeof(struct sdca_fdl_file), GFP_KERNEL);
+ sizeof(*files), GFP_KERNEL);
if (!files)
return -ENOMEM;
- fileset_entries = kcalloc(num_entries, sizeof(u32), GFP_KERNEL);
+ u32 *fileset_entries __free(kfree) = kcalloc(num_entries, sizeof(u32),
+ GFP_KERNEL);
if (!fileset_entries)
return -ENOMEM;
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 8f6a2adfb6fb..d9e22cf40f77 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -22,6 +22,7 @@
#include <sound/sdca_function.h>
#include <sound/sdca_hid.h>
#include <sound/sdca_interrupts.h>
+#include <sound/sdca_jack.h>
#include <sound/sdca_ump.h>
#include <sound/soc-component.h>
#include <sound/soc.h>
@@ -155,14 +156,7 @@ static irqreturn_t detected_mode_handler(int irq, void *data)
{
struct sdca_interrupt *interrupt = data;
struct device *dev = interrupt->dev;
- struct snd_soc_component *component = interrupt->component;
- struct snd_soc_card *card = component->card;
- struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
- struct snd_kcontrol *kctl = interrupt->priv;
- struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
- struct soc_enum *soc_enum;
irqreturn_t irqret = IRQ_NONE;
- unsigned int reg, val;
int ret;
ret = pm_runtime_get_sync(dev);
@@ -171,76 +165,9 @@ static irqreturn_t detected_mode_handler(int irq, void *data)
goto error;
}
- if (!kctl) {
- const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
- interrupt->entity->label,
- SDCA_CTL_SELECTED_MODE_NAME);
-
- if (!name)
- goto error;
-
- kctl = snd_soc_component_get_kcontrol(component, name);
- if (!kctl) {
- dev_dbg(dev, "control not found: %s\n", name);
- goto error;
- }
-
- interrupt->priv = kctl;
- }
-
- soc_enum = (struct soc_enum *)kctl->private_value;
-
- reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
- interrupt->control->sel, 0);
-
- ret = regmap_read(interrupt->function_regmap, reg, &val);
- if (ret < 0) {
- dev_err(dev, "failed to read detected mode: %d\n", ret);
- goto error;
- }
-
- switch (val) {
- case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
- case SDCA_DETECTED_MODE_JACK_UNKNOWN:
- reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
- interrupt->entity->id,
- SDCA_CTL_GE_SELECTED_MODE, 0);
-
- /*
- * Selected mode is not normally marked as volatile register
- * (RW), but here force a read from the hardware. If the
- * detected mode is unknown we need to see what the device
- * selected as a "safe" option.
- */
- regcache_drop_region(interrupt->function_regmap, reg, reg);
-
- ret = regmap_read(interrupt->function_regmap, reg, &val);
- if (ret) {
- dev_err(dev, "failed to re-check selected mode: %d\n", ret);
- goto error;
- }
- break;
- default:
- break;
- }
-
- dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
-
- ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
- if (!ucontrol)
- goto error;
-
- ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
-
- down_write(rwsem);
- ret = kctl->put(kctl, ucontrol);
- up_write(rwsem);
- if (ret < 0) {
- dev_err(dev, "failed to update selected mode: %d\n", ret);
+ ret = sdca_jack_process(interrupt);
+ if (ret)
goto error;
- }
-
- snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
irqret = IRQ_HANDLED;
error:
@@ -271,6 +198,18 @@ error:
return irqret;
}
+#ifdef CONFIG_PM_SLEEP
+static bool no_pm_in_progress(struct device *dev)
+{
+ return completion_done(&dev->power.completion);
+}
+#else
+static bool no_pm_in_progress(struct device *dev)
+{
+ return true;
+}
+#endif
+
static irqreturn_t fdl_owner_handler(int irq, void *data)
{
struct sdca_interrupt *interrupt = data;
@@ -278,10 +217,16 @@ static irqreturn_t fdl_owner_handler(int irq, void *data)
irqreturn_t irqret = IRQ_NONE;
int ret;
- ret = pm_runtime_get_sync(dev);
- if (ret < 0) {
- dev_err(dev, "failed to resume for fdl: %d\n", ret);
- goto error;
+ /*
+ * FDL has to run from the system resume handler, at which point
+ * we can't wait for the pm runtime.
+ */
+ if (no_pm_in_progress(dev)) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume for fdl: %d\n", ret);
+ goto error;
+ }
}
ret = sdca_fdl_process(interrupt);
@@ -290,7 +235,8 @@ static irqreturn_t fdl_owner_handler(int irq, void *data)
irqret = IRQ_HANDLED;
error:
- pm_runtime_put(dev);
+ if (no_pm_in_progress(dev))
+ pm_runtime_put(dev);
return irqret;
}
@@ -536,6 +482,10 @@ int sdca_irq_populate(struct sdca_function_data *function,
handler = function_status_handler;
break;
case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ ret = sdca_jack_alloc_state(interrupt);
+ if (ret)
+ return ret;
+
handler = detected_mode_handler;
break;
case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
@@ -610,3 +560,79 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
return info;
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
+
+static void irq_enable_flags(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info, bool early)
+{
+ struct sdca_interrupt *interrupt;
+ int i;
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ interrupt = &info->irqs[i];
+
+ if (!interrupt || interrupt->function != function)
+ continue;
+
+ switch (SDCA_CTL_TYPE(interrupt->entity->type,
+ interrupt->control->sel)) {
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ if (early)
+ enable_irq(interrupt->irq);
+ break;
+ default:
+ if (!early)
+ enable_irq(interrupt->irq);
+ break;
+ }
+ }
+}
+
+/**
+ * sdca_irq_enable_early - Re-enable early SDCA IRQs for a given function
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * The early version of the IRQ enable allows enabling IRQs which may be
+ * necessary to bootstrap functionality for other IRQs, such as the FDL
+ * process.
+ */
+void sdca_irq_enable_early(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ irq_enable_flags(function, info, true);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_enable_early, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_enable - Re-enable SDCA IRQs for a given function
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ */
+void sdca_irq_enable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ irq_enable_flags(function, info, false);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_enable, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_disable - Disable SDCA IRQs for a given function
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ */
+void sdca_irq_disable(struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ struct sdca_interrupt *interrupt;
+ int i;
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ interrupt = &info->irqs[i];
+
+ if (!interrupt || interrupt->function != function)
+ continue;
+
+ disable_irq(interrupt->irq);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_disable, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
new file mode 100644
index 000000000000..605514f02045
--- /dev/null
+++ b/sound/soc/sdca/sdca_jack.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/sprintf.h>
+#include <linux/regmap.h>
+#include <linux/rwsem.h>
+#include <sound/asound.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/sdca_jack.h>
+#include <sound/soc-component.h>
+#include <sound/soc-jack.h>
+#include <sound/soc.h>
+
+/**
+ * sdca_jack_process - Process an SDCA jack event
+ * @interrupt: SDCA interrupt structure
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_process(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct snd_soc_component *component = interrupt->component;
+ struct snd_soc_card *card = component->card;
+ struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
+ struct jack_state *state = interrupt->priv;
+ struct snd_kcontrol *kctl = state->kctl;
+ struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ unsigned int reg, val;
+ int ret;
+
+ guard(rwsem_write)(rwsem);
+
+ if (!kctl) {
+ const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+ interrupt->entity->label,
+ SDCA_CTL_SELECTED_MODE_NAME);
+
+ if (!name)
+ return -ENOMEM;
+
+ kctl = snd_soc_component_get_kcontrol(component, name);
+ if (!kctl)
+ dev_dbg(dev, "control not found: %s\n", name);
+ else
+ state->kctl = kctl;
+ }
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ interrupt->control->sel, 0);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read detected mode: %d\n", ret);
+ return ret;
+ }
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ SDCA_CTL_GE_SELECTED_MODE, 0);
+
+ switch (val) {
+ case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
+ case SDCA_DETECTED_MODE_JACK_UNKNOWN:
+ /*
+ * Selected mode is not normally marked as volatile register
+ * (RW), but here force a read from the hardware. If the
+ * detected mode is unknown we need to see what the device
+ * selected as a "safe" option.
+ */
+ regcache_drop_region(interrupt->function_regmap, reg, reg);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "failed to re-check selected mode: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
+
+ if (kctl) {
+ struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value;
+
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ return -ENOMEM;
+
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
+
+ ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
+ if (ret < 0) {
+ dev_err(dev, "failed to update selected mode: %d\n", ret);
+ return ret;
+ }
+
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ } else {
+ ret = regmap_write(interrupt->function_regmap, reg, val);
+ if (ret) {
+ dev_err(dev, "failed to write selected mode: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return sdca_jack_report(interrupt);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
+
+/**
+ * sdca_jack_alloc_state - allocate state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
+{
+ struct device *dev = interrupt->dev;
+ struct jack_state *jack_state;
+
+ jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL);
+ if (!jack_state)
+ return -ENOMEM;
+
+ interrupt->priv = jack_state;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
+
+/**
+ * sdca_jack_set_jack - attach an ASoC jack to SDCA
+ * @info: SDCA interrupt information.
+ * @jack: ASoC jack to be attached.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack)
+{
+ int i, ret;
+
+ guard(mutex)(&info->irq_lock);
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ struct sdca_interrupt *interrupt = &info->irqs[i];
+ struct sdca_control *control = interrupt->control;
+ struct sdca_entity *entity = interrupt->entity;
+ struct jack_state *jack_state;
+
+ if (!interrupt->irq)
+ continue;
+
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ jack_state = interrupt->priv;
+ jack_state->jack = jack;
+
+ /* Report initial state in case IRQ was already handled */
+ ret = sdca_jack_report(interrupt);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA");
+
+int sdca_jack_report(struct sdca_interrupt *interrupt)
+{
+ struct jack_state *jack_state = interrupt->priv;
+ struct sdca_control_range *range;
+ enum sdca_terminal_type type;
+ unsigned int report = 0;
+ unsigned int reg, val;
+ int ret;
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ SDCA_CTL_GE_SELECTED_MODE, 0);
+
+ ret = regmap_read(interrupt->function_regmap, reg, &val);
+ if (ret) {
+ dev_err(interrupt->dev, "failed to read selected mode: %d\n", ret);
+ return ret;
+ }
+
+ range = sdca_selector_find_range(interrupt->dev, interrupt->entity,
+ SDCA_CTL_GE_SELECTED_MODE,
+ SDCA_SELECTED_MODE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
+ val, SDCA_SELECTED_MODE_TERM_TYPE);
+
+ switch (type) {
+ case SDCA_TERM_TYPE_LINEIN_STEREO:
+ case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
+ case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
+ case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
+ case SDCA_TERM_TYPE_LINEIN_REAR_LR:
+ report = SND_JACK_LINEIN;
+ break;
+ case SDCA_TERM_TYPE_LINEOUT_STEREO:
+ case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
+ case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
+ case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
+ case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
+ report = SND_JACK_LINEOUT;
+ break;
+ case SDCA_TERM_TYPE_MIC_JACK:
+ report = SND_JACK_MICROPHONE;
+ break;
+ case SDCA_TERM_TYPE_HEADPHONE_JACK:
+ report = SND_JACK_HEADPHONE;
+ break;
+ case SDCA_TERM_TYPE_HEADSET_JACK:
+ report = SND_JACK_HEADSET;
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_jack_report(jack_state->jack, report, 0xFFFF);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_report, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c
index 2cca9a9c71ea..4f8a685dc43d 100644
--- a/sound/soc/sdca/sdca_regmap.c
+++ b/sound/soc/sdca/sdca_regmap.c
@@ -218,7 +218,8 @@ int sdca_regmap_count_constants(struct device *dev,
struct sdca_entity *entity = &function->entities[i];
for (j = 0; j < entity->num_controls; j++) {
- if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC)
+ if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC ||
+ entity->controls[j].has_reset)
nconsts += hweight64(entity->controls[j].cn_list);
}
}
@@ -255,7 +256,8 @@ int sdca_regmap_populate_constants(struct device *dev,
struct sdca_control *control = &entity->controls[j];
int cn;
- if (control->mode != SDCA_ACCESS_MODE_DC)
+ if (control->mode != SDCA_ACCESS_MODE_DC &&
+ !control->has_reset)
continue;
l = 0;
@@ -264,7 +266,10 @@ int sdca_regmap_populate_constants(struct device *dev,
consts[k].reg = SDW_SDCA_CTL(function->desc->adr,
entity->id,
control->sel, cn);
- consts[k].def = control->values[l];
+ if (control->mode == SDCA_ACCESS_MODE_DC)
+ consts[k].def = control->values[l];
+ else
+ consts[k].def = control->reset;
k++;
l++;
}
@@ -306,6 +311,9 @@ static int populate_control_defaults(struct device *dev, struct regmap *regmap,
i++;
} else if (!control->is_volatile) {
+ if (control->has_reset)
+ regcache_drop_region(regmap, reg, reg);
+
ret = regmap_read(regmap, reg, &val);
if (ret) {
dev_err(dev, "Failed to read initial %#x: %d\n",
diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c
index 8aba3ff16872..a86bb28c6d0a 100644
--- a/sound/soc/sdca/sdca_ump.c
+++ b/sound/soc/sdca/sdca_ump.c
@@ -257,6 +257,6 @@ void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_u
if (!timeout_us)
return;
- queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us));
+ queue_delayed_work(system_dfl_wq, work, usecs_to_jiffies(timeout_us));
}
EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA");
diff --git a/sound/soc/sdw_utils/soc_sdw_ti_amp.c b/sound/soc/sdw_utils/soc_sdw_ti_amp.c
index cbd60faecd09..488ef2ef45d4 100644
--- a/sound/soc/sdw_utils/soc_sdw_ti_amp.c
+++ b/sound/soc/sdw_utils/soc_sdw_ti_amp.c
@@ -58,6 +58,10 @@ int asoc_sdw_ti_spk_rtd_init(struct snd_soc_pcm_runtime *rtd,
strscpy(speaker, "Left Spk", sizeof(speaker));
} else if (!strncmp(prefix, "tas2783-2", strlen("tas2783-2"))) {
strscpy(speaker, "Right Spk", sizeof(speaker));
+ } else if (!strncmp(prefix, "tas2783-3", strlen("tas2783-3"))) {
+ strscpy(speaker, "Left Spk2", sizeof(speaker));
+ } else if (!strncmp(prefix, "tas2783-4", strlen("tas2783-4"))) {
+ strscpy(speaker, "Right Spk2", sizeof(speaker));
} else {
ret = -EINVAL;
dev_err(card->dev, "unhandled prefix %s", prefix);
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
index ccf149f949e8..0e67d9f34cba 100644
--- a/sound/soc/sdw_utils/soc_sdw_utils.c
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -40,11 +40,25 @@ static const struct snd_soc_dapm_widget lr_spk_widgets[] = {
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
+static const struct snd_soc_dapm_widget lr_4spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+ SND_SOC_DAPM_SPK("Left Spk2", NULL),
+ SND_SOC_DAPM_SPK("Right Spk2", NULL),
+};
+
static const struct snd_kcontrol_new lr_spk_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
+static const struct snd_kcontrol_new lr_4spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+ SOC_DAPM_PIN_SWITCH("Left Spk2"),
+ SOC_DAPM_PIN_SWITCH("Right Spk2"),
+};
+
static const struct snd_soc_dapm_widget rt700_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
@@ -69,10 +83,10 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.init = asoc_sdw_ti_amp_init,
.rtd_init = asoc_sdw_ti_spk_rtd_init,
- .controls = lr_spk_controls,
- .num_controls = ARRAY_SIZE(lr_spk_controls),
- .widgets = lr_spk_widgets,
- .num_widgets = ARRAY_SIZE(lr_spk_widgets),
+ .controls = lr_4spk_controls,
+ .num_controls = ARRAY_SIZE(lr_4spk_controls),
+ .widgets = lr_4spk_widgets,
+ .num_widgets = ARRAY_SIZE(lr_4spk_widgets),
},
},
.dai_num = 1,
@@ -492,6 +506,8 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_type = SOC_SDW_DAI_TYPE_MIC,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
},
},
.dai_num = 3,
@@ -1421,29 +1437,14 @@ static int is_sdca_endpoint_present(struct device *dev,
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
const struct snd_soc_acpi_endpoint *adr_end;
const struct asoc_sdw_dai_info *dai_info;
- struct snd_soc_dai_link_component *dlc;
- struct snd_soc_dai *codec_dai;
struct sdw_slave *slave;
struct device *sdw_dev;
const char *sdw_codec_name;
int ret, i;
- dlc = kzalloc(sizeof(*dlc), GFP_KERNEL);
- if (!dlc)
- return -ENOMEM;
-
adr_end = &adr_dev->endpoints[end_index];
dai_info = &codec_info->dais[adr_end->num];
- dlc->dai_name = dai_info->dai_name;
- codec_dai = snd_soc_find_dai_with_mutex(dlc);
- if (!codec_dai) {
- dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
- kfree(dlc);
- return -EPROBE_DEFER;
- }
- kfree(dlc);
-
sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
if (!sdw_codec_name)
return -ENOMEM;
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index c815fd1b3fd1..89f236ab3034 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -142,88 +142,6 @@ int snd_soc_component_set_bias_level(struct snd_soc_component *component,
return soc_component_ret(component, ret);
}
-int snd_soc_component_enable_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_enable_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin);
-
-int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_enable_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked);
-
-int snd_soc_component_disable_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_disable_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin);
-
-int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_disable_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked);
-
-int snd_soc_component_nc_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_nc_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin);
-
-int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_nc_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked);
-
-int snd_soc_component_get_pin_status(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_get_pin_status(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status);
-
-int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_force_enable_pin(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin);
-
-int snd_soc_component_force_enable_pin_unlocked(
- struct snd_soc_component *component,
- const char *pin)
-{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked);
-
static void soc_get_kcontrol_name(struct snd_soc_component *component,
char *buf, int size, const char * const ctl)
{
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e4b21bf39e59..5811d053ce7a 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2556,6 +2556,10 @@ int snd_soc_register_card(struct snd_soc_card *card)
if (!card->name || !card->dev)
return -EINVAL;
+ card->dapm = snd_soc_dapm_alloc(card->dev);
+ if (!card->dapm)
+ return -ENOMEM;
+
dev_set_drvdata(card->dev, card);
INIT_LIST_HEAD(&card->widgets);
@@ -2675,6 +2679,8 @@ static inline char *fmt_multiple_name(struct device *dev,
void snd_soc_unregister_dai(struct snd_soc_dai *dai)
{
+ lockdep_assert_held(&client_mutex);
+
dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
list_del(&dai->list);
}
@@ -2840,6 +2846,10 @@ int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver,
struct device *dev)
{
+ component->dapm = snd_soc_dapm_alloc(dev);
+ if (!component->dapm)
+ return -ENOMEM;
+
INIT_LIST_HEAD(&component->dai_list);
INIT_LIST_HEAD(&component->dobj_list);
INIT_LIST_HEAD(&component->card_list);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 4d920a59da3c..c23ccf4a602d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -40,12 +40,33 @@
#include <trace/events/asoc.h>
+/* DAPM context */
+struct snd_soc_dapm_context {
+ enum snd_soc_bias_level bias_level;
+
+ bool idle_bias; /* Use BIAS_OFF instead of STANDBY when false */
+
+ struct snd_soc_component *component; /* parent component */
+ struct snd_soc_card *card; /* parent card */
+
+ /* used during DAPM updates */
+ enum snd_soc_bias_level target_bias_level;
+ struct list_head list;
+
+ struct snd_soc_dapm_widget *wcache_sink;
+ struct snd_soc_dapm_widget *wcache_source;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_dapm;
+#endif
+};
+
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
-#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \
+#define DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \
SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN)
-#define snd_soc_dapm_for_each_direction(dir) \
+#define dapm_for_each_direction(dir) \
for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
(dir)++)
@@ -138,14 +159,14 @@ static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
snd_soc_dapm_mutex_assert_held(dapm);
}
-static void pop_wait(u32 pop_time)
+static void dapm_pop_wait(u32 pop_time)
{
if (pop_time)
schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
}
__printf(3, 4)
-static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
+static void dapm_pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
{
va_list args;
char *buf;
@@ -165,6 +186,11 @@ static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
kfree(buf);
}
+struct snd_soc_dapm_context *snd_soc_dapm_alloc(struct device *dev)
+{
+ return devm_kzalloc(dev, sizeof(struct snd_soc_dapm_context), GFP_KERNEL);
+}
+
struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm)
{
if (dapm->component)
@@ -214,7 +240,7 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
static __always_inline void dapm_widget_invalidate_paths(
struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir)
{
- enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_widget *node;
struct snd_soc_dapm_path *p;
LIST_HEAD(list);
@@ -361,7 +387,7 @@ struct dapm_kcontrol_data {
struct snd_soc_dapm_widget_list *wlist;
};
-static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg)
+static unsigned int dapm_read(struct snd_soc_dapm_context *dapm, int reg)
{
if (!dapm->component)
return -EIO;
@@ -381,7 +407,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
unsigned int shift = mc->shift;
unsigned int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
- unsigned int val = soc_dapm_read(p->sink->dapm, reg);
+ unsigned int val = dapm_read(p->sink->dapm, reg);
/*
* The nth_path argument allows this function to know
@@ -397,7 +423,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
*/
if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
if (reg != mc->rreg)
- val = soc_dapm_read(p->sink->dapm, mc->rreg);
+ val = dapm_read(p->sink->dapm, mc->rreg);
val = (val >> mc->rshift) & mask;
} else {
val = (val >> shift) & mask;
@@ -429,7 +455,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
if (e->reg != SND_SOC_NOPM) {
unsigned int val;
- val = soc_dapm_read(dapm, e->reg);
+ val = dapm_read(dapm, e->reg);
val = (val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
} else {
@@ -517,7 +543,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
break;
case snd_soc_dapm_line:
ep = 0;
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
if (!list_empty(&w->edges[dir]))
ep |= SND_SOC_DAPM_DIR_TO_EP(dir);
}
@@ -529,7 +555,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
w->is_ep = ep;
}
-static int snd_soc_dapm_check_dynamic_path(
+static int dapm_check_dynamic_path(
struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
const char *control)
@@ -575,7 +601,7 @@ static int snd_soc_dapm_check_dynamic_path(
return 0;
}
-static int snd_soc_dapm_add_path(
+static int dapm_add_path(
struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
@@ -608,7 +634,7 @@ static int snd_soc_dapm_add_path(
return -EINVAL;
}
- ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
+ ret = dapm_check_dynamic_path(dapm, wsource, wsink, control);
if (ret)
return ret;
@@ -660,10 +686,10 @@ static int snd_soc_dapm_add_path(
list_add(&path->list, &dapm->card->paths);
- snd_soc_dapm_for_each_direction(dir)
+ dapm_for_each_direction(dir)
list_add(&path->list_node[dir], &path->node[dir]->edges[dir]);
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
dapm_update_widget_flags(path->node[dir]);
dapm_mark_dirty(path->node[dir], "Route added");
}
@@ -771,10 +797,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
goto err_data;
}
- snd_soc_dapm_add_path(widget->dapm, data->widget,
- widget, NULL, NULL);
+ dapm_add_path(widget->dapm, data->widget,
+ widget, NULL, NULL);
} else if (e->reg != SND_SOC_NOPM) {
- data->value = soc_dapm_read(widget->dapm, e->reg) &
+ data->value = dapm_read(widget->dapm, e->reg) &
(e->mask << e->shift_l);
}
break;
@@ -954,14 +980,14 @@ static void dapm_reset(struct snd_soc_card *card)
}
}
-static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm)
+static const char *dapm_prefix(struct snd_soc_dapm_context *dapm)
{
if (!dapm->component)
return NULL;
return dapm->component->name_prefix;
}
-static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
+static int dapm_update_bits(struct snd_soc_dapm_context *dapm,
int reg, unsigned int mask, unsigned int value)
{
if (!dapm->component)
@@ -970,7 +996,7 @@ static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
mask, value);
}
-static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,
+static int dapm_test_bits(struct snd_soc_dapm_context *dapm,
int reg, unsigned int mask, unsigned int value)
{
if (!dapm->component)
@@ -978,7 +1004,7 @@ static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,
return snd_soc_component_test_bits(dapm->component, reg, mask, value);
}
-static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
+static void dapm_async_complete(struct snd_soc_dapm_context *dapm)
{
if (dapm->component)
snd_soc_component_async_complete(dapm->component);
@@ -1076,7 +1102,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
if (ret != 0)
goto out;
- if (dapm != &card->dapm)
+ if (dapm != card->dapm)
ret = snd_soc_dapm_force_bias_level(dapm, level);
if (ret != 0)
@@ -1149,7 +1175,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
const char *name;
int ret = 0;
- prefix = soc_dapm_prefix(dapm);
+ prefix = dapm_prefix(dapm);
if (prefix)
prefix_len = strlen(prefix) + 1;
else
@@ -1269,10 +1295,10 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
data = snd_kcontrol_chip(w->kcontrols[i]);
if (data->widget)
- snd_soc_dapm_add_path(data->widget->dapm,
- data->widget,
- path->source,
- NULL, NULL);
+ dapm_add_path(data->widget->dapm,
+ data->widget,
+ path->source,
+ NULL, NULL);
}
}
@@ -1376,7 +1402,7 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
*/
-static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
+static int dapm_suspend_check(struct snd_soc_dapm_widget *widget)
{
struct device *dev = snd_soc_dapm_to_dev(widget->dapm);
int level = snd_power_get_state(widget->dapm->card->snd_card);
@@ -1428,10 +1454,10 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
* widget and all widgets that can be reached via incoming or outcoming paths
* from the widget.
*/
-static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
+static void dapm_invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
enum snd_soc_dapm_direction dir)
{
- enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
widget->endpoints[dir] = -1;
@@ -1445,7 +1471,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
if (path->connect) {
path->walking = 1;
- invalidate_paths_ep(path->node[dir], dir);
+ dapm_invalidate_paths_ep(path->node[dir], dir);
path->walking = 0;
}
}
@@ -1458,7 +1484,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
* generic function and at the same time the fast path of the specialized
* functions is significantly smaller than the generic function.
*/
-static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
+static __always_inline int dapm_is_connected_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list, enum snd_soc_dapm_direction dir,
int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
@@ -1466,7 +1492,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
enum snd_soc_dapm_direction))
{
- enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ enum snd_soc_dapm_direction rdir = DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
int con = 0;
@@ -1485,7 +1511,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
}
if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
- widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
+ widget->endpoints[dir] = dapm_suspend_check(widget);
return widget->endpoints[dir];
}
@@ -1521,13 +1547,13 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
* direction as an arguments, it should return true if widgets from that point
* in the graph onwards should not be added to the widget list.
*/
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+static int dapm_is_connected_output_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
enum snd_soc_dapm_direction))
{
- return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
- is_connected_output_ep, custom_stop_condition);
+ return dapm_is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
+ dapm_is_connected_output_ep, custom_stop_condition);
}
/*
@@ -1539,13 +1565,13 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
* direction as an arguments, it should return true if the walk should be
* stopped and false otherwise.
*/
-static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
+static int dapm_is_connected_input_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
enum snd_soc_dapm_direction))
{
- return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
- is_connected_input_ep, custom_stop_condition);
+ return dapm_is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
+ dapm_is_connected_input_ep, custom_stop_condition);
}
/**
@@ -1581,12 +1607,12 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
snd_soc_dapm_mutex_lock(card);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
- paths = is_connected_output_ep(w, &widgets,
+ dapm_invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
+ paths = dapm_is_connected_output_ep(w, &widgets,
custom_stop_condition);
} else {
- invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);
- paths = is_connected_input_ep(w, &widgets,
+ dapm_invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);
+ paths = dapm_is_connected_input_ep(w, &widgets,
custom_stop_condition);
}
@@ -1619,7 +1645,7 @@ int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w,
struct device *dev = snd_soc_dapm_to_dev(w->dapm);
int ret;
- soc_dapm_async_complete(w->dapm);
+ dapm_async_complete(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event)) {
if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
@@ -1679,7 +1705,7 @@ int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w,
if (!w->clk)
return -EIO;
- soc_dapm_async_complete(w->dapm);
+ dapm_async_complete(w->dapm);
if (SND_SOC_DAPM_EVENT_ON(event)) {
return clk_prepare_enable(w->clk);
@@ -1714,8 +1740,8 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
- in = is_connected_input_ep(w, NULL, NULL);
- out = is_connected_output_ep(w, NULL, NULL);
+ in = dapm_is_connected_input_ep(w, NULL, NULL);
+ out = dapm_is_connected_output_ep(w, NULL, NULL);
return out != 0 && in != 0;
}
@@ -1838,9 +1864,9 @@ static void dapm_seq_check_event(struct snd_soc_card *card,
if (w->event && (w->event_flags & event)) {
int ret;
- pop_dbg(dev, card->pop_time, "pop test : %s %s\n",
+ dapm_pop_dbg(dev, card->pop_time, "pop test : %s %s\n",
w->name, ev_name);
- soc_dapm_async_complete(w->dapm);
+ dapm_async_complete(w->dapm);
trace_snd_soc_dapm_widget_event_start(w, event);
ret = w->event(w, NULL, event);
trace_snd_soc_dapm_widget_event_done(w, event);
@@ -1875,7 +1901,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
else
value |= w->off_val << w->shift;
- pop_dbg(dev, card->pop_time,
+ dapm_pop_dbg(dev, card->pop_time,
"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
w->name, reg, value, mask);
@@ -1889,11 +1915,11 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
* same register.
*/
- pop_dbg(dev, card->pop_time,
+ dapm_pop_dbg(dev, card->pop_time,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, card->pop_time);
- pop_wait(card->pop_time);
- soc_dapm_update_bits(dapm, reg, mask, value);
+ dapm_pop_wait(card->pop_time);
+ dapm_update_bits(dapm, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
@@ -1947,7 +1973,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
}
if (cur_dapm && w->dapm != cur_dapm)
- soc_dapm_async_complete(cur_dapm);
+ dapm_async_complete(cur_dapm);
INIT_LIST_HEAD(&pending);
cur_sort = -1;
@@ -2008,7 +2034,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
}
for_each_card_dapms(card, d)
- soc_dapm_async_complete(d);
+ dapm_async_complete(d);
}
static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_update *update)
@@ -2036,14 +2062,14 @@ static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_up
if (!w)
return;
- ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask,
+ ret = dapm_update_bits(w->dapm, update->reg, update->mask,
update->val);
if (ret < 0)
dev_err(dev, "ASoC: %s DAPM update failed: %d\n",
w->name, ret);
if (update->has_second_set) {
- ret = soc_dapm_update_bits(w->dapm, update->reg2,
+ ret = dapm_update_bits(w->dapm, update->reg2,
update->mask2, update->val2);
if (ret < 0)
dev_err(dev,
@@ -2358,9 +2384,9 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
return ret;
}
- pop_dbg(card->dev, card->pop_time,
+ dapm_pop_dbg(card->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
- pop_wait(card->pop_time);
+ dapm_pop_wait(card->pop_time);
trace_snd_soc_dapm_done(card, event);
@@ -2369,7 +2395,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
#ifdef CONFIG_DEBUG_FS
-static const char * const snd_soc_dapm_type_name[] = {
+static const char * const dapm_type_name[] = {
[snd_soc_dapm_input] = "input",
[snd_soc_dapm_output] = "output",
[snd_soc_dapm_mux] = "mux",
@@ -2422,7 +2448,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
struct snd_soc_dapm_path *p = NULL;
const char *c_name;
- BUILD_BUG_ON(ARRAY_SIZE(snd_soc_dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT);
+ BUILD_BUG_ON(ARRAY_SIZE(dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT);
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
@@ -2430,13 +2456,13 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
snd_soc_dapm_mutex_lock_root(w->dapm);
- /* Supply widgets are not handled by is_connected_{input,output}_ep() */
+ /* Supply widgets are not handled by dapm_is_connected_{input,output}_ep() */
if (w->is_supply) {
in = 0;
out = 0;
} else {
- in = is_connected_input_ep(w, NULL, NULL);
- out = is_connected_output_ep(w, NULL, NULL);
+ in = dapm_is_connected_input_ep(w, NULL, NULL);
+ out = dapm_is_connected_output_ep(w, NULL, NULL);
}
ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
@@ -2456,10 +2482,10 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->active ? "active" : "inactive");
ret += scnprintf(buf + ret, PAGE_SIZE - ret, " widget-type %s\n",
- snd_soc_dapm_type_name[w->id]);
+ dapm_type_name[w->id]);
- snd_soc_dapm_for_each_direction(dir) {
- rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+ dapm_for_each_direction(dir) {
+ rdir = DAPM_DIR_REVERSE(dir);
snd_soc_dapm_widget_for_each_path(w, dir, p) {
if (p->connected && !p->connected(p->source, p->sink))
continue;
@@ -2586,13 +2612,13 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
/*
- * soc_dapm_connect_path() - Connects or disconnects a path
+ * dapm_connect_path() - Connects or disconnects a path
* @path: The path to update
* @connect: The new connect state of the path. True if the path is connected,
* false if it is disconnected.
* @reason: The reason why the path changed (for debugging only)
*/
-static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+static void dapm_connect_path(struct snd_soc_dapm_path *path,
bool connect, const char *reason)
{
if (path->connect == connect)
@@ -2605,10 +2631,10 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
}
/* test and update the power status of a mux widget */
-static int soc_dapm_mux_update_power(struct snd_soc_card *card,
- struct snd_kcontrol *kcontrol,
- struct snd_soc_dapm_update *update,
- int mux, struct soc_enum *e)
+static int dapm_mux_update_power(struct snd_soc_card *card,
+ struct snd_kcontrol *kcontrol,
+ struct snd_soc_dapm_update *update,
+ int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -2625,7 +2651,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card,
else
connect = false;
- soc_dapm_connect_path(path, connect, "mux update");
+ dapm_connect_path(path, connect, "mux update");
}
if (found)
@@ -2642,7 +2668,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
int ret;
snd_soc_dapm_mutex_lock(card);
- ret = soc_dapm_mux_update_power(card, kcontrol, update, mux, e);
+ ret = dapm_mux_update_power(card, kcontrol, update, mux, e);
snd_soc_dapm_mutex_unlock(card);
if (ret > 0)
snd_soc_dpcm_runtime_update(card);
@@ -2651,10 +2677,10 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */
-static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
- struct snd_kcontrol *kcontrol,
- struct snd_soc_dapm_update *update,
- int connect, int rconnect)
+static int dapm_mixer_update_power(struct snd_soc_card *card,
+ struct snd_kcontrol *kcontrol,
+ struct snd_soc_dapm_update *update,
+ int connect, int rconnect)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -2686,9 +2712,9 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
* channel.
*/
if (found && rconnect >= 0)
- soc_dapm_connect_path(path, rconnect, "mixer update");
+ dapm_connect_path(path, rconnect, "mixer update");
else
- soc_dapm_connect_path(path, connect, "mixer update");
+ dapm_connect_path(path, connect, "mixer update");
found = 1;
}
@@ -2706,7 +2732,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
int ret;
snd_soc_dapm_mutex_lock(card);
- ret = soc_dapm_mixer_update_power(card, kcontrol, update, connect, -1);
+ ret = dapm_mixer_update_power(card, kcontrol, update, connect, -1);
snd_soc_dapm_mutex_unlock(card);
if (ret > 0)
snd_soc_dpcm_runtime_update(card);
@@ -2836,7 +2862,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
* While removing the path, remove reference to it from both
* source and sink widgets so that path is removed only once.
*/
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)
dapm_free_path(p);
}
@@ -2873,7 +2899,7 @@ static struct snd_soc_dapm_widget *dapm_find_widget(
struct snd_soc_dapm_widget *fallback = NULL;
char prefixed_pin[80];
const char *pin_name;
- const char *prefix = soc_dapm_prefix(dapm);
+ const char *prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s",
@@ -2903,8 +2929,8 @@ static struct snd_soc_dapm_widget *dapm_find_widget(
* returns 1 when the value has been updated, 0 when unchanged, or a negative
* error code; called from kcontrol put callback
*/
-static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
- const char *pin, int status)
+static int __dapm_set_pin(struct snd_soc_dapm_context *dapm,
+ const char *pin, int status)
{
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
struct device *dev = snd_soc_dapm_to_dev(dapm);
@@ -2932,13 +2958,13 @@ static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
}
/*
- * similar as __snd_soc_dapm_set_pin(), but returns 0 when successful;
+ * similar as __dapm_set_pin(), but returns 0 when successful;
* called from several API functions below
*/
-static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
+static int dapm_set_pin(struct snd_soc_dapm_context *dapm,
const char *pin, int status)
{
- int ret = __snd_soc_dapm_set_pin(dapm, pin, status);
+ int ret = __dapm_set_pin(dapm, pin, status);
return ret < 0 ? ret : 0;
}
@@ -3006,9 +3032,9 @@ static int dapm_update_dai_chan(struct snd_soc_dapm_path *p,
p->source->name, p->sink->name);
if (w->channel < channels)
- soc_dapm_connect_path(p, true, "dai update");
+ dapm_connect_path(p, true, "dai update");
else
- soc_dapm_connect_path(p, false, "dai update");
+ dapm_connect_path(p, false, "dai update");
return 0;
}
@@ -3086,7 +3112,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
unsigned int source_ref = 0;
int ret;
- prefix = soc_dapm_prefix(dapm);
+ prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
prefix, route->sink);
@@ -3155,7 +3181,7 @@ skip_search:
dapm->wcache_sink = wsink;
dapm->wcache_source = wsource;
- ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
+ ret = dapm_add_path(dapm, wsource, wsink, route->control,
route->connected);
err:
if (ret)
@@ -3185,7 +3211,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}
- prefix = soc_dapm_prefix(dapm);
+ prefix = dapm_prefix(dapm);
if (prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
prefix, route->sink);
@@ -3339,7 +3365,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
/* Read the initial power state from the device */
if (w->reg >= 0) {
- val = soc_dapm_read(w->dapm, w->reg);
+ val = dapm_read(w->dapm, w->reg);
val = val >> w->shift;
val &= w->mask;
if (val == w->on_val)
@@ -3383,11 +3409,11 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
snd_soc_dapm_mutex_lock(dapm);
if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
- reg_val = soc_dapm_read(dapm, reg);
+ reg_val = dapm_read(dapm, reg);
val = (reg_val >> shift) & mask;
if (reg != mc->rreg)
- reg_val = soc_dapm_read(dapm, mc->rreg);
+ reg_val = dapm_read(dapm, mc->rreg);
if (snd_soc_volsw_is_stereo(mc))
rval = (reg_val >> mc->rshift) & mask;
@@ -3471,12 +3497,12 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
val = val << shift;
rval = rval << mc->rshift;
- reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
+ reg_change = dapm_test_bits(dapm, reg, mask << shift, val);
if (snd_soc_volsw_is_stereo(mc))
- reg_change |= soc_dapm_test_bits(dapm, mc->rreg,
- mask << mc->rshift,
- rval);
+ reg_change |= dapm_test_bits(dapm, mc->rreg,
+ mask << mc->rshift,
+ rval);
}
if (change || reg_change) {
@@ -3493,7 +3519,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
update.val = val;
pupdate = &update;
}
- ret = soc_dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect);
+ ret = dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect);
}
snd_soc_dapm_mutex_unlock(card);
@@ -3523,7 +3549,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
snd_soc_dapm_mutex_lock(dapm);
if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) {
- reg_val = soc_dapm_read(dapm, e->reg);
+ reg_val = dapm_read(dapm, e->reg);
} else {
reg_val = snd_soc_dapm_kcontrol_get_value(kcontrol);
}
@@ -3580,7 +3606,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
change = dapm_kcontrol_set_value(kcontrol, val);
if (e->reg != SND_SOC_NOPM)
- reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val);
+ reg_change = dapm_test_bits(dapm, e->reg, mask, val);
if (change || reg_change) {
if (reg_change) {
@@ -3590,7 +3616,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
pupdate = &update;
}
- ret = soc_dapm_mux_update_power(card, kcontrol, pupdate, item[0], e);
+ ret = dapm_mux_update_power(card, kcontrol, pupdate, item[0], e);
}
snd_soc_dapm_mutex_unlock(card);
@@ -3673,14 +3699,14 @@ int snd_soc_dapm_get_component_pin_switch(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_component_pin_switch);
-static int __snd_soc_dapm_put_pin_switch(struct snd_soc_dapm_context *dapm,
- const char *pin,
- struct snd_ctl_elem_value *ucontrol)
+static int __dapm_put_pin_switch(struct snd_soc_dapm_context *dapm,
+ const char *pin,
+ struct snd_ctl_elem_value *ucontrol)
{
int ret;
snd_soc_dapm_mutex_lock(dapm);
- ret = __snd_soc_dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]);
+ ret = __dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]);
snd_soc_dapm_mutex_unlock(dapm);
snd_soc_dapm_sync(dapm);
@@ -3704,7 +3730,7 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
const char *pin = (const char *)kcontrol->private_value;
- return __snd_soc_dapm_put_pin_switch(dapm, pin, ucontrol);
+ return __dapm_put_pin_switch(dapm, pin, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
@@ -3724,7 +3750,7 @@ int snd_soc_dapm_put_component_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
const char *pin = (const char *)kcontrol->private_value;
- return __snd_soc_dapm_put_pin_switch(dapm, pin, ucontrol);
+ return __dapm_put_pin_switch(dapm, pin, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_component_pin_switch);
@@ -3737,7 +3763,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *w;
int ret = -ENOMEM;
- w = dapm_cnew_widget(widget, soc_dapm_prefix(dapm));
+ w = dapm_cnew_widget(widget, dapm_prefix(dapm));
if (!w)
goto cnew_failed;
@@ -3852,7 +3878,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
/* see for_each_card_widgets */
list_add_tail(&w->list, &dapm->card->widgets);
- snd_soc_dapm_for_each_direction(dir) {
+ dapm_for_each_direction(dir) {
INIT_LIST_HEAD(&w->edges[dir]);
w->endpoints[dir] = -1;
}
@@ -3925,9 +3951,8 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
-static int
-snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
- struct snd_pcm_substream *substream)
+static int dapm_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
+ struct snd_pcm_substream *substream)
{
struct device *dev = snd_soc_dapm_to_dev(w->dapm);
struct snd_soc_dapm_path *path;
@@ -4042,8 +4067,8 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
return 0;
}
-static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int dapm_dai_link_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dai *source, *sink;
@@ -4056,7 +4081,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- ret = snd_soc_dai_link_event_pre_pmu(w, substream);
+ ret = dapm_dai_link_event_pre_pmu(w, substream);
if (ret < 0)
goto out;
@@ -4134,8 +4159,8 @@ out:
return ret;
}
-static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int dapm_dai_link_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
struct snd_soc_pcm_runtime *rtd = w->priv;
@@ -4145,8 +4170,8 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int dapm_dai_link_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
struct snd_soc_pcm_runtime *rtd = w->priv;
@@ -4166,11 +4191,10 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static void
-snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
- unsigned long *private_value,
- int num_c2c_params,
- const char **w_param_text)
+static void dapm_free_kcontrol(struct snd_soc_card *card,
+ unsigned long *private_value,
+ int num_c2c_params,
+ const char **w_param_text)
{
int count;
@@ -4185,7 +4209,7 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,
}
static struct snd_kcontrol_new *
-snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
+dapm_alloc_kcontrol(struct snd_soc_card *card,
char *link_name,
const struct snd_soc_pcm_stream *c2c_params,
int num_c2c_params, const char **w_param_text,
@@ -4196,8 +4220,8 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
};
struct snd_kcontrol_new kcontrol_dai_link[] = {
SOC_ENUM_EXT(NULL, w_param_enum[0],
- snd_soc_dapm_dai_link_get,
- snd_soc_dapm_dai_link_put),
+ dapm_dai_link_get,
+ dapm_dai_link_put),
};
struct snd_kcontrol_new *kcontrol_news;
const struct snd_soc_pcm_stream *config = c2c_params;
@@ -4248,14 +4272,14 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
return kcontrol_news;
outfree_w_param:
- snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text);
+ dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text);
+
return NULL;
}
-static struct snd_soc_dapm_widget *
-snd_soc_dapm_new_dai(struct snd_soc_card *card,
- struct snd_pcm_substream *substream,
- char *id)
+static struct snd_soc_dapm_widget *dapm_new_dai(struct snd_soc_card *card,
+ struct snd_pcm_substream *substream,
+ char *id)
{
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -4285,10 +4309,10 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
goto param_fail;
num_kcontrols = 1;
- kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name,
- rtd->dai_link->c2c_params,
- rtd->dai_link->num_c2c_params,
- w_param_text, &private_value);
+ kcontrol_news = dapm_alloc_kcontrol(card, link_name,
+ rtd->dai_link->c2c_params,
+ rtd->dai_link->num_c2c_params,
+ w_param_text, &private_value);
if (!kcontrol_news)
goto param_fail;
}
@@ -4297,7 +4321,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
template.reg = SND_SOC_NOPM;
template.id = snd_soc_dapm_dai_link;
template.name = link_name;
- template.event = snd_soc_dai_link_event;
+ template.event = dapm_dai_link_event;
template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD;
template.kcontrol_news = kcontrol_news;
@@ -4317,7 +4341,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
outfree_kcontrol_news:
devm_kfree(card->dev, (void *)template.kcontrol_news);
- snd_soc_dapm_free_kcontrol(card, &private_value,
+ dapm_free_kcontrol(card, &private_value,
rtd->dai_link->num_c2c_params, w_param_text);
param_fail:
devm_kfree(card->dev, link_name);
@@ -4431,7 +4455,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
sink = dai_w;
}
dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);
- snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);
+ dapm_add_path(w->dapm, src, sink, NULL, NULL);
}
}
@@ -4452,11 +4476,11 @@ static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,
sink_dai->component->name, sink->name);
if (dai) {
- snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL);
+ dapm_add_path(dapm, src, dai, NULL, NULL);
src = dai;
}
- snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);
+ dapm_add_path(dapm, src, sink, NULL, NULL);
}
static void dapm_connect_dai_pair(struct snd_soc_card *card,
@@ -4490,8 +4514,8 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
/* special handling for [Codec2Codec] */
if (dai_link->c2c_params && !rtd->c2c_widget[stream]) {
struct snd_pcm_substream *substream = rtd->pcm->streams[stream].substream;
- struct snd_soc_dapm_widget *dai = snd_soc_dapm_new_dai(card, substream,
- widget_name[stream]);
+ struct snd_soc_dapm_widget *dai = dapm_new_dai(card, substream,
+ widget_name[stream]);
if (IS_ERR(dai))
continue;
@@ -4505,8 +4529,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
}
}
-static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
- int event)
+static void dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event)
{
struct snd_soc_dapm_widget *w;
@@ -4574,14 +4597,13 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
}
}
-static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
- int event)
+static void dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event)
{
struct snd_soc_dai *dai;
int i;
for_each_rtd_dais(rtd, i, dai)
- soc_dapm_dai_stream_event(dai, stream, event);
+ dapm_dai_stream_event(dai, stream, event);
dapm_power_widgets(rtd->card, event, NULL);
}
@@ -4603,7 +4625,7 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
struct snd_soc_card *card = rtd->card;
snd_soc_dapm_mutex_lock(card);
- soc_dapm_stream_event(rtd, stream, event);
+ dapm_stream_event(rtd, stream, event);
snd_soc_dapm_mutex_unlock(card);
}
@@ -4646,7 +4668,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop);
int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- return snd_soc_dapm_set_pin(dapm, pin, 1);
+ return dapm_set_pin(dapm, pin, 1);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked);
@@ -4667,7 +4689,7 @@ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin)
snd_soc_dapm_mutex_lock(dapm);
- ret = snd_soc_dapm_set_pin(dapm, pin, 1);
+ ret = dapm_set_pin(dapm, pin, 1);
snd_soc_dapm_mutex_unlock(dapm);
@@ -4763,7 +4785,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- return snd_soc_dapm_set_pin(dapm, pin, 0);
+ return dapm_set_pin(dapm, pin, 0);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked);
@@ -4784,7 +4806,7 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
snd_soc_dapm_mutex_lock(dapm);
- ret = snd_soc_dapm_set_pin(dapm, pin, 0);
+ ret = dapm_set_pin(dapm, pin, 0);
snd_soc_dapm_mutex_unlock(dapm);
@@ -4862,19 +4884,15 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
dapm->component = component;
dapm->bias_level = SND_SOC_BIAS_OFF;
- if (component) {
- dapm->dev = component->dev;
+ if (component)
dapm->idle_bias = component->driver->idle_bias_on;
- } else {
- dapm->dev = card->dev;
- }
INIT_LIST_HEAD(&dapm->list);
/* see for_each_card_dapms */
list_add(&dapm->list, &card->dapm_list);
}
-static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
+static void dapm_shutdown(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
@@ -4919,13 +4937,13 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
for_each_card_dapms(card, dapm) {
if (dapm != card_dapm) {
- soc_dapm_shutdown_dapm(dapm);
+ dapm_shutdown(dapm);
if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_OFF);
}
}
- soc_dapm_shutdown_dapm(card_dapm);
+ dapm_shutdown(card_dapm);
if (card_dapm->bias_level == SND_SOC_BIAS_STANDBY)
snd_soc_dapm_set_bias_level(card_dapm, SND_SOC_BIAS_OFF);
}
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 22d4b807e1bb..3cd4674dd800 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -190,15 +190,13 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
if (dsp_ack) {
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
- spin_lock_irq(&sdev->ipc_lock);
+ guard(spinlock_irq)(&sdev->ipc_lock);
/* handle immediate reply from DSP core */
acp_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, 0);
/* set the done bit */
acp_dsp_ipc_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_BOOT_COMPLETE: %#x\n",
dsp_ack);
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 90b932ae3bab..86d563c864e5 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -195,6 +195,14 @@ static int sof_compr_set_params(struct snd_soc_component *component,
if (sizeof(*pcm) + ext_data_size > sdev->ipc->max_payload_size)
return -EINVAL;
+ /*
+ * Make sure that the DSP is booted up, which might not be the
+ * case if the on-demand DSP boot is used
+ */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (ret)
+ return ret;
+
pcm = kzalloc(sizeof(*pcm) + ext_data_size, GFP_KERNEL);
if (!pcm)
return -ENOMEM;
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index a3fd1d523c09..74d997a4f620 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -187,14 +187,23 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
int ret, err;
+ /* ignore the ext_volatile_get call if the callbacks are not provided */
+ if (!tplg_ops || !tplg_ops->control ||
+ !tplg_ops->control->bytes_ext_volatile_get)
+ return 0;
+
ret = pm_runtime_resume_and_get(scomp->dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret);
return ret;
}
- if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get)
- ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
+ /* Make sure the DSP/firmware is booted up */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (!ret)
+ ret = tplg_ops->control->bytes_ext_volatile_get(scontrol,
+ binary_data,
+ size);
err = pm_runtime_put_autosuspend(scomp->dev);
if (err < 0)
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index b11f408f1366..2d394389c945 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -680,6 +680,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
mutex_init(&sdev->power_state_access);
mutex_init(&sdev->ipc_client_mutex);
mutex_init(&sdev->client_event_handler_mutex);
+ mutex_init(&sdev->dsp_fw_boot_mutex);
/* set default timeouts if none provided */
if (plat_data->desc->ipc_timeout == 0)
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index b24943a65c89..6b9e1f1ee657 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -216,7 +216,12 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
goto error;
}
- ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
+ /* Make sure the DSP/firmware is booted up */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (!ret)
+ ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply,
+ SOF_IPC_MSG_MAX_SIZE);
+
pm_runtime_put_autosuspend(sdev->dev);
if (ret < 0 || reply->rhdr.error < 0) {
ret = min(ret, reply->rhdr.error);
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index e787d3932fbb..7a03c8cc5dd4 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -81,14 +81,10 @@ EXPORT_SYMBOL(imx8_dump);
static void imx_handle_reply(struct imx_dsp_ipc *ipc)
{
- struct snd_sof_dev *sdev;
- unsigned long flags;
-
- sdev = imx_dsp_get_data(ipc);
+ struct snd_sof_dev *sdev = imx_dsp_get_data(ipc);
- spin_lock_irqsave(&sdev->ipc_lock, flags);
+ guard(spinlock_irqsave)(&sdev->ipc_lock);
snd_sof_ipc_process_reply(sdev, 0);
- spin_unlock_irqrestore(&sdev->ipc_lock, flags);
}
static void imx_handle_request(struct imx_dsp_ipc *ipc)
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 54cd3807f8c6..e31f4c4061d8 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -319,6 +319,7 @@ config SND_SOC_SOF_NOVALAKE
config SND_SOC_SOF_HDA_COMMON
tristate
+ select SND_HDA_EXT_CORE
config SND_SOC_SOF_HDA_GENERIC
tristate
diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c
index 0d364bcdcfa9..32bf5e5e5978 100644
--- a/sound/soc/sof/intel/atom.c
+++ b/sound/soc/sof/intel/atom.c
@@ -143,9 +143,6 @@ irqreturn_t atom_irq_thread(int irq, void *context)
/* reply message from DSP */
if (ipcx & SHIM_BYT_IPCX_DONE) {
-
- spin_lock_irq(&sdev->ipc_lock);
-
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@@ -153,11 +150,9 @@ irqreturn_t atom_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_process_reply(sdev, ipcx);
-
atom_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
}
/* new message from DSP */
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index f1287d509835..9534d18be97d 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -315,9 +315,6 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR,
SHIM_IMRX, SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
-
- spin_lock_irq(&sdev->ipc_lock);
-
/*
* handle immediate reply from DSP core. If the msg is
* found, set done bit in cmd_done which is called at the
@@ -325,11 +322,9 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_process_reply(sdev, ipcx);
-
bdw_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
}
ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD);
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 0cc5725515e7..69376fb5b20d 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -69,13 +69,10 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context)
data->primary = primary;
data->extension = extension;
- spin_lock_irq(&sdev->ipc_lock);
-
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_get_reply(sdev);
cnl_ipc_host_done(sdev);
snd_sof_ipc_reply(sdev, data->primary);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev,
"IPC reply before FW_READY: %#x|%#x\n",
@@ -141,15 +138,11 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context)
CNL_DSP_REG_HIPCCTL_DONE, 0);
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
- spin_lock_irq(&sdev->ipc_lock);
-
/* handle immediate reply from DSP core */
+ guard(spinlock_irq)(&sdev->ipc_lock);
hda_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg);
-
cnl_ipc_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
msg);
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 37674ea452d6..fd371850b0d6 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -451,7 +451,6 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS_GPL(hda_codec_i915_exit, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915");
-MODULE_SOFTDEP("pre: snd-hda-codec-hdmi");
#endif
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index 92681ca7f24d..b2c559559962 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -58,7 +58,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
return NULL;
}
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
list_for_each_entry(hstream, &bus->stream_list, list) {
struct hdac_ext_stream *hext_stream =
stream_to_hdac_ext_stream(hstream);
@@ -110,7 +110,6 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
res->link_locked = 1;
res->link_substream = substream;
}
- spin_unlock_irq(&bus->reg_lock);
return res;
}
@@ -311,7 +310,7 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
if (pipe_widget->instance_id < 0)
return 0;
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -323,16 +322,16 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
- goto out;
+ return ret;
pipeline->state = SOF_IPC4_PIPE_PAUSED;
+
break;
default:
dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
ret = -EINVAL;
}
-out:
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
return ret;
}
@@ -388,7 +387,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
if (pipe_widget->instance_id < 0)
return 0;
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -396,14 +395,16 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
- goto out;
+ return ret;
+
pipeline->state = SOF_IPC4_PIPE_PAUSED;
}
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_RUNNING);
if (ret < 0)
- goto out;
+ return ret;
+
pipeline->state = SOF_IPC4_PIPE_RUNNING;
swidget->spipe->started_count++;
break;
@@ -411,7 +412,8 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_RUNNING);
if (ret < 0)
- goto out;
+ return ret;
+
pipeline->state = SOF_IPC4_PIPE_RUNNING;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -429,8 +431,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
ret = -EINVAL;
break;
}
-out:
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
return ret;
}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 883d0d3bae9e..15faedeec16d 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -70,12 +70,22 @@ static const struct hda_dai_widget_dma_ops *
hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
- struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_widget *swidget;
struct snd_sof_dev *sdev;
struct snd_sof_dai *sdai;
- sdev = widget_to_sdev(w);
+ /*
+ * this is unlikely if the topology and the machine driver DAI links match.
+ * But if there's a missing DAI link in topology, this will prevent a NULL pointer
+ * dereference later on.
+ */
+ if (!w) {
+ dev_err(cpu_dai->dev, "%s: widget is NULL\n", __func__);
+ return NULL;
+ }
+ sdev = widget_to_sdev(w);
+ swidget = w->dobj.private;
if (!swidget) {
dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
return NULL;
@@ -856,6 +866,14 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 4,
},
},
+{
+ /* Virtual CPU DAI for Echo reference */
+ .name = "Loopback Virtual Pin",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
{
.name = "iDisp1 Pin",
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 94425c510861..2aef3954f4f7 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -204,13 +204,10 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context)
data->primary = primary;
data->extension = extension;
- spin_lock_irq(&sdev->ipc_lock);
-
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_get_reply(sdev);
hda_dsp_ipc_host_done(sdev);
snd_sof_ipc_reply(sdev, data->primary);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev,
"IPC reply before FW_READY: %#x|%#x\n",
@@ -289,16 +286,12 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
* reply.
*/
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
- spin_lock_irq(&sdev->ipc_lock);
-
/* handle immediate reply from DSP core */
+ guard(spinlock_irq)(&sdev->ipc_lock);
hda_dsp_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg);
-
/* set the done bit */
hda_dsp_ipc_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n",
msg);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 2cc11d8b0f70..2b3abcf75d55 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -53,65 +53,8 @@ hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size,
struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
bool is_iccmax)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct hdac_ext_stream *hext_stream;
- struct hdac_stream *hstream;
- int ret;
-
- hext_stream = hda_dsp_stream_get(sdev, direction, 0);
-
- if (!hext_stream) {
- dev_err(sdev->dev, "error: no stream available\n");
- return ERR_PTR(-ENODEV);
- }
- hstream = &hext_stream->hstream;
- hstream->substream = NULL;
-
- /*
- * Allocate DMA buffer if it is temporary or if the buffer is intended
- * to be persistent but not yet allocated.
- * We cannot rely solely on !dmab->area as caller might use a struct on
- * stack (when it is temporary) without clearing it to 0.
- */
- if (!persistent_buffer || !dmab->area) {
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
- __func__, ret);
- goto out_put;
- }
- }
-
- hstream->period_bytes = 0;/* initialize period_bytes */
- hstream->format_val = format;
- hstream->bufsize = size;
-
- if (is_iccmax) {
- ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
- if (ret < 0) {
- dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
- goto out_free;
- }
- } else {
- ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
- if (ret < 0) {
- dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
- goto out_free;
- }
- hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
- }
-
- return hext_stream;
-
-out_free:
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
- dmab->bytes = 0;
- hstream->bufsize = 0;
- hstream->format_val = 0;
-out_put:
- hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
- return ERR_PTR(ret);
+ return hda_data_stream_prepare(dev, format, size, dmab, persistent_buffer,
+ direction, is_iccmax, false);
}
EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
@@ -275,38 +218,7 @@ EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
bool persistent_buffer, struct hdac_ext_stream *hext_stream)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct hdac_stream *hstream = &hext_stream->hstream;
- int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret = 0;
-
- if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
- ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
- else
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
- SOF_HDA_SD_CTL_DMA_START, 0);
-
- hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
- hstream->running = 0;
- hstream->substream = NULL;
-
- /* reset BDL address */
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
- sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
-
- snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
-
- if (!persistent_buffer) {
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
- dmab->bytes = 0;
- hstream->bufsize = 0;
- hstream->format_val = 0;
- }
-
- return ret;
+ return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false);
}
EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index ce561fe52bd5..6f15213937a3 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -524,11 +524,8 @@ void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, boo
hlink = &h2link->hext_link;
- mutex_lock(&h2link->eml_lock);
-
- hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
}
EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, "SND_SOC_SOF_HDA_MLINK");
@@ -837,11 +834,8 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
hlink = &h2link->hext_link;
- mutex_lock(&h2link->eml_lock);
-
- hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
return 0;
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, "SND_SOC_SOF_HDA_MLINK");
@@ -875,12 +869,8 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
lchan = 0;
}
- mutex_lock(&h2link->eml_lock);
-
- hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
- stream_id, dir);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir);
val = readw(pcmsycm);
@@ -1012,11 +1002,8 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e
hlink = &h2link->hext_link;
- mutex_lock(&h2link->eml_lock);
-
- hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
-
- mutex_unlock(&h2link->eml_lock);
+ scoped_guard(mutex, &h2link->eml_lock)
+ hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
return 0;
}
diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c
index e45dd051ab8c..728ffe7ae54d 100644
--- a/sound/soc/sof/intel/hda-sdw-bpt.c
+++ b/sound/soc/sof/intel/hda-sdw-bpt.c
@@ -98,6 +98,17 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **
struct hdac_ext_stream *bpt_stream;
unsigned int format = HDA_CL_STREAM_FORMAT;
+ if (!sdev->dspless_mode_selected) {
+ int ret;
+
+ /*
+ * Make sure that the DSP is booted up, which might not be the
+ * case if the on-demand DSP boot is used
+ */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (ret)
+ return ret;
+ }
/*
* the baseline format needs to be adjusted to
* bandwidth requirements
@@ -107,7 +118,8 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **
dev_dbg(dev, "direction %d format_val %#x\n", direction, format);
- bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false);
+ bpt_stream = hda_data_stream_prepare(dev, format, bpt_num_bytes, dmab_bdl,
+ false, direction, false, true);
if (IS_ERR(bpt_stream)) {
dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n",
__func__, direction);
@@ -151,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
u32 mask;
int ret;
- ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream);
+ ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true);
if (ret < 0) {
dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n",
__func__);
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 9c3b3a9aaf83..1c04b5d9c0d8 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -210,8 +210,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
}
/* get next unused stream */
-struct hdac_ext_stream *
-hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
+static struct hdac_ext_stream *
+_hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags, bool pair)
{
const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -233,7 +233,14 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
if (hda_stream->host_reserved)
continue;
+ if (pair && hext_stream->link_locked)
+ continue;
+
s->opened = true;
+
+ if (pair)
+ hext_stream->link_locked = true;
+
break;
}
}
@@ -264,14 +271,27 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
return hext_stream;
}
+struct hdac_ext_stream *
+hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
+{
+ return _hda_dsp_stream_get(sdev, direction, flags, false);
+}
+
+struct hdac_ext_stream *
+hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags)
+{
+ return _hda_dsp_stream_get(sdev, direction, flags, true);
+}
+
/* free a stream */
-int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
+static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag, bool pair)
{
const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream;
+ struct hdac_ext_stream *link_stream;
struct hdac_stream *s;
bool dmi_l1_enable = true;
bool found = false;
@@ -292,6 +312,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
if (s->direction == direction && s->stream_tag == stream_tag) {
s->opened = false;
found = true;
+ if (pair)
+ link_stream = hext_stream;
} else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
dmi_l1_enable = false;
}
@@ -312,9 +334,22 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
return -ENODEV;
}
+ if (pair)
+ snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+
return 0;
}
+int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
+{
+ return _hda_dsp_stream_put(sdev, direction, stream_tag, false);
+}
+
+int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
+{
+ return _hda_dsp_stream_put(sdev, direction, stream_tag, true);
+}
+
static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
{
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
@@ -724,12 +759,12 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct hdac_bus *bus = sof_to_bus(sdev);
u32 mask = BIT(hstream->index);
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
+
/* couple host and link DMA if link DMA channel is idle */
if (!hext_stream->link_locked)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
SOF_HDA_REG_PP_PPCTL, mask, 0);
- spin_unlock_irq(&bus->reg_lock);
}
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
@@ -747,7 +782,7 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
u32 status;
/* The function can be called at irq thread, so use spin_lock_irq */
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
@@ -757,8 +792,6 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
if (status != 0xffffffff)
ret = true;
- spin_unlock_irq(&bus->reg_lock);
-
return ret;
}
EXPORT_SYMBOL_NS(hda_dsp_check_stream_irq, "SND_SOC_SOF_INTEL_HDA_COMMON");
@@ -842,7 +875,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
* unsolicited responses from the codec
*/
for (i = 0, active = true; i < 10 && active; i++) {
- spin_lock_irq(&bus->reg_lock);
+ guard(spinlock_irq)(&bus->reg_lock);
status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
@@ -853,7 +886,6 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
if (status & AZX_INT_CTRL_EN) {
active |= hda_codec_check_rirb_status(sdev);
}
- spin_unlock_irq(&bus->reg_lock);
}
return IRQ_HANDLED;
@@ -1211,3 +1243,119 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
return ((u64)ldp_u << 32) | ldp_l;
}
EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+struct hdac_ext_stream *
+hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size,
+ struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
+ bool is_iccmax, bool pair)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
+ int ret;
+
+ if (pair)
+ hext_stream = hda_dsp_stream_pair_get(sdev, direction, 0);
+ else
+ hext_stream = hda_dsp_stream_get(sdev, direction, 0);
+
+ if (!hext_stream) {
+ dev_err(sdev->dev, "%s: no stream available\n", __func__);
+ return ERR_PTR(-ENODEV);
+ }
+ hstream = &hext_stream->hstream;
+ hstream->substream = NULL;
+
+ /*
+ * Allocate DMA buffer if it is temporary or if the buffer is intended
+ * to be persistent but not yet allocated.
+ * We cannot rely solely on !dmab->area as caller might use a struct on
+ * stack (when it is temporary) without clearing it to 0.
+ */
+ if (!persistent_buffer || !dmab->area) {
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
+ __func__, ret);
+ goto out_put;
+ }
+ }
+
+ hstream->period_bytes = 0; /* initialize period_bytes */
+ hstream->format_val = format;
+ hstream->bufsize = size;
+
+ if (is_iccmax) {
+ ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: iccmax stream prepare failed: %d\n",
+ __func__, ret);
+ goto out_free;
+ }
+ } else {
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret);
+ goto out_free;
+ }
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
+ }
+
+ return hext_stream;
+
+out_free:
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+out_put:
+ if (pair)
+ hda_dsp_stream_pair_put(sdev, direction, hstream->stream_tag);
+ else
+ hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct hdac_stream *hstream = hdac_stream(hext_stream);
+ int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ int ret = 0;
+
+ if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
+ else
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
+ SOF_HDA_SD_CTL_DMA_START, 0);
+
+ if (pair)
+ hda_dsp_stream_pair_put(sdev, hstream->direction, hstream->stream_tag);
+ else
+ hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
+
+ hstream->running = 0;
+ hstream->substream = NULL;
+
+ /* reset BDL address */
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
+ sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
+
+ snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
+
+ if (!persistent_buffer) {
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(hda_data_stream_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 686ecc040867..c0cc7d3ce526 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -482,7 +482,7 @@ static int mclk_id_override = -1;
module_param_named(mclk_id, mclk_id_override, int, 0444);
MODULE_PARM_DESC(mclk_id, "SOF SSP mclk_id");
-static int bt_link_mask_override;
+static int bt_link_mask_override = -1;
module_param_named(bt_link_mask, bt_link_mask_override, int, 0444);
MODULE_PARM_DESC(bt_link_mask, "SOF BT offload link mask");
@@ -1138,6 +1138,12 @@ static bool is_endpoint_present(struct sdw_slave *sdw_device,
{
int i;
+ /* If SDCA is not present, assume the endpoint is present */
+ if (!sdw_device->sdca_data.interface_revision) {
+ dev_warn(&sdw_device->dev, "SDCA properties not found in BIOS\n");
+ return true;
+ }
+
for (i = 0; i < sdw_device->sdca_data.num_functions; i++) {
if (dai_type == dai_info->dais[i].dai_type)
return true;
@@ -1531,7 +1537,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
mach->mach_params.bt_link_mask);
/* allow for module parameter override */
- if (bt_link_mask_override) {
+ if (bt_link_mask_override != -1) {
dev_dbg(sdev->dev, "overriding BT link detected in NHLT tables %#x by kernel param %#x\n",
mach->mach_params.bt_link_mask, bt_link_mask_override);
mach->mach_params.bt_link_mask = bt_link_mask_override;
@@ -1622,7 +1628,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER &&
mach->mach_params.i2s_link_mask) {
int ssp_num;
- int mclk_mask;
if (hweight_long(mach->mach_params.i2s_link_mask) > 1 &&
!(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB))
@@ -1647,19 +1652,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
sof_pdata->tplg_filename = tplg_filename;
- mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num);
-
- if (mclk_mask < 0) {
- dev_err(sdev->dev, "Invalid MCLK configuration\n");
- return NULL;
- }
-
- dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask);
-
- if (mclk_mask) {
- dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask);
- sdev->mclk_id_override = true;
- sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1;
+ if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) {
+ int mclk_mask = check_nhlt_ssp_mclk_mask(sdev,
+ ssp_num);
+
+ if (mclk_mask < 0) {
+ dev_err(sdev->dev,
+ "Invalid MCLK configuration for SSP%d\n",
+ ssp_num);
+ return NULL;
+ }
+
+ if (mclk_mask) {
+ sdev->mclk_id_override = true;
+ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1;
+ dev_info(sdev->dev,
+ "SSP%d to use MCLK id %d (mask: %#x)\n",
+ ssp_num, sdev->mclk_id_quirk, mclk_mask);
+ } else {
+ dev_dbg(sdev->dev,
+ "MCLK mask is empty for SSP%d in NHLT\n",
+ ssp_num);
+ }
}
}
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 562fe8be79c1..3f0966477ace 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -418,10 +418,10 @@
(HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl))
/* Number of DAIs */
-#define SOF_SKL_NUM_DAIS_NOCODEC 8
+#define SOF_SKL_NUM_DAIS_NOCODEC 9
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
-#define SOF_SKL_NUM_DAIS 15
+#define SOF_SKL_NUM_DAIS 16
#else
#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC
#endif
@@ -694,7 +694,10 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
+struct hdac_ext_stream *
+ hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags);
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
+int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
struct hdac_ext_stream *hext_stream,
int enable, u32 size);
@@ -902,6 +905,14 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai);
+struct hdac_ext_stream *
+hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size,
+ struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
+ bool is_iccmax, bool pair);
+
+int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair);
+
/* common dai driver */
extern struct snd_soc_dai_driver skl_dai[];
int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);
@@ -936,6 +947,7 @@ extern const struct sof_intel_dsp_desc arl_s_chip_info;
extern const struct sof_intel_dsp_desc lnl_chip_info;
extern const struct sof_intel_dsp_desc ptl_chip_info;
extern const struct sof_intel_dsp_desc wcl_chip_info;
+extern const struct sof_intel_dsp_desc nvl_chip_info;
extern const struct sof_intel_dsp_desc nvl_s_chip_info;
/* Probes support */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 095dcf1a18e4..4ac81537ca05 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -596,13 +596,10 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context)
data->primary = primary;
data->extension = extension;
- spin_lock_irq(&sdev->ipc_lock);
-
+ guard(spinlock_irq)(&sdev->ipc_lock);
snd_sof_ipc_get_reply(sdev);
mtl_ipc_host_done(sdev);
snd_sof_ipc_reply(sdev, data->primary);
-
- spin_unlock_irq(&sdev->ipc_lock);
} else {
dev_dbg_ratelimited(sdev->dev,
"IPC reply before FW_READY: %#x|%#x\n",
diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c
index ff215151af2a..0d763998558f 100644
--- a/sound/soc/sof/intel/nvl.c
+++ b/sound/soc/sof/intel/nvl.c
@@ -26,6 +26,30 @@ int sof_nvl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
};
EXPORT_SYMBOL_NS(sof_nvl_set_ops, "SND_SOC_SOF_INTEL_NVL");
+const struct sof_intel_dsp_desc nvl_chip_info = {
+ .cores_num = 4,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_4_0,
+};
+
const struct sof_intel_dsp_desc nvl_s_chip_info = {
.cores_num = 2,
.init_core_mask = BIT(0),
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
index 0bf7ee753bc3..3241403efa60 100644
--- a/sound/soc/sof/intel/pci-apl.c
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -86,7 +86,7 @@ static const struct sof_dev_desc glk_desc = {
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_APL, &bxt_desc) },
- { PCI_DEVICE_DATA(INTEL, HDA_GML, &glk_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_GLK, &glk_desc) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sof_pci_ids);
diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c
index ae379c23f008..acb4429df9ec 100644
--- a/sound/soc/sof/intel/pci-lnl.c
+++ b/sound/soc/sof/intel/pci-lnl.c
@@ -40,6 +40,7 @@ static const struct sof_dev_desc lnl_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl",
},
diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c
index c499c14b93d5..bb3c29ef5477 100644
--- a/sound/soc/sof/intel/pci-nvl.c
+++ b/sound/soc/sof/intel/pci-nvl.c
@@ -26,6 +26,36 @@ static int sof_nvl_ops_init(struct snd_sof_dev *sdev)
return sof_nvl_set_ops(sdev, &sof_nvl_ops);
}
+static const struct sof_dev_desc nvl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_nvl_machines,
+ .alt_machines = snd_soc_acpi_intel_nvl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &nvl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/nvl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-nvl.ri",
+ },
+ .nocodec_tplg_filename = "sof-nvl-nocodec.tplg",
+ .ops = &sof_nvl_ops,
+ .ops_init = sof_nvl_ops_init,
+};
+
static const struct sof_dev_desc nvl_s_desc = {
.use_acpi_target_states = true,
.machines = snd_soc_acpi_intel_nvl_machines,
@@ -38,6 +68,7 @@ static const struct sof_dev_desc nvl_s_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/nvl-s",
},
@@ -57,6 +88,7 @@ static const struct sof_dev_desc nvl_s_desc = {
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
+ { PCI_DEVICE_DATA(INTEL, HDA_NVL, &nvl_desc) }, /* NVL */
{ PCI_DEVICE_DATA(INTEL, HDA_NVL_S, &nvl_s_desc) }, /* NVL-S */
{ 0, }
};
diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c
index 68f6a9841633..9cb785ef763f 100644
--- a/sound/soc/sof/intel/pci-ptl.c
+++ b/sound/soc/sof/intel/pci-ptl.c
@@ -38,6 +38,7 @@ static const struct sof_dev_desc ptl_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/ptl",
},
@@ -67,6 +68,7 @@ static const struct sof_dev_desc wcl_desc = {
.ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
.ipc_default = SOF_IPC_TYPE_4,
.dspless_mode_supported = true,
+ .on_demand_dsp_boot = true,
.default_fw_path = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/wcl",
},
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 3fb8d3e9dc6a..e6d8894b8ef6 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -47,7 +47,7 @@ int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
* The spin-lock is needed to protect message objects against other
* atomic contexts.
*/
- spin_lock_irq(&sdev->ipc_lock);
+ guard(spinlock_irq)(&sdev->ipc_lock);
/* initialise the message */
msg = &ipc->msg;
@@ -66,8 +66,6 @@ int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
if (!ret)
msg->ipc_complete = false;
- spin_unlock_irq(&sdev->ipc_lock);
-
return ret;
}
@@ -225,9 +223,8 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev)
return;
/* disable sending of ipc's */
- mutex_lock(&ipc->tx_mutex);
- ipc->disable_ipc_tx = true;
- mutex_unlock(&ipc->tx_mutex);
+ scoped_guard(mutex, &ipc->tx_mutex)
+ ipc->disable_ipc_tx = true;
if (ipc->ops->exit)
ipc->ops->exit(sdev);
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index 6ec391fd39a9..50700f5cb0ef 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -171,7 +171,12 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
dev_err(sdev->dev, "enabling device failed: %d\n", ret);
goto error;
}
- ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
+
+ /* Make sure the DSP/firmware is booted up */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (!ret)
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
+
pm_runtime_put_autosuspend(sdev->dev);
error:
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index f449362a2905..743f42fb26c0 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2427,9 +2427,9 @@ static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_
/* Do not free widgets for static pipelines with FW older than SOF2.2 */
if (!verify && !swidget->dynamic_pipeline_widget &&
SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
- mutex_lock(&swidget->setup_mutex);
- swidget->use_count = 0;
- mutex_unlock(&swidget->setup_mutex);
+ scoped_guard(mutex, &swidget->setup_mutex)
+ swidget->use_count = 0;
+
if (swidget->spipe)
swidget->spipe->complete = 0;
continue;
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 4a194a705ace..85bb22bbe18d 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -378,7 +378,7 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
}
/* Serialise IPC TX */
- mutex_lock(&ipc->tx_mutex);
+ guard(mutex)(&ipc->tx_mutex);
ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
@@ -405,8 +405,6 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
}
}
- mutex_unlock(&ipc->tx_mutex);
-
return ret;
}
@@ -477,7 +475,7 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da
memcpy(cdata_chunk, cdata, hdr_bytes);
/* Serialise IPC TX */
- mutex_lock(&sdev->ipc->tx_mutex);
+ guard(mutex)(&ipc->tx_mutex);
/* copy the payload data in a loop */
for (i = 0; i < num_msg; i++) {
@@ -511,8 +509,6 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da
sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes);
}
- mutex_unlock(&sdev->ipc->tx_mutex);
-
kfree(cdata_chunk);
return ret;
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 976a4794d610..596c3d77a34e 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -66,7 +66,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
* configuration
*/
memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
- scontrol->max_size);
+ scontrol->size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
/* Send the last known good configuration to firmware */
@@ -284,6 +284,106 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
kfree(data);
}
+static int
+sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc4_control_msg_payload *msg_data;
+ struct sof_abi_hdr *data = cdata->data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+ size_t data_size;
+ int ret;
+
+ data_size = struct_size(msg_data, data, data->size);
+ msg_data = kzalloc(data_size, GFP_KERNEL);
+ if (!msg_data)
+ return -ENOMEM;
+
+ msg_data->id = cdata->index;
+ msg_data->num_elems = data->size;
+ memcpy(msg_data->data, data->data, data->size);
+
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+
+ msg->data_ptr = msg_data;
+ msg->data_size = data_size;
+
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
+ msg->data_ptr = NULL;
+ msg->data_size = 0;
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: Failed to set control update for %s\n",
+ __func__, scontrol->name);
+
+ kfree(msg_data);
+
+ return ret;
+}
+
+static int
+sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc4_control_msg_payload *msg_data;
+ struct sof_abi_hdr *data = cdata->data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+ size_t data_size;
+ int ret = 0;
+
+ if (!scontrol->comp_data_dirty)
+ return 0;
+
+ if (!pm_runtime_active(scomp->dev))
+ return 0;
+
+ data_size = scontrol->max_size - sizeof(*data);
+ if (data_size < sizeof(*msg_data))
+ data_size = sizeof(*msg_data);
+
+ msg_data = kzalloc(data_size, GFP_KERNEL);
+ if (!msg_data)
+ return -ENOMEM;
+
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+
+ msg_data->id = cdata->index;
+ msg_data->num_elems = 0; /* ignored for bytes */
+
+ msg->data_ptr = msg_data;
+ msg->data_size = data_size;
+
+ scontrol->comp_data_dirty = false;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, lock);
+ if (!ret) {
+ if (msg->data_size > scontrol->max_size - sizeof(*data)) {
+ dev_err(scomp->dev,
+ "%s: no space for data in %s (%zu, %zu)\n",
+ __func__, scontrol->name, msg->data_size,
+ scontrol->max_size - sizeof(*data));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ data->size = msg->data_size;
+ scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
+ memcpy(data->data, msg->data_ptr, data->size);
+ } else {
+ dev_err(scomp->dev, "Failed to read control data for %s\n",
+ scontrol->name);
+ scontrol->comp_data_dirty = true;
+ }
+
+out:
+ msg->data_ptr = NULL;
+ msg->data_size = 0;
+
+ kfree(msg_data);
+
+ return ret;
+}
+
static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -412,19 +512,42 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
int ret = 0;
/* Send the new data to the firmware only if it is powered up */
- if (set && !pm_runtime_active(sdev->dev))
- return 0;
+ if (set) {
+ if (!pm_runtime_active(sdev->dev))
+ return 0;
+
+ if (!data->size) {
+ dev_dbg(sdev->dev, "%s: No data to be sent.\n",
+ scontrol->name);
+ return 0;
+ }
+ }
+
+ if (data->type == SOF_IPC4_BYTES_CONTROL_PARAM_ID) {
+ if (set)
+ return sof_ipc4_set_bytes_control_data(scontrol, lock);
+ else
+ return sof_ipc4_refresh_bytes_control(scontrol, lock);
+ }
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
msg->data_ptr = data->data;
- msg->data_size = data->size;
+ if (set)
+ msg->data_size = data->size;
+ else
+ msg->data_size = scontrol->max_size - sizeof(*data);
ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);
- if (ret < 0)
+ if (ret < 0) {
dev_err(sdev->dev, "Failed to %s for %s\n",
set ? "set bytes update" : "get bytes",
scontrol->name);
+ } else if (!set) {
+ /* Update the sizes according to the received payload data */
+ data->size = msg->data_size;
+ scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
+ }
msg->data_ptr = NULL;
msg->data_size = 0;
@@ -440,6 +563,7 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->data;
size_t size;
+ int ret;
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(scomp->dev,
@@ -461,9 +585,12 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
- sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+ ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+ if (!ret)
+ /* Update the cdata size */
+ scontrol->size = sizeof(*cdata) + size;
- return 0;
+ return ret;
}
static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
@@ -487,6 +614,8 @@ static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
return -EINVAL;
}
+ sof_ipc4_refresh_bytes_control(scontrol, true);
+
size = data->size + sizeof(*data);
/* copy back to kcontrol */
@@ -559,7 +688,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
if (!scontrol->old_ipc_control_data) {
/* Create a backup of the current, valid bytes control */
scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,
- scontrol->max_size, GFP_KERNEL);
+ scontrol->size, GFP_KERNEL);
if (!scontrol->old_ipc_control_data)
return -ENOMEM;
}
@@ -567,12 +696,15 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
/* Copy the whole binary data which includes the ABI header and the payload */
if (copy_from_user(data, tlvd->tlv, header.length)) {
memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,
- scontrol->max_size);
+ scontrol->size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
return -EFAULT;
}
+ /* Update the cdata size */
+ scontrol->size = sizeof(*cdata) + header.length;
+
return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
}
@@ -638,6 +770,8 @@ static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
const unsigned int __user *binary_data,
unsigned int size)
{
+ sof_ipc4_refresh_bytes_control(scontrol, true);
+
return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
}
@@ -691,6 +825,9 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
case SOF_IPC4_ENUM_CONTROL_PARAM_ID:
type = SND_SOC_TPLG_TYPE_ENUM;
break;
+ case SOF_IPC4_BYTES_CONTROL_PARAM_ID:
+ type = SND_SOC_TPLG_TYPE_BYTES;
+ break;
default:
dev_err(sdev->dev,
"%s: Invalid control type for module %u.%u: %u\n",
@@ -741,23 +878,38 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
* The message includes the updated value/data, update the
* control's local cache using the received notification
*/
- for (i = 0; i < msg_data->num_elems; i++) {
- u32 channel = msg_data->chanv[i].channel;
+ if (type == SND_SOC_TPLG_TYPE_BYTES) {
+ struct sof_abi_hdr *data = cdata->data;
- if (channel >= scontrol->num_channels) {
+ if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) {
dev_warn(sdev->dev,
- "Invalid channel index for %s: %u\n",
- scontrol->name, i);
-
- /*
- * Mark the scontrol as dirty to force a refresh
- * on next read
- */
- scontrol->comp_data_dirty = true;
- break;
+ "%s: no space for data in %s (%u, %zu)\n",
+ __func__, scontrol->name, msg_data->num_elems,
+ scontrol->max_size - sizeof(*data));
+ } else {
+ memcpy(data->data, msg_data->data, msg_data->num_elems);
+ data->size = msg_data->num_elems;
+ scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
+ }
+ } else {
+ for (i = 0; i < msg_data->num_elems; i++) {
+ u32 channel = msg_data->chanv[i].channel;
+
+ if (channel >= scontrol->num_channels) {
+ dev_warn(sdev->dev,
+ "Invalid channel index for %s: %u\n",
+ scontrol->name, i);
+
+ /*
+ * Mark the scontrol as dirty to force a refresh
+ * on next read
+ */
+ scontrol->comp_data_dirty = true;
+ break;
+ }
+
+ cdata->chanv[channel].value = msg_data->chanv[i].value;
}
-
- cdata->chanv[channel].value = msg_data->chanv[i].value;
}
} else {
/*
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index b0d293f62d1c..07a78cb3c25c 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -175,7 +175,7 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id,
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_fw_library *fw_lib;
ssize_t payload_offset;
- int ret, i, err;
+ int ret, i;
if (!ipc4_data->load_library) {
dev_err(sdev->dev, "Library loading is not supported on this platform\n");
@@ -223,24 +223,7 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id,
for (i = 0; i < fw_lib->num_modules; i++)
fw_lib->modules[i].man4_module_entry.id |= (lib_id << SOF_IPC4_MOD_LIB_ID_SHIFT);
- /*
- * Make sure that the DSP is booted and stays up while attempting the
- * loading the library for the first time
- */
- ret = pm_runtime_resume_and_get(sdev->dev);
- if (ret < 0 && ret != -EACCES) {
- dev_err_ratelimited(sdev->dev, "%s: pm_runtime resume failed: %d\n",
- __func__, ret);
- goto release;
- }
-
ret = ipc4_data->load_library(sdev, fw_lib, false);
-
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n",
- __func__, err);
-
if (ret)
goto release;
diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c
index aa5b78604db6..cfd999b42458 100644
--- a/sound/soc/sof/ipc4-mtrace.c
+++ b/sound/soc/sof/ipc4-mtrace.c
@@ -118,22 +118,19 @@ static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file)
struct sof_mtrace_core_data *core_data = inode->i_private;
int ret;
- mutex_lock(&core_data->buffer_lock);
+ guard(mutex)(&core_data->buffer_lock);
- if (core_data->log_buffer) {
- ret = -EBUSY;
- goto out;
- }
+ if (core_data->log_buffer)
+ return -EBUSY;
ret = debugfs_file_get(file->f_path.dentry);
if (unlikely(ret))
- goto out;
+ return ret;
core_data->log_buffer = kmalloc(SOF_IPC4_DEBUG_SLOT_SIZE, GFP_KERNEL);
if (!core_data->log_buffer) {
debugfs_file_put(file->f_path.dentry);
- ret = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
ret = simple_open(inode, file);
@@ -142,9 +139,6 @@ static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file)
debugfs_file_put(file->f_path.dentry);
}
-out:
- mutex_unlock(&core_data->buffer_lock);
-
return ret;
}
@@ -281,10 +275,10 @@ static int sof_ipc4_mtrace_dfs_release(struct inode *inode, struct file *file)
debugfs_file_put(file->f_path.dentry);
- mutex_lock(&core_data->buffer_lock);
- kfree(core_data->log_buffer);
- core_data->log_buffer = NULL;
- mutex_unlock(&core_data->buffer_lock);
+ scoped_guard(mutex, &core_data->buffer_lock) {
+ kfree(core_data->log_buffer);
+ core_data->log_buffer = NULL;
+ }
return 0;
}
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 6d81969e181c..c3337c3f08c1 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -487,7 +487,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
return -ENOMEM;
}
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
/*
* IPC4 requires pipelines to be triggered in order starting at the sink and
@@ -580,7 +580,6 @@ skip_pause_transition:
}
free:
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
kfree(trigger_list);
kfree(pipe_priority);
return ret;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index d621e7914a73..622bffb50a1c 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -76,6 +76,10 @@ static const struct sof_topology_token ipc4_sched_tokens[] = {
offsetof(struct sof_ipc4_pipeline, core_id)},
{SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct sof_ipc4_pipeline, priority)},
+ {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pipeline, direction)},
+ {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct sof_ipc4_pipeline, direction_valid)},
};
static const struct sof_topology_token pipeline_tokens[] = {
@@ -158,6 +162,12 @@ static const struct sof_topology_token comp_ext_tokens[] = {
offsetof(struct snd_sof_widget, core)},
{SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain,
offsetof(struct snd_sof_widget, comp_domain)},
+ {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, dp_domain_id)},
+ {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, dp_heap_bytes)},
+ {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, dp_stack_bytes)},
};
static const struct sof_topology_token gain_tokens[] = {
@@ -933,6 +943,10 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
swidget->core = pipeline->core_id;
spipe->core_mask |= BIT(pipeline->core_id);
+ if (pipeline->direction_valid) {
+ spipe->direction = pipeline->direction;
+ spipe->direction_valid = true;
+ }
if (pipeline->use_chain_dma) {
dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
@@ -948,9 +962,9 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
goto err;
}
- dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",
+ dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d direction %d\n",
swidget->widget->name, swidget->pipeline_id,
- pipeline->priority, pipeline->core_id, pipeline->lp_mode);
+ pipeline->priority, pipeline->core_id, pipeline->lp_mode, pipeline->direction);
swidget->private = pipeline;
@@ -1998,6 +2012,25 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
return ret;
}
+static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ struct snd_sof_platform_stream_params *platform_params)
+{
+ struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ u32 host_dma_id = platform_params->stream_tag - 1;
+
+ if (pipeline->use_chain_dma) {
+ pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK;
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
+ return;
+ }
+
+ copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
+ copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id);
+}
+
static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
@@ -2720,12 +2753,14 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
int input_fmt_index = 0;
int ret;
- input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
- &process->base_config,
- pipeline_params,
- available_fmt);
- if (input_fmt_index < 0)
- return input_fmt_index;
+ if (available_fmt->num_input_formats) {
+ input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
+ &process->base_config,
+ pipeline_params,
+ available_fmt);
+ if (input_fmt_index < 0)
+ return input_fmt_index;
+ }
/* Configure output audio format only if the module supports output */
if (available_fmt->num_output_formats) {
@@ -2734,12 +2769,28 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
u32 out_ref_rate, out_ref_channels;
int out_ref_valid_bits, out_ref_type;
- in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
+ if (available_fmt->num_input_formats) {
+ in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
- out_ref_rate = in_fmt->sampling_frequency;
- out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
- out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
- out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
+ out_ref_rate = in_fmt->sampling_frequency;
+ out_ref_channels =
+ SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+ out_ref_valid_bits =
+ SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+ out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
+ } else {
+ /* for modules without input formats, use FE params as reference */
+ out_ref_rate = params_rate(fe_params);
+ out_ref_channels = params_channels(fe_params);
+ ret = sof_ipc4_get_sample_type(sdev, fe_params);
+ if (ret < 0)
+ return ret;
+ out_ref_type = (u32)ret;
+
+ out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
+ if (out_ref_valid_bits < 0)
+ return out_ref_valid_bits;
+ }
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&process->base_config,
@@ -2767,6 +2818,16 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
if (ret)
return ret;
}
+
+ /* set base cfg to match the first output format if there are no input formats */
+ if (!available_fmt->num_input_formats) {
+ struct sof_ipc4_audio_format *out_fmt;
+
+ out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
+
+ /* copy output format */
+ memcpy(&process->base_config.audio_fmt, out_fmt, sizeof(*out_fmt));
+ }
}
sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt,
@@ -2870,22 +2931,41 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
struct sof_ipc4_msg *msg;
int ret;
- if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) {
- dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n",
+ /*
+ * The max_size is coming from topology and indicates the maximum size
+ * of sof_abi_hdr plus the payload, which excludes the local only
+ * 'struct sof_ipc4_control_data'
+ */
+ if (scontrol->max_size < sizeof(struct sof_abi_hdr)) {
+ dev_err(sdev->dev,
+ "insufficient maximum size for a bytes control %s: %zu.\n",
scontrol->name, scontrol->max_size);
return -EINVAL;
}
- if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) {
- dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n",
- scontrol->name, scontrol->priv_size,
- scontrol->max_size - sizeof(*control_data));
+ if (scontrol->priv_size > scontrol->max_size) {
+ dev_err(sdev->dev,
+ "bytes control %s initial data size %zu exceeds max %zu.\n",
+ scontrol->name, scontrol->priv_size, scontrol->max_size);
return -EINVAL;
}
- scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size;
+ if (scontrol->priv_size < sizeof(struct sof_abi_hdr)) {
+ dev_err(sdev->dev,
+ "bytes control %s initial data size %zu is insufficient.\n",
+ scontrol->name, scontrol->priv_size);
+ return -EINVAL;
+ }
- scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
+ /*
+ * The used size behind the cdata pointer, which can be smaller than
+ * the maximum size
+ */
+ scontrol->size = sizeof(*control_data) + scontrol->priv_size;
+
+ /* Allocate the cdata: local struct size + maximum payload size */
+ scontrol->ipc_control_data = kzalloc(sizeof(*control_data) + scontrol->max_size,
+ GFP_KERNEL);
if (!scontrol->ipc_control_data)
return -ENOMEM;
@@ -2920,6 +3000,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(control_data->data->type);
return 0;
@@ -2948,6 +3029,77 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
return 0;
}
+static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget,
+ struct sof_ipc4_msg *msg,
+ void *ipc_data, u32 ipc_size,
+ void **new_data)
+{
+ struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data;
+ struct sof_ipc4_module_init_ext_init *ext_init;
+ struct sof_ipc4_module_init_ext_object *hdr;
+ int new_size;
+ u32 *payload;
+ u32 ext_pos;
+
+ /* For the moment the only reason for adding init_ext_init payload is DP
+ * memory data. If both stack and heap size are 0 (= use default), then
+ * there is no need for init_ext_init payload.
+ */
+ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) {
+ msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK;
+ return 0;
+ }
+
+ payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ /* Add ext_init first and set objects array flag to 1 */
+ ext_init = (struct sof_ipc4_module_init_ext_init *)payload;
+ ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK;
+ ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32));
+
+ /* Add object array objects after ext_init */
+
+ /* Add dp_memory_data if comp_domain indicates DP */
+ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) {
+ hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos];
+ hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK |
+ SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) |
+ SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data),
+ sizeof(u32)));
+ ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32));
+ dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos];
+ dp_mem_data->domain_id = swidget->dp_domain_id;
+ dp_mem_data->stack_bytes = swidget->dp_stack_bytes;
+ dp_mem_data->heap_bytes = swidget->dp_heap_bytes;
+ ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32));
+ }
+
+ /* If another array object is added, remember clear previous OBJ_LAST bit */
+
+ /* Calculate final size and check that it fits to max payload size */
+ new_size = ext_pos * sizeof(u32) + ipc_size;
+ if (new_size > sdev->ipc->max_payload_size) {
+ dev_err(sdev->dev, "Max ipc payload size %zu exceeded: %u",
+ sdev->ipc->max_payload_size, new_size);
+ kfree(payload);
+ return -EINVAL;
+ }
+ *new_data = payload;
+
+ /* Copy module specific ipc_payload to end */
+ memcpy(&payload[ext_pos], ipc_data, ipc_size);
+
+ /* Update msg extension bits according to the payload changes */
+ msg->extension |= SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK;
+ msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK;
+ msg->extension |= SOF_IPC4_MOD_EXT_PARAM_SIZE(DIV_ROUND_UP(new_size, sizeof(u32)));
+
+ return new_size;
+}
+
static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
@@ -2955,6 +3107,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc4_pipeline *pipeline;
struct sof_ipc4_msg *msg;
void *ipc_data = NULL;
+ void *ext_data = NULL;
u32 ipc_size = 0;
int ret;
@@ -3099,6 +3252,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n",
swidget->widget->name, swidget->pipeline_id, module_id,
swidget->instance_id, swidget->core);
+
+ ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size,
+ &ext_data);
+ if (ret < 0)
+ goto fail;
+
+ if (ret > 0) {
+ ipc_size = ret;
+ ipc_data = ext_data;
+ }
} else {
dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n",
swidget->widget->name, swidget->pipeline_id,
@@ -3109,6 +3272,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
msg->data_ptr = ipc_data;
ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size);
+
+fail:
if (ret < 0) {
dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
@@ -3121,6 +3286,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
}
}
+ kfree(ext_data);
return ret;
}
@@ -3130,7 +3296,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret = 0;
- mutex_lock(&ipc4_data->pipeline_state_mutex);
+ guard(mutex)(&ipc4_data->pipeline_state_mutex);
/* freeing a pipeline frees all the widgets associated with it */
if (swidget->id == snd_soc_dapm_scheduler) {
@@ -3141,7 +3307,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
if (pipeline->use_chain_dma) {
dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
swidget->widget->name);
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
return 0;
}
@@ -3169,8 +3334,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
ida_free(&fw_module->m_ida, swidget->instance_id);
}
- mutex_unlock(&ipc4_data->pipeline_state_mutex);
-
return ret;
}
@@ -3821,4 +3984,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
.dai_get_param = sof_ipc4_dai_get_param,
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
.link_setup = sof_ipc4_link_setup,
+ .host_config = sof_ipc4_host_config,
};
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 191b51d97993..a289c1d8f3ff 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -150,6 +150,8 @@ struct sof_ipc4_copier_config_set_sink_format {
* @use_chain_dma: flag to indicate if the firmware shall use chained DMA
* @msg: message structure for pipeline
* @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
+ * @direction_valid: flag indicating if valid direction is set in topology
+ * @direction: pipeline direction set in topology if direction_valid is true
*/
struct sof_ipc4_pipeline {
uint32_t priority;
@@ -160,6 +162,8 @@ struct sof_ipc4_pipeline {
bool use_chain_dma;
struct sof_ipc4_msg msg;
bool skip_during_fe_trigger;
+ bool direction_valid;
+ u32 direction;
};
/**
@@ -368,19 +372,24 @@ struct sof_ipc4_control_data {
#define SOF_IPC4_SWITCH_CONTROL_PARAM_ID 200
#define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201
+#define SOF_IPC4_BYTES_CONTROL_PARAM_ID 202
/**
* struct sof_ipc4_control_msg_payload - IPC payload for kcontrol parameters
* @id: unique id of the control
- * @num_elems: Number of elements in the chanv array
+ * @num_elems: Number of elements in the chanv array or number of bytes in data
* @reserved: reserved for future use, must be set to 0
* @chanv: channel ID and value array
+ * @data: binary payload
*/
struct sof_ipc4_control_msg_payload {
uint16_t id;
uint16_t num_elems;
uint32_t reserved[4];
- DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv);
+ union {
+ DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv);
+ DECLARE_FLEX_ARRAY(uint8_t, data);
+ };
} __packed;
/**
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index a4a090e6724a..c9c6c0c52c62 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -15,6 +15,7 @@
#include "sof-audio.h"
#include "ipc4-fw-reg.h"
#include "ipc4-priv.h"
+#include "ipc4-topology.h"
#include "ipc4-telemetry.h"
#include "ops.h"
@@ -411,7 +412,7 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
}
/* Serialise IPC TX */
- mutex_lock(&ipc->tx_mutex);
+ guard(mutex)(&ipc->tx_mutex);
ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
@@ -428,11 +429,27 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
}
- mutex_unlock(&ipc->tx_mutex);
-
return ret;
}
+static bool sof_ipc4_tx_payload_for_get_data(struct sof_ipc4_msg *tx)
+{
+ /*
+ * Messages that require TX payload with LARGE_CONFIG_GET.
+ * The TX payload is placed into the IPC message data section by caller,
+ * which needs to be copied to temporary buffer since the received data
+ * will overwrite it.
+ */
+ switch (tx->extension & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) {
+ case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID):
+ case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID):
+ case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_BYTES_CONTROL_PARAM_ID):
+ return true;
+ default:
+ return false;
+ }
+}
+
static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
size_t payload_bytes, bool set)
{
@@ -444,6 +461,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
struct sof_ipc4_msg tx = {{ 0 }};
struct sof_ipc4_msg rx = {{ 0 }};
size_t remaining = payload_bytes;
+ void *tx_payload_for_get = NULL;
+ size_t tx_data_size = 0;
size_t offset = 0;
size_t chunk_size;
int ret;
@@ -469,13 +488,23 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+ if (sof_ipc4_tx_payload_for_get_data(&tx)) {
+ tx_data_size = min(ipc4_msg->data_size, payload_limit);
+ tx_payload_for_get = kmemdup(ipc4_msg->data_ptr, tx_data_size,
+ GFP_KERNEL);
+ if (!tx_payload_for_get)
+ return -ENOMEM;
+ }
+
/* ensure the DSP is in D0i0 before sending IPC */
ret = snd_sof_dsp_set_power_state(sdev, &target_state);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(tx_payload_for_get);
return ret;
+ }
/* Serialise IPC TX */
- mutex_lock(&sdev->ipc->tx_mutex);
+ guard(mutex)(&sdev->ipc->tx_mutex);
do {
size_t tx_size, rx_size;
@@ -506,7 +535,15 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
rx.data_size = chunk_size;
rx.data_ptr = ipc4_msg->data_ptr + offset;
- tx_size = 0;
+ if (tx_payload_for_get) {
+ tx_size = tx_data_size;
+ tx.data_size = tx_size;
+ tx.data_ptr = tx_payload_for_get;
+ } else {
+ tx_size = 0;
+ tx.data_size = 0;
+ tx.data_ptr = NULL;
+ }
rx_size = chunk_size;
}
@@ -551,7 +588,7 @@ out:
if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD))
sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
- mutex_unlock(&sdev->ipc->tx_mutex);
+ kfree(tx_payload_for_get);
return ret;
}
@@ -892,6 +929,19 @@ void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state)
struct sof_ipc4_msg msg;
u32 data = state;
+ /*
+ * The mic privacy change notification's role is to notify the running
+ * firmware that there is a change in mic privacy state from whatever
+ * the state was before - since the firmware booted up or since the
+ * previous change during runtime.
+ *
+ * If the firmware has not been booted up, there is no need to send
+ * change notification (the firmware is not booted up).
+ * The firmware checks the current state during its boot.
+ */
+ if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
+ return;
+
msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c
index 01bbadb160ff..75b4af4b5111 100644
--- a/sound/soc/sof/mediatek/mtk-adsp-common.c
+++ b/sound/soc/sof/mediatek/mtk-adsp-common.c
@@ -107,11 +107,9 @@ EXPORT_SYMBOL(mtk_adsp_send_msg);
void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc)
{
struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc);
- unsigned long flags;
- spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
+ guard(spinlock_irqsave)(&priv->sdev->ipc_lock);
snd_sof_ipc_process_reply(priv->sdev, 0);
- spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
EXPORT_SYMBOL(mtk_adsp_handle_reply);
diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c
index bd52e7ec6883..74c04dcf4167 100644
--- a/sound/soc/sof/ops.c
+++ b/sound/soc/sof/ops.c
@@ -38,13 +38,8 @@ bool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset,
bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset,
u32 mask, u32 value)
{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
- change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
- return change;
+ guard(spinlock_irqsave)(&sdev->hw_lock);
+ return snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value);
}
EXPORT_SYMBOL(snd_sof_pci_update_bits);
@@ -90,28 +85,16 @@ EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked);
bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset,
u32 mask, u32 value)
{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
- change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask,
- value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
- return change;
+ guard(spinlock_irqsave)(&sdev->hw_lock);
+ return snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, value);
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits);
bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset,
u64 mask, u64 value)
{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
- change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask,
- value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
- return change;
+ guard(spinlock_irqsave)(&sdev->hw_lock);
+ return snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, value);
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits64);
@@ -134,11 +117,8 @@ void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar,
void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar,
u32 offset, u32 mask, u32 value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&sdev->hw_lock, flags);
+ guard(spinlock_irqsave)(&sdev->hw_lock);
snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value);
- spin_unlock_irqrestore(&sdev->hw_lock, flags);
}
EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced);
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index d73644e85b6e..72af1f4ff620 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -287,16 +287,12 @@ static inline int
snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev,
const struct sof_dsp_power_state *target_state)
{
- int ret = 0;
-
- mutex_lock(&sdev->power_state_access);
+ guard(mutex)(&sdev->power_state_access);
if (sof_ops(sdev)->set_power_state)
- ret = sof_ops(sdev)->set_power_state(sdev, target_state);
-
- mutex_unlock(&sdev->power_state_access);
+ return sof_ops(sdev)->set_power_state(sdev, target_state);
- return ret;
+ return 0;
}
/* debug */
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index cee04574264e..5b598d0940eb 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -88,9 +88,9 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
spcm->stream[dir].list = list;
- ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
+ ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
- spcm_err(spcm, dir, "Widget list set up failed\n");
+ spcm_err(spcm, dir, "widget list prepare failed\n");
spcm->stream[dir].list = NULL;
snd_soc_dapm_dai_free_widgets(&list);
return ret;
@@ -100,15 +100,30 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
return 0;
}
+static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev,
+ int comp_id)
+{
+ struct snd_sof_widget *swidget;
+
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (comp_id == swidget->comp_id)
+ return swidget;
+ }
+
+ return NULL;
+}
+
static int sof_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
- struct snd_sof_platform_stream_params platform_params = { 0 };
+ struct snd_sof_platform_stream_params *platform_params;
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_sof_widget *host_widget;
struct snd_sof_pcm *spcm;
int ret;
@@ -122,6 +137,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
spcm_dbg(spcm, substream->stream, "Entry: hw_params\n");
+ if (!sdev->dspless_mode_selected) {
+ /*
+ * Make sure that the DSP is booted up, which might not be the
+ * case if the on-demand DSP boot is used
+ */
+ ret = snd_sof_boot_dsp_firmware(sdev);
+ if (ret)
+ return ret;
+ }
+
/*
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
@@ -134,7 +159,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
spcm->prepared[substream->stream] = false;
}
- ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
+ platform_params = &spcm->platform_params[substream->stream];
+ ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params);
if (ret < 0) {
spcm_err(spcm, substream->stream, "platform hw params failed\n");
return ret;
@@ -142,12 +168,27 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
/* if this is a repeated hw_params without hw_free, skip setting up widgets */
if (!spcm->stream[substream->stream].list) {
- ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
+ ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, platform_params,
substream->stream);
if (ret < 0)
return ret;
}
+ if (!sdev->dspless_mode_selected) {
+ int host_comp_id = spcm->stream[substream->stream].comp_id;
+
+ host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id);
+ if (!host_widget) {
+ spcm_err(spcm, substream->stream,
+ "failed to find host widget with comp_id %d\n", host_comp_id);
+ return -EINVAL;
+ }
+
+ /* set the host DMA ID */
+ if (tplg_ops && tplg_ops->host_config)
+ tplg_ops->host_config(sdev, host_widget, platform_params);
+ }
+
/* create compressed page table for audio firmware */
if (runtime->buffer_changed) {
struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
@@ -159,14 +200,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- if (pcm_ops && pcm_ops->hw_params) {
- ret = pcm_ops->hw_params(component, substream, params, &platform_params);
- if (ret < 0)
- return ret;
- }
-
- spcm->prepared[substream->stream] = true;
-
/* save pcm hw_params */
memcpy(&spcm->params[substream->stream], params, sizeof(*params));
@@ -271,6 +304,9 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
+ /* unprepare and free the list of DAPM widgets */
+ sof_widget_list_unprepare(sdev, spcm, substream->stream);
+
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
return ret;
@@ -281,7 +317,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+ struct snd_sof_platform_stream_params *platform_params;
+ struct snd_soc_dapm_widget_list *list;
+ struct snd_pcm_hw_params *params;
struct snd_sof_pcm *spcm;
+ int dir = substream->stream;
int ret;
/* nothing to do for BE */
@@ -307,15 +348,33 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
return ret;
}
- /* set hw_params */
- ret = sof_pcm_hw_params(component,
- substream, &spcm->params[substream->stream]);
+ ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]);
if (ret < 0) {
spcm_err(spcm, substream->stream,
"failed to set hw_params after resume\n");
return ret;
}
+ list = spcm->stream[dir].list;
+ params = &spcm->params[substream->stream];
+ platform_params = &spcm->platform_params[substream->stream];
+ ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n",
+ spcm->pcm.pcm_id, dir);
+ spcm->stream[dir].list = NULL;
+ snd_soc_dapm_dai_free_widgets(&list);
+ return ret;
+ }
+
+ if (pcm_ops && pcm_ops->hw_params) {
+ ret = pcm_ops->hw_params(component, substream, params, platform_params);
+ if (ret < 0)
+ return ret;
+ }
+
+ spcm->prepared[substream->stream] = true;
+
return 0;
}
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8e3bcf602beb..dd7cd87f1fa5 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -8,10 +8,15 @@
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
+#include <linux/module.h>
#include "ops.h"
#include "sof-priv.h"
#include "sof-audio.h"
+static int override_on_demand_boot = -1;
+module_param_named(on_demand_boot, override_on_demand_boot, int, 0444);
+MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - enabled");
+
/*
* Helper function to determine the target DSP state during
* system suspend. This function only cares about the device
@@ -70,67 +75,28 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev)
}
#endif
-static int sof_resume(struct device *dev, bool runtime_resume)
+int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- u32 old_state = sdev->dsp_power_state.state;
int ret;
- /* do nothing if dsp resume callbacks are not set */
- if (!runtime_resume && !sof_ops(sdev)->resume)
- return 0;
-
- if (runtime_resume && !sof_ops(sdev)->runtime_resume)
- return 0;
-
- /* DSP was never successfully started, nothing to resume */
- if (sdev->first_boot)
- return 0;
-
- /*
- * if the runtime_resume flag is set, call the runtime_resume routine
- * or else call the system resume routine
- */
- if (runtime_resume)
- ret = snd_sof_dsp_runtime_resume(sdev);
- else
- ret = snd_sof_dsp_resume(sdev);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to power up DSP after resume\n");
- return ret;
- }
+ guard(mutex)(&sdev->dsp_fw_boot_mutex);
- if (sdev->dspless_mode_selected) {
- sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
+ /* Firmware already booted, just return */
return 0;
}
- /*
- * Nothing further to be done for platforms that support the low power
- * D0 substate. Resume trace and return when resuming from
- * low-power D0 substate
- */
- if (!runtime_resume && sof_ops(sdev)->set_power_state &&
- old_state == SOF_DSP_PM_D0) {
- ret = sof_fw_trace_resume(sdev);
- if (ret < 0)
- /* non fatal */
- dev_warn(sdev->dev,
- "failed to enable trace after resume %d\n", ret);
- return 0;
- }
+ dev_dbg(sdev->dev, "Booting DSP firmware\n");
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* load the firmware */
ret = snd_sof_load_firmware(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to load DSP firmware after resume %d\n",
- ret);
+ dev_err(sdev->dev, "%s: failed to load DSP firmware: %d\n",
+ __func__, ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
@@ -143,9 +109,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
*/
ret = snd_sof_run_firmware(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to boot DSP firmware after resume %d\n",
- ret);
+ dev_err(sdev->dev, "%s: failed to boot DSP firmware: %d\n",
+ __func__, ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
@@ -154,16 +119,16 @@ static int sof_resume(struct device *dev, bool runtime_resume)
ret = sof_fw_trace_resume(sdev);
if (ret < 0) {
/* non fatal */
- dev_warn(sdev->dev,
- "warning: failed to init trace after resume %d\n",
- ret);
+ dev_warn(sdev->dev, "%s: failed to resume trace: %d\n",
+ __func__, ret);
}
/* restore pipelines */
if (tplg_ops && tplg_ops->set_up_all_pipelines) {
ret = tplg_ops->set_up_all_pipelines(sdev, false);
if (ret < 0) {
- dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
+ dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n",
+ __func__, ret);
goto setup_fail;
}
}
@@ -175,7 +140,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
if (pm_ops && pm_ops->ctx_restore) {
ret = pm_ops->ctx_restore(sdev);
if (ret < 0)
- dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
+ dev_err(sdev->dev, "%s: ctx_restore IPC failed: %d\n",
+ __func__, ret);
}
setup_fail:
@@ -192,6 +158,73 @@ setup_fail:
return ret;
}
+EXPORT_SYMBOL(snd_sof_boot_dsp_firmware);
+
+static int sof_resume(struct device *dev, bool runtime_resume)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ u32 old_state = sdev->dsp_power_state.state;
+ bool on_demand_boot;
+ int ret;
+
+ /* do nothing if dsp resume callbacks are not set */
+ if (!runtime_resume && !sof_ops(sdev)->resume)
+ return 0;
+
+ if (runtime_resume && !sof_ops(sdev)->runtime_resume)
+ return 0;
+
+ /* DSP was never successfully started, nothing to resume */
+ if (sdev->first_boot)
+ return 0;
+
+ /*
+ * if the runtime_resume flag is set, call the runtime_resume routine
+ * or else call the system resume routine
+ */
+ if (runtime_resume)
+ ret = snd_sof_dsp_runtime_resume(sdev);
+ else
+ ret = snd_sof_dsp_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: failed to power up DSP after resume\n");
+ return ret;
+ }
+
+ if (sdev->dspless_mode_selected) {
+ sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ return 0;
+ }
+
+ /*
+ * Nothing further to be done for platforms that support the low power
+ * D0 substate. Resume trace and return when resuming from
+ * low-power D0 substate
+ */
+ if (!runtime_resume && sof_ops(sdev)->set_power_state &&
+ old_state == SOF_DSP_PM_D0) {
+ ret = sof_fw_trace_resume(sdev);
+ if (ret < 0)
+ /* non fatal */
+ dev_warn(sdev->dev,
+ "failed to enable trace after resume %d\n", ret);
+ return 0;
+ }
+
+ if (override_on_demand_boot > -1)
+ on_demand_boot = override_on_demand_boot ? true : false;
+ else
+ on_demand_boot = sdev->pdata->desc->on_demand_dsp_boot;
+
+ if (on_demand_boot) {
+ /* Only change the fw_state to PREPARE but skip booting */
+ sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
+ return 0;
+ }
+
+ return snd_sof_boot_dsp_firmware(sdev);
+}
static int sof_suspend(struct device *dev, bool runtime_suspend)
{
@@ -297,8 +330,12 @@ int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
{
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
- /* Notify DSP of upcoming power down */
- if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
+ /*
+ * Notify DSP of upcoming power down only if the firmware has been
+ * booted up
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE && sof_ops(sdev)->remove &&
+ pm_ops && pm_ops->ctx_save)
return pm_ops->ctx_save(sdev);
return 0;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index a9664b4cf43f..acf56607bc9c 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -13,6 +13,21 @@
#include "sof-audio.h"
#include "ops.h"
+/*
+ * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers
+ * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains
+ * 2 DAI's names alh-copier.SDW1.Playback.0 and alh-copier-SDW1.Playback.1. In this case, only the
+ * DAI alh-copier.SDW1.Playback.0 is set up in the firmware. The other DAI,
+ * alh-copier.SDW1.Playback.1 in topology is for the sake of completeness to show aggregation for
+ * the speaker amp and does not need any firmware configuration.
+ */
+static bool is_aggregated_dai(struct snd_sof_widget *swidget)
+{
+ return (WIDGET_IS_DAI(swidget->id) &&
+ isdigit(swidget->widget->name[strlen(swidget->widget->name) - 1]) &&
+ swidget->widget->name[strlen(swidget->widget->name) - 1] != '0');
+}
+
static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
const char *func)
{
@@ -121,13 +136,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- int ret;
-
- mutex_lock(&swidget->setup_mutex);
- ret = sof_widget_free_unlocked(sdev, swidget);
- mutex_unlock(&swidget->setup_mutex);
-
- return ret;
+ guard(mutex)(&swidget->setup_mutex);
+ return sof_widget_free_unlocked(sdev, swidget);
}
EXPORT_SYMBOL(sof_widget_free);
@@ -240,13 +250,8 @@ use_count_dec:
int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- int ret;
-
- mutex_lock(&swidget->setup_mutex);
- ret = sof_widget_setup_unlocked(sdev, swidget);
- mutex_unlock(&swidget->setup_mutex);
-
- return ret;
+ guard(mutex)(&swidget->setup_mutex);
+ return sof_widget_setup_unlocked(sdev, swidget);
}
EXPORT_SYMBOL(sof_widget_setup);
@@ -264,6 +269,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
is_virtual_widget(sdev, sink_widget->widget, __func__))
return 0;
+ /* skip route if source/sink widget is not set up */
+ if (!src_widget->use_count || !sink_widget->use_count)
+ return 0;
+
/* find route matching source and sink widgets */
list_for_each_entry(sroute, &sdev->route_list, list)
if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
@@ -292,10 +301,34 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
return 0;
}
+static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir)
+{
+ return swidget->spipe->direction == dir;
+}
+
+static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev,
+ struct snd_soc_dapm_widget *wsource,
+ struct snd_soc_dapm_widget *wsink)
+{
+ struct snd_sof_widget *src_widget = wsource->dobj.private;
+ struct snd_sof_widget *sink_widget = wsink->dobj.private;
+
+ /*
+ * skip setting up route if source and sink are in different directions (ex. playback and
+ * echo ref) if the direction is set in topology. These will be set up later. It is enough
+ * to check if the direction_valid is set for one of the widgets as all widgets will have
+ * the direction set in topology if one is set.
+ */
+ if (sink_widget->spipe && sink_widget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
+ return 0;
+
+ return sof_route_setup(sdev, wsource, wsink);
+}
+
static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget_list *list, int dir)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_dapm_widget *widget;
struct snd_sof_route *sroute;
struct snd_soc_dapm_path *p;
@@ -318,7 +351,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
continue;
if (p->sink->dobj.private) {
- ret = sof_route_setup(sdev, widget, p->sink);
+ ret = sof_set_up_same_dir_widget_routes(sdev, widget,
+ p->sink);
if (ret < 0)
return ret;
}
@@ -334,7 +368,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
continue;
if (p->source->dobj.private) {
- ret = sof_route_setup(sdev, p->source, widget);
+ ret = sof_set_up_same_dir_widget_routes(sdev, p->source,
+ widget);
if (ret < 0)
return ret;
}
@@ -350,7 +385,6 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
*/
list_for_each_entry(sroute, &sdev->route_list, list) {
bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
- struct snd_sof_widget *swidget;
if (sroute->setup)
continue;
@@ -359,41 +393,36 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
/*
- * if both source and sink are in the DAPM list, the route must already have been
- * set up above. And if neither are in the DAPM list, the route shouldn't be
- * handled now.
+ * no need to set up the route if both the source and sink widgets are not in the
+ * DAPM list
*/
- if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
+ if (!src_widget_in_dapm_list && !sink_widget_in_dapm_list)
continue;
/*
- * At this point either the source widget or the sink widget is in the DAPM list
- * with a route that might need to be set up. Check the use_count of the widget
- * that is not in the DAPM list to confirm if it is in use currently before setting
- * up the route.
+ * set up the route only if both the source and sink widgets are in the DAPM list
+ * but are in different directions. The ones in the same direction would already
+ * have been set up in the previous loop.
*/
- if (src_widget_in_dapm_list)
- swidget = sroute->sink_widget;
- else
- swidget = sroute->src_widget;
-
- mutex_lock(&swidget->setup_mutex);
- if (!swidget->use_count) {
- mutex_unlock(&swidget->setup_mutex);
- continue;
- }
+ if (src_widget_in_dapm_list && sink_widget_in_dapm_list) {
+ struct snd_sof_widget *src_widget, *sink_widget;
+
+ src_widget = sroute->src_widget->widget->dobj.private;
+ sink_widget = sroute->sink_widget->widget->dobj.private;
- if (tplg_ops && tplg_ops->route_setup) {
/*
- * this route will get freed when either the source widget or the sink
- * widget is freed during hw_free
+ * it is enough to check if the direction_valid is set for one of the
+ * widgets as all widgets will have the direction set in topology if one
+ * is set.
*/
- ret = tplg_ops->route_setup(sdev, sroute);
- if (!ret)
- sroute->setup = true;
+ if (src_widget && sink_widget &&
+ src_widget->spipe && src_widget->spipe->direction_valid &&
+ sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction))
+ continue;
}
- mutex_unlock(&swidget->setup_mutex);
+ ret = sof_route_setup(sdev, sroute->src_widget->widget,
+ sroute->sink_widget->widget);
if (ret < 0)
return ret;
@@ -404,7 +433,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
static void
sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
- struct snd_soc_dapm_widget_list *list)
+ struct snd_soc_dapm_widget_list *list, int dir)
{
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget = widget->dobj.private;
@@ -414,8 +443,15 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
if (is_virtual_widget(sdev, widget, __func__))
return;
- /* skip if the widget is in use or if it is already unprepared */
- if (!swidget || !swidget->prepared || swidget->use_count > 0)
+ if (!swidget)
+ goto sink_unprepare;
+
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return;
+
+ /* skip widgets in use, those already unprepared or aggregated DAIs */
+ if (!swidget->prepared || swidget->use_count > 0 || is_aggregated_dai(swidget))
goto sink_unprepare;
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
@@ -430,9 +466,10 @@ sink_unprepare:
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!widget_in_list(list, p->sink))
continue;
+
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
- sof_unprepare_widgets_in_path(sdev, p->sink, list);
+ sof_unprepare_widgets_in_path(sdev, p->sink, list, dir);
p->walking = false;
}
}
@@ -454,11 +491,20 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
if (is_virtual_widget(sdev, widget, __func__))
return 0;
+ if (!swidget)
+ goto sink_prepare;
+
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
if (!widget_ops)
return 0;
- if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return 0;
+
+ /* skip widgets already prepared or aggregated DAI widgets*/
+ if (!widget_ops[widget->id].ipc_prepare || swidget->prepared ||
+ is_aggregated_dai(swidget))
goto sink_prepare;
/* prepare the source widget */
@@ -476,6 +522,7 @@ sink_prepare:
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!widget_in_list(list, p->sink))
continue;
+
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
@@ -505,6 +552,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
int dir, struct snd_sof_pcm *spcm)
{
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
int err;
int ret = 0;
@@ -512,12 +560,21 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
if (is_virtual_widget(sdev, widget, __func__))
return 0;
- if (widget->dobj.private) {
- err = sof_widget_free(sdev, widget->dobj.private);
- if (err < 0)
- ret = err;
- }
+ if (!swidget)
+ goto sink_free;
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return 0;
+
+ /* skip aggregated DAIs */
+ if (is_aggregated_dai(swidget))
+ goto sink_free;
+
+ err = sof_widget_free(sdev, widget->dobj.private);
+ if (err < 0)
+ ret = err;
+sink_free:
/* free all widgets in the sink paths even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking) {
@@ -557,7 +614,15 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
if (swidget) {
int i;
- ret = sof_widget_setup(sdev, widget->dobj.private);
+ if (swidget->spipe && swidget->spipe->direction_valid &&
+ !sof_widget_in_same_direction(swidget, dir))
+ return 0;
+
+ /* skip aggregated DAIs */
+ if (is_aggregated_dai(swidget))
+ goto sink_setup;
+
+ ret = sof_widget_setup(sdev, swidget);
if (ret < 0)
return ret;
@@ -619,15 +684,13 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
for_each_dapm_widgets(list, i, widget) {
- if (is_virtual_widget(sdev, widget, __func__))
- continue;
-
- /* starting widget for playback is AIF type */
+ /* starting widget for playback is of AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
continue;
/* starting widget for capture is DAI type */
- if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)
+ if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out &&
+ widget->id != snd_soc_dapm_output)
continue;
switch (op) {
@@ -657,7 +720,7 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
break;
}
case SOF_WIDGET_UNPREPARE:
- sof_unprepare_widgets_in_path(sdev, widget, list);
+ sof_unprepare_widgets_in_path(sdev, widget, list, dir);
break;
default:
dev_err(sdev->dev, "Invalid widget op %d\n", op);
@@ -672,6 +735,30 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
}
+int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ int dir)
+{
+ /*
+ * Prepare widgets for set up. The prepare step is used to allocate memory, assign
+ * instance ID and pick the widget configuration based on the runtime PCM params.
+ */
+ return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
+ dir, SOF_WIDGET_PREPARE);
+}
+
+void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+
+ /* unprepare the widget */
+ sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+
+ snd_soc_dapm_dai_free_widgets(&list);
+ spcm->stream[dir].list = NULL;
+}
+
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
@@ -682,19 +769,10 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_soc_dapm_widget *widget;
int i, ret;
- /* nothing to set up */
- if (!list)
+ /* nothing to set up or setup has been already done */
+ if (!list || spcm->setup_done[dir])
return 0;
- /*
- * Prepare widgets for set up. The prepare step is used to allocate memory, assign
- * instance ID and pick the widget configuration based on the runtime PCM params.
- */
- ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
- dir, SOF_WIDGET_PREPARE);
- if (ret < 0)
- return ret;
-
/* Set up is used to send the IPC to the DSP to create the widget */
ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_SETUP);
@@ -749,6 +827,8 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
}
}
+ spcm->setup_done[dir] = true;
+
return 0;
widget_free:
@@ -766,18 +846,13 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
int ret;
/* nothing to free */
- if (!list)
+ if (!list || !spcm->setup_done[dir])
return 0;
/* send IPC to free widget in the DSP */
ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
- /* unprepare the widget */
- sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
-
- snd_soc_dapm_dai_free_widgets(&list);
- spcm->stream[dir].list = NULL;
-
+ spcm->setup_done[dir] = false;
pipeline_list->count = 0;
return ret;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index a8b93a2eec9c..36082e764bf9 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -209,6 +209,7 @@ struct sof_ipc_tplg_widget_ops {
* @widget_setup: Function pointer for setting up setup in the DSP
* @widget_free: Function pointer for freeing widget in the DSP
* @dai_config: Function pointer for sending DAI config IPC to the DSP
+ * @host_config: Function pointer for setting the DMA ID for host widgets
* @dai_get_param: Function pointer for getting the DAI parameter
* @set_up_all_pipelines: Function pointer for setting up all topology pipelines
* @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
@@ -230,6 +231,8 @@ struct sof_ipc_tplg_ops {
int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
unsigned int flags, struct snd_sof_dai_config_data *data);
+ void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ struct snd_sof_platform_stream_params *platform_params);
int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type);
int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
@@ -351,7 +354,9 @@ struct snd_sof_pcm {
struct snd_sof_pcm_stream stream[2];
struct list_head list; /* list in sdev pcm list */
struct snd_pcm_hw_params params[2];
+ struct snd_sof_platform_stream_params platform_params[2];
bool prepared[2]; /* PCM_PARAMS set successfully */
+ bool setup_done[2]; /* the setup of the SOF PCM device is done */
bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */
/* Must be last - ends in a flex-array member. */
@@ -454,6 +459,11 @@ struct snd_sof_widget {
/* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */
u32 comp_domain;
+ /* The values below are added to mod_init pay load if comp_domain indicates DP component */
+ u32 dp_domain_id; /* DP process userspace domain ID */
+ u32 dp_stack_bytes; /* DP process stack size requirement in bytes */
+ u32 dp_heap_bytes; /* DP process heap size requirement in bytes */
+
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_pipeline *spipe;
@@ -502,6 +512,9 @@ struct snd_sof_widget {
* @complete: flag used to indicate that pipeline set up is complete.
* @core_mask: Mask containing target cores for all modules in the pipeline
* @list: List item in sdev pipeline_list
+ * @direction_valid: flag indicating if the direction is set in topology
+ * @direction: pipeline direction set in topology, valid is direction_valid is true
+ *
*/
struct snd_sof_pipeline {
struct snd_sof_widget *pipe_widget;
@@ -510,6 +523,8 @@ struct snd_sof_pipeline {
int complete;
unsigned long core_mask;
struct list_head list;
+ bool direction_valid;
+ u32 direction;
};
/* ASoC SOF DAPM route */
@@ -668,6 +683,11 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
int dir);
+int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ int dir);
+void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
struct snd_sof_pcm *spcm);
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
index 373f3a125372..7b72d1c9c739 100644
--- a/sound/soc/sof/sof-client-ipc-flood-test.c
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -219,9 +219,10 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf
goto out;
}
- /* flood test */
- ret = sof_debug_ipc_flood_test(cdev, flood_duration_test,
- ipc_duration_ms, ipc_count);
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret)
+ ret = sof_debug_ipc_flood_test(cdev, flood_duration_test,
+ ipc_duration_ms, ipc_count);
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c
index 249bd2d6c8d2..d5984990098a 100644
--- a/sound/soc/sof/sof-client-ipc-kernel-injector.c
+++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c
@@ -63,7 +63,9 @@ static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __u
return ret;
}
- sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret)
+ sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
ret = pm_runtime_put_autosuspend(dev);
if (ret < 0)
diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c
index 9c8a0fbfb8df..c28f106de6ba 100644
--- a/sound/soc/sof/sof-client-ipc-msg-injector.c
+++ b/sound/soc/sof/sof-client-ipc-msg-injector.c
@@ -131,11 +131,15 @@ static int sof_msg_inject_send_message(struct sof_client_dev *cdev)
return ret;
}
- /* send the message */
- ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
- priv->max_msg_size);
- if (ret)
- dev_err(dev, "IPC message send failed: %d\n", ret);
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret) {
+ /* send the message */
+ ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer,
+ priv->rx_buffer,
+ priv->max_msg_size);
+ if (ret)
+ dev_err(dev, "IPC message send failed: %d\n", ret);
+ }
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index f753e0faff99..124f55508159 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -123,6 +123,10 @@ static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
if (ret)
return ret;
+ ret = sof_client_boot_dsp(cdev);
+ if (ret)
+ return ret;
+
ret = ipc->init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
if (ret < 0) {
dev_err(dai->dev, "Failed to init probe: %d\n", ret);
@@ -224,6 +228,10 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
goto exit;
}
+ ret = sof_client_boot_dsp(cdev);
+ if (ret)
+ goto pm_error;
+
ret = ipc->points_info(cdev, &desc, &num_desc, type);
if (ret < 0)
goto pm_error;
@@ -312,9 +320,12 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
goto exit;
}
- ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc));
- if (!ret)
- ret = count;
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret) {
+ ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc));
+ if (!ret)
+ ret = count;
+ }
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
@@ -367,9 +378,12 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
goto exit;
}
- ret = ipc->points_remove(cdev, &array[1], array[0]);
- if (!ret)
- ret = count;
+ ret = sof_client_boot_dsp(cdev);
+ if (!ret) {
+ ret = ipc->points_remove(cdev, &array[1], array[0]);
+ if (!ret)
+ ret = count;
+ }
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 2dbfc7699c73..38f1d7cec470 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -265,9 +265,8 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
}
/* add to list of SOF client devices */
- mutex_lock(&sdev->ipc_client_mutex);
- list_add(&centry->list, &sdev->ipc_client_list);
- mutex_unlock(&sdev->ipc_client_mutex);
+ scoped_guard(mutex, &sdev->ipc_client_mutex)
+ list_add(&centry->list, &sdev->ipc_client_list);
return 0;
@@ -285,7 +284,7 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i
{
struct sof_client_dev_entry *centry;
- mutex_lock(&sdev->ipc_client_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
/*
* sof_client_auxdev_release() will be invoked to free up memory
@@ -301,8 +300,6 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i
break;
}
}
-
- mutex_unlock(&sdev->ipc_client_mutex);
}
EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT");
@@ -400,7 +397,7 @@ int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
const struct auxiliary_driver *adrv;
struct sof_client_dev_entry *centry;
- mutex_lock(&sdev->ipc_client_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(centry, &sdev->ipc_client_list, list) {
struct sof_client_dev *cdev = &centry->client_dev;
@@ -414,8 +411,6 @@ int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
adrv->suspend(&cdev->auxdev, state);
}
- mutex_unlock(&sdev->ipc_client_mutex);
-
return 0;
}
EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT");
@@ -425,7 +420,7 @@ int sof_resume_clients(struct snd_sof_dev *sdev)
const struct auxiliary_driver *adrv;
struct sof_client_dev_entry *centry;
- mutex_lock(&sdev->ipc_client_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(centry, &sdev->ipc_client_list, list) {
struct sof_client_dev *cdev = &centry->client_dev;
@@ -439,8 +434,6 @@ int sof_resume_clients(struct snd_sof_dev *sdev)
adrv->resume(&cdev->auxdev);
}
- mutex_unlock(&sdev->ipc_client_mutex);
-
return 0;
}
EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT");
@@ -486,6 +479,12 @@ enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev)
}
EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT");
+int sof_client_boot_dsp(struct sof_client_dev *cdev)
+{
+ return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev));
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_boot_dsp, "SND_SOC_SOF_CLIENT");
+
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev)
{
@@ -526,14 +525,11 @@ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
return;
}
- mutex_lock(&sdev->client_event_handler_mutex);
-
+ guard(mutex)(&sdev->client_event_handler_mutex);
list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
if (event->ipc_msg_type == msg_type)
event->callback(event->cdev, msg_buf);
}
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
@@ -567,9 +563,8 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
event->callback = callback;
/* add to list of SOF client devices */
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->client_event_handler_mutex);
list_add(&event->list, &sdev->ipc_rx_handler_list);
- mutex_unlock(&sdev->client_event_handler_mutex);
return 0;
}
@@ -581,7 +576,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct sof_ipc_event_entry *event;
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
@@ -590,8 +585,6 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
break;
}
}
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT");
@@ -600,12 +593,10 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
{
struct sof_state_event_entry *event;
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(event, &sdev->fw_state_handler_list, list)
event->callback(event->cdev, sdev->fw_state);
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
@@ -625,9 +616,8 @@ int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
event->callback = callback;
/* add to list of SOF client devices */
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->client_event_handler_mutex);
list_add(&event->list, &sdev->fw_state_handler_list);
- mutex_unlock(&sdev->client_event_handler_mutex);
return 0;
}
@@ -638,7 +628,7 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
struct sof_state_event_entry *event;
- mutex_lock(&sdev->client_event_handler_mutex);
+ guard(mutex)(&sdev->ipc_client_mutex);
list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
if (event->cdev == cdev) {
@@ -647,8 +637,6 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
break;
}
}
-
- mutex_unlock(&sdev->client_event_handler_mutex);
}
EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, "SND_SOC_SOF_CLIENT");
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index 1a9015e38474..3b02506c03f1 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -50,6 +50,9 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev
size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev);
enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev);
+/* DSP/firmware boot request */
+int sof_client_boot_dsp(struct sof_client_dev *cdev);
+
/* module refcount management of SOF core */
int sof_client_core_module_get(struct sof_client_dev *cdev);
void sof_client_core_module_put(struct sof_client_dev *cdev);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 0f624d8cde20..693d063830fa 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -580,6 +580,8 @@ struct snd_sof_dev {
wait_queue_head_t boot_wait;
enum sof_fw_state fw_state;
bool first_boot;
+ /* mutex to protect DSP firmware boot (except initial, probe time boot */
+ struct mutex dsp_fw_boot_mutex;
/* work queue in case the probe is implemented in two steps */
struct work_struct probe_work;
@@ -703,6 +705,7 @@ int snd_sof_suspend(struct device *dev);
int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev);
int snd_sof_prepare(struct device *dev);
void snd_sof_complete(struct device *dev);
+int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev);
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sophgo/Kconfig b/sound/soc/sophgo/Kconfig
new file mode 100644
index 000000000000..9b454261bcfd
--- /dev/null
+++ b/sound/soc/sophgo/Kconfig
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# SoC audio configuration for cv1800b
+#
+
+menu "Sophgo"
+ depends on COMPILE_TEST || ARCH_SOPHGO
+
+config SND_SOC_CV1800B_TDM
+ tristate "Sophgo CV1800B I2S/TDM support"
+ depends on SND_SOC && OF
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ This option enables the I2S/TDM audio controller found in Sophgo
+ CV1800B / SG2002 SoCs. The controller supports standard I2S
+ audio modes for playback and capture.
+
+ The driver integrates with the ASoC framework and uses the DMA
+ engine for audio data transfer. It is intended to be configured
+ via Device Tree along with simple-audio-card module.
+
+ To compile the driver as a module, choose M here: the module will
+ be called cv1800b_tdm.
+
+config SND_SOC_CV1800B_ADC_CODEC
+ tristate "Sophgo CV1800B/SG2002 internal ADC codec"
+ depends on SND_SOC
+ help
+ This driver provides an ASoC codec DAI for capture and basic
+ control of the RXADC registers.
+
+ Say Y or M to build support for the Sophgo CV1800B
+ internal analog ADC codec block (RXADC).
+ The module will be called cv1800b-sound-adc
+
+config SND_SOC_CV1800B_DAC_CODEC
+ tristate "Sophgo CV1800B/SG2002 internal DAC codec"
+ depends on SND_SOC
+ help
+ This driver provides an ASoC codec DAI for playback and basic
+ control of the TXDAC registers.
+
+ Say Y or M to build support for the Sophgo CV1800B
+ internal analog DAC codec block (TXDAC).
+ The module will be called cv1800b-sound-dac
+
+endmenu
diff --git a/sound/soc/sophgo/Makefile b/sound/soc/sophgo/Makefile
new file mode 100644
index 000000000000..ec8dd31efddd
--- /dev/null
+++ b/sound/soc/sophgo/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+# Sophgo Platform Support
+obj-$(CONFIG_SND_SOC_CV1800B_TDM) += cv1800b-tdm.o
+obj-$(CONFIG_SND_SOC_CV1800B_ADC_CODEC) += cv1800b-sound-adc.o
+obj-$(CONFIG_SND_SOC_CV1800B_DAC_CODEC) += cv1800b-sound-dac.o
diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c
new file mode 100644
index 000000000000..b66761156b99
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-sound-adc.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal adc codec for cv1800b compatible SoC
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <sound/tlv.h>
+#include <sound/soc-component.h>
+#include <sound/control.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+
+#define CV1800B_RXADC_WORD_LEN 16
+#define CV1800B_RXADC_CHANNELS 2
+
+#define CV1800B_RXADC_CTRL0 0x00
+#define CV1800B_RXADCC_CTRL1 0x04
+#define CV1800B_RXADC_STATUS 0x08
+#define CV1800B_RXADC_CLK 0x0c
+#define CV1800B_RXADC_ANA0 0x10
+#define CV1800B_RXADC_ANA1 0x14
+#define CV1800B_RXADC_ANA2 0x18
+#define CV1800B_RXADC_ANA3 0x1c
+#define CV1800B_RXADC_ANA4 0x20
+
+/* CV1800B_RXADC_CTRL0 */
+#define REG_RXADC_EN GENMASK(0, 0)
+#define REG_I2S_TX_EN GENMASK(1, 1)
+
+/* CV1800B_RXADCC_CTRL1 */
+#define REG_RXADC_CIC_OPT GENMASK(1, 0)
+#define REG_RXADC_IGR_INIT GENMASK(8, 8)
+
+/* CV1800B_RXADC_ANA0 */
+#define REG_GSTEPL_RXPGA GENMASK(12, 0)
+#define REG_G6DBL_RXPGA GENMASK(13, 13)
+#define REG_GAINL_RXADC GENMASK(15, 14)
+#define REG_GSTEPR_RXPGA GENMASK(28, 16)
+#define REG_G6DBR_RXPGA GENMASK(29, 29)
+#define REG_GAINR_RXADC GENMASK(31, 30)
+#define REG_COMB_LEFT_VOLUME GENMASK(15, 0)
+#define REG_COMB_RIGHT_VOLUME GENMASK(31, 16)
+
+/* CV1800B_RXADC_ANA2 */
+#define REG_MUTEL_RXPGA GENMASK(0, 0)
+#define REG_MUTER_RXPGA GENMASK(1, 1)
+
+/* CV1800B_RXADC_CLK */
+#define REG_RXADC_CLK_INV GENMASK(0, 0)
+#define REG_RXADC_SCK_DIV GENMASK(15, 8)
+#define REG_RXADC_DLYEN GENMASK(23, 16)
+
+enum decimation_values {
+ DECIMATION_64 = 0,
+ DECIMATION_128,
+ DECIMATION_256,
+ DECIMATION_512,
+};
+
+static const u32 cv1800b_gains[] = {
+ 0x0001, /* 0dB */
+ 0x0002, /* 2dB */
+ 0x0004, /* 4dB */
+ 0x0008, /* 6dB */
+ 0x0010, /* 8dB */
+ 0x0020, /* 10dB */
+ 0x0040, /* 12dB */
+ 0x0080, /* 14dB */
+ 0x0100, /* 16dB */
+ 0x0200, /* 18dB */
+ 0x0400, /* 20dB */
+ 0x0800, /* 22dB */
+ 0x1000, /* 24dB */
+ 0x2400, /* 26dB */
+ 0x2800, /* 28dB */
+ 0x3000, /* 30dB */
+ 0x6400, /* 32dB */
+ 0x6800, /* 34dB */
+ 0x7000, /* 36dB */
+ 0xA400, /* 38dB */
+ 0xA800, /* 40dB */
+ 0xB000, /* 42dB */
+ 0xE400, /* 44dB */
+ 0xE800, /* 46dB */
+ 0xF000, /* 48dB */
+};
+
+struct cv1800b_priv {
+ void __iomem *regs;
+ struct device *dev;
+ unsigned int mclk_rate;
+};
+
+static int cv1800b_adc_setbclk_div(struct cv1800b_priv *priv, unsigned int rate)
+{
+ u32 val;
+ u32 bclk_div;
+ u64 tmp;
+
+ if (!priv->mclk_rate || !rate)
+ return -EINVAL;
+
+ tmp = div_u64(priv->mclk_rate, CV1800B_RXADC_WORD_LEN *
+ CV1800B_RXADC_CHANNELS * rate * 2);
+
+ if (!tmp) {
+ dev_err(priv->dev, "computed BCLK divider is zero\n");
+ return -EINVAL;
+ }
+
+ if (tmp > 256) {
+ dev_err(priv->dev, "BCLK divider %llu out of range\n", tmp);
+ return -EINVAL;
+ }
+
+ bclk_div = tmp - 1;
+ val = readl(priv->regs + CV1800B_RXADC_CLK);
+ val = u32_replace_bits(val, bclk_div, REG_RXADC_SCK_DIV);
+ /* Vendor value for 48kHz, tested on SG2000/SG2002 */
+ val = u32_replace_bits(val, 0x19, REG_RXADC_DLYEN);
+ writel(val, priv->regs + CV1800B_RXADC_CLK);
+
+ return 0;
+}
+
+static void cv1800b_adc_enable(struct cv1800b_priv *priv, bool enable)
+{
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_RXADC_CTRL0);
+ val = u32_replace_bits(val, enable, REG_RXADC_EN);
+ val = u32_replace_bits(val, enable, REG_I2S_TX_EN);
+ writel(val, priv->regs + CV1800B_RXADC_CTRL0);
+}
+
+static unsigned int cv1800b_adc_calc_db(u32 ana0, bool right)
+{
+ u32 step_mask = right ? FIELD_GET(REG_GSTEPR_RXPGA, ana0) :
+ FIELD_GET(REG_GSTEPL_RXPGA, ana0);
+ u32 coarse = right ? FIELD_GET(REG_GAINR_RXADC, ana0) :
+ FIELD_GET(REG_GAINL_RXADC, ana0);
+ bool g6db = right ? FIELD_GET(REG_G6DBR_RXPGA, ana0) :
+ FIELD_GET(REG_G6DBL_RXPGA, ana0);
+
+ u32 step = step_mask ? __ffs(step_mask) : 0;
+
+ step = min(step, 12U);
+ coarse = min(coarse, 3U);
+
+ return 2 * step + 6 * coarse + (g6db ? 6 : 0);
+}
+
+static int cv1800b_adc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ u32 val;
+ int ret;
+
+ ret = cv1800b_adc_setbclk_div(priv, rate);
+ if (ret) {
+ dev_err(priv->dev,
+ "could not set rate, check DT node for fixed clock\n");
+ return ret;
+ }
+
+ /* init adc */
+ val = readl(priv->regs + CV1800B_RXADCC_CTRL1);
+ val = u32_replace_bits(val, 1, REG_RXADC_IGR_INIT);
+ val = u32_replace_bits(val, DECIMATION_64, REG_RXADC_CIC_OPT);
+ writel(val, priv->regs + CV1800B_RXADCC_CTRL1);
+ return 0;
+}
+
+static int cv1800b_adc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cv1800b_adc_enable(priv, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cv1800b_adc_enable(priv, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cv1800b_adc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ priv->mclk_rate = freq;
+ dev_dbg(priv->dev, "mclk is set to %u\n", freq);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_adc_dai_ops = {
+ .hw_params = cv1800b_adc_hw_params,
+ .set_sysclk = cv1800b_adc_dai_set_sysclk,
+ .trigger = cv1800b_adc_dai_trigger,
+};
+
+static struct snd_soc_dai_driver cv1800b_adc_dai = {
+ .name = "adc-hifi",
+ .capture = { .stream_name = "ADC Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .ops = &cv1800b_adc_dai_ops,
+};
+
+static int cv1800b_adc_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cv1800b_priv *priv = snd_soc_component_get_drvdata(component);
+ u32 ana0 = readl(priv->regs + CV1800B_RXADC_ANA0);
+
+ unsigned int left = cv1800b_adc_calc_db(ana0, false);
+ unsigned int right = cv1800b_adc_calc_db(ana0, true);
+
+ ucontrol->value.integer.value[0] = min(left / 2, 24U);
+ ucontrol->value.integer.value[1] = min(right / 2, 24U);
+ return 0;
+}
+
+static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cv1800b_priv *priv = snd_soc_component_get_drvdata(component);
+
+ u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24);
+ u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24);
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_RXADC_ANA0);
+ val = u32_replace_bits(val, cv1800b_gains[v_left],
+ REG_COMB_LEFT_VOLUME);
+ val = u32_replace_bits(val, cv1800b_gains[v_right],
+ REG_COMB_RIGHT_VOLUME);
+ writel(val, priv->regs + CV1800B_RXADC_ANA0);
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new cv1800b_adc_controls[] = {
+ SOC_DOUBLE_EXT_TLV("Internal I2S Capture Volume", SND_SOC_NOPM, 0, 16, 24, false,
+ cv1800b_adc_volume_get, cv1800b_adc_volume_set,
+ cv1800b_volume_tlv),
+};
+
+static const struct snd_soc_component_driver cv1800b_adc_component = {
+ .name = "cv1800b-adc-codec",
+ .controls = cv1800b_adc_controls,
+ .num_controls = ARRAY_SIZE(cv1800b_adc_controls),
+};
+
+static int cv1800b_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ platform_set_drvdata(pdev, priv);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cv1800b_adc_component,
+ &cv1800b_adc_dai, 1);
+}
+
+static const struct of_device_id cv1800b_adc_of_match[] = {
+ { .compatible = "sophgo,cv1800b-sound-adc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cv1800b_adc_of_match);
+
+static struct platform_driver cv1800b_adc_driver = {
+ .probe = cv1800b_adc_probe,
+ .driver = {
+ .name = "cv1800b-sound-adc",
+ .of_match_table = cv1800b_adc_of_match,
+ },
+};
+
+module_platform_driver(cv1800b_adc_driver);
+
+MODULE_DESCRIPTION("ADC codec for CV1800B");
+MODULE_AUTHOR("Anton D. Stavinskii <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sophgo/cv1800b-sound-dac.c b/sound/soc/sophgo/cv1800b-sound-dac.c
new file mode 100644
index 000000000000..135322bcf6ad
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-sound-dac.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal DAC codec for cv1800b based CPUs
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include <linux/io.h>
+
+#define CV1800B_TXDAC_CTRL0 0x00
+#define CV1800B_TXDAC_CTRL1 0x04
+#define CV1800B_TXDAC_STATUS 0x08
+#define CV1800B_TXDAC_AFE0 0x0c
+#define CV1800B_TXDAC_AFE1 0x10
+#define CV1800B_TXDAC_ANA0 0x20
+#define CV1800B_TXDAC_ANA1 0x24
+#define CV1800B_TXDAC_ANA2 0x28
+
+/* cv1800b_TXDAC_CTRL0 */
+#define REG_TXDAC_EN GENMASK(0, 0)
+#define REG_I2S_RX_EN GENMASK(1, 1)
+
+/* cv1800b_TXDAC_CTRL1 */
+#define REG_TXDAC_CIC_OPT GENMASK(1, 0)
+
+/* cv1800b_TXDAC_AFE0 */
+#define REG_TXDAC_INIT_DLY_CNT GENMASK(5, 0)
+
+/* cv1800b_TXDAC_ANA2 */
+#define TXDAC_OW_VAL_L_MASK GENMASK(7, 0)
+#define TXDAC_OW_VAL_R_MASK GENMASK(15, 8)
+#define TXDAC_OW_EN_L_MASK GENMASK(16, 16)
+#define TXDAC_OW_EN_R_MASK GENMASK(17, 17)
+
+struct cv1800b_priv {
+ void __iomem *regs;
+ struct device *dev;
+};
+
+enum decimation_values {
+ DECIMATION_64 = 0,
+ DECIMATION_128,
+ DECIMATION_256,
+ DECIMATION_512,
+};
+
+static void cv1800b_dac_enable(struct cv1800b_priv *priv, bool enable)
+{
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_TXDAC_CTRL0);
+ val = u32_replace_bits(val, enable, REG_TXDAC_EN);
+ val = u32_replace_bits(val, enable, REG_I2S_RX_EN);
+ writel(val, priv->regs + CV1800B_TXDAC_CTRL0);
+}
+
+/*
+ * Control the DAC overwrite bits. When enabled, the DAC outputs the fixed
+ * overwrite value instead of samples from the I2S input.
+ */
+static void cv1800b_dac_mute(struct cv1800b_priv *priv, bool enable)
+{
+ u32 val;
+
+ val = readl(priv->regs + CV1800B_TXDAC_ANA2);
+ val = u32_replace_bits(val, enable, TXDAC_OW_EN_L_MASK);
+ val = u32_replace_bits(val, enable, TXDAC_OW_EN_R_MASK);
+ writel(val, priv->regs + CV1800B_TXDAC_ANA2);
+}
+
+static int cv1800b_dac_decimation(struct cv1800b_priv *priv, u8 dec)
+{
+ u32 val;
+
+ if (dec > 3)
+ return -EINVAL;
+
+ val = readl(priv->regs + CV1800B_TXDAC_CTRL1);
+ val = u32_replace_bits(val, dec, REG_TXDAC_CIC_OPT);
+ writel(val, priv->regs + CV1800B_TXDAC_CTRL1);
+ return 0;
+}
+
+static int cv1800b_dac_dly(struct cv1800b_priv *priv, u32 dly)
+{
+ u32 val;
+
+ if (dly > 63)
+ return -EINVAL;
+
+ val = readl(priv->regs + CV1800B_TXDAC_AFE0);
+ val = u32_replace_bits(val, dly, REG_TXDAC_INIT_DLY_CNT);
+ writel(val, priv->regs + CV1800B_TXDAC_AFE0);
+ return 0;
+}
+
+static int cv1800b_dac_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+ unsigned int rate = params_rate(params);
+
+ if (rate != 48000) {
+ dev_err(priv->dev, "rate %u is not supported\n", rate);
+ return -EINVAL;
+ }
+ /* Clear DAC overwrite so playback uses I2S data. */
+ cv1800b_dac_mute(priv, false);
+ /* minimal decimation for 48kHz is 64*/
+ ret = cv1800b_dac_decimation(priv, DECIMATION_64);
+ if (ret)
+ return ret;
+
+ /* value is taken from vendors driver 48kHz
+ * tested on sg2000 and sg2002.
+ */
+ ret = cv1800b_dac_dly(priv, 0x19);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cv1800b_dac_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ cv1800b_dac_enable(priv, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ cv1800b_dac_enable(priv, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_dac_dai_ops = {
+ .hw_params = cv1800b_dac_hw_params,
+ .trigger = cv1800b_dac_dai_trigger,
+};
+
+static struct snd_soc_dai_driver cv1800b_dac_dai = {
+ .name = "dac-hifi",
+ .playback = { .stream_name = "DAC Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .ops = &cv1800b_dac_dai_ops,
+};
+
+static const struct snd_soc_component_driver cv1800b_dac_component = {
+ .name = "cv1800b-dac-codec",
+};
+
+static int cv1800b_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ platform_set_drvdata(pdev, priv);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cv1800b_dac_component,
+ &cv1800b_dac_dai, 1);
+}
+
+static const struct of_device_id cv1800b_dac_of_match[] = {
+ { .compatible = "sophgo,cv1800b-sound-dac" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cv1800b_dac_of_match);
+
+static struct platform_driver cv1800b_dac_driver = {
+ .probe = cv1800b_dac_probe,
+ .driver = {
+ .name = "cv1800b-dac-codec",
+ .of_match_table = cv1800b_dac_of_match,
+ },
+};
+module_platform_driver(cv1800b_dac_driver);
+
+MODULE_DESCRIPTION("DAC codec for CV1800B");
+MODULE_AUTHOR("Anton D. Stavinskii <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c
new file mode 100644
index 000000000000..4cbac8c1160f
--- /dev/null
+++ b/sound/soc/sophgo/cv1800b-tdm.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/string.h>
+#include <linux/dev_printk.h>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/limits.h>
+#include <linux/overflow.h>
+
+#define TX_FIFO_SIZE (1024)
+#define RX_FIFO_SIZE (1024)
+#define TX_MAX_BURST (8)
+#define RX_MAX_BURST (8)
+
+#define CV1800B_DEF_FREQ 24576000
+#define CV1800B_DEF_MCLK_FS_RATIO 256
+
+/* tdm registers */
+#define CV1800B_BLK_MODE_SETTING 0x000
+#define CV1800B_FRAME_SETTING 0x004
+#define CV1800B_SLOT_SETTING1 0x008
+#define CV1800B_SLOT_SETTING2 0x00C
+#define CV1800B_DATA_FORMAT 0x010
+#define CV1800B_BLK_CFG 0x014
+#define CV1800B_I2S_ENABLE 0x018
+#define CV1800B_I2S_RESET 0x01C
+#define CV1800B_I2S_INT_EN 0x020
+#define CV1800B_I2S_INT 0x024
+#define CV1800B_FIFO_THRESHOLD 0x028
+#define CV1800B_LRCK_MASTER 0x02C /* special clock only mode */
+#define CV1800B_FIFO_RESET 0x030
+#define CV1800B_RX_STATUS 0x040
+#define CV1800B_TX_STATUS 0x048
+#define CV1800B_CLK_CTRL0 0x060
+#define CV1800B_CLK_CTRL1 0x064
+#define CV1800B_PCM_SYNTH 0x068
+#define CV1800B_RX_RD_PORT 0x080
+#define CV1800B_TX_WR_PORT 0x0C0
+
+/* CV1800B_BLK_MODE_SETTING (0x000) */
+#define BLK_TX_MODE_MASK GENMASK(0, 0)
+#define BLK_MASTER_MODE_MASK GENMASK(1, 1)
+#define BLK_DMA_MODE_MASK GENMASK(7, 7)
+
+/* CV1800B_CLK_CTRL1 (0x064) */
+#define CLK_MCLK_DIV_MASK GENMASK(15, 0)
+#define CLK_BCLK_DIV_MASK GENMASK(31, 16)
+
+/* CV1800B_CLK_CTRL0 (0x060) */
+#define CLK_AUD_CLK_SEL_MASK GENMASK(0, 0)
+#define CLK_BCLK_OUT_CLK_FORCE_EN_MASK GENMASK(6, 6)
+#define CLK_MCLK_OUT_EN_MASK GENMASK(7, 7)
+#define CLK_AUD_EN_MASK GENMASK(8, 8)
+
+/* CV1800B_I2S_RESET (0x01C) */
+#define RST_I2S_RESET_RX_MASK GENMASK(0, 0)
+#define RST_I2S_RESET_TX_MASK GENMASK(1, 1)
+
+/* CV1800B_FIFO_RESET (0x030) */
+#define FIFO_RX_RESET_MASK GENMASK(0, 0)
+#define FIFO_TX_RESET_MASK GENMASK(16, 16)
+
+/* CV1800B_I2S_ENABLE (0x018) */
+#define I2S_ENABLE_MASK GENMASK(0, 0)
+
+/* CV1800B_BLK_CFG (0x014) */
+#define BLK_AUTO_DISABLE_WITH_CH_EN_MASK GENMASK(4, 4)
+#define BLK_RX_BLK_CLK_FORCE_EN_MASK GENMASK(8, 8)
+#define BLK_RX_FIFO_DMA_CLK_FORCE_EN_MASK GENMASK(9, 9)
+#define BLK_TX_BLK_CLK_FORCE_EN_MASK GENMASK(16, 16)
+#define BLK_TX_FIFO_DMA_CLK_FORCE_EN_MASK GENMASK(17, 17)
+
+/* CV1800B_FRAME_SETTING (0x004) */
+#define FRAME_LENGTH_MASK GENMASK(8, 0)
+#define FS_ACTIVE_LENGTH_MASK GENMASK(23, 16)
+
+/* CV1800B_I2S_INT_EN (0x020) */
+#define INT_I2S_INT_EN_MASK GENMASK(8, 8)
+
+/* CV1800B_SLOT_SETTING2 (0x00C) */
+#define SLOT_EN_MASK GENMASK(15, 0)
+
+/* CV1800B_LRCK_MASTER (0x02C) */
+#define LRCK_MASTER_ENABLE_MASK GENMASK(0, 0)
+
+/* CV1800B_DATA_FORMAT (0x010) */
+#define DF_WORD_LENGTH_MASK GENMASK(2, 1)
+#define DF_TX_SOURCE_LEFT_ALIGN_MASK GENMASK(6, 6)
+
+/* CV1800B_FIFO_THRESHOLD (0x028) */
+#define FIFO_RX_THRESHOLD_MASK GENMASK(4, 0)
+#define FIFO_TX_THRESHOLD_MASK GENMASK(20, 16)
+#define FIFO_TX_HIGH_THRESHOLD_MASK GENMASK(28, 24)
+
+/* CV1800B_SLOT_SETTING1 (0x008) */
+#define SLOT_NUM_MASK GENMASK(3, 0)
+#define SLOT_SIZE_MASK GENMASK(13, 8)
+#define DATA_SIZE_MASK GENMASK(20, 16)
+#define FB_OFFSET_MASK GENMASK(28, 24)
+
+enum cv1800b_tdm_word_length {
+ CV1800B_WORD_LENGTH_8_BIT = 0,
+ CV1800B_WORD_LENGTH_16_BIT = 1,
+ CV1800B_WORD_LENGTH_32_BIT = 2,
+};
+
+struct cv1800b_i2s {
+ void __iomem *base;
+ struct clk *clk;
+ struct clk *sysclk;
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data playback_dma;
+ struct snd_dmaengine_dai_dma_data capture_dma;
+ u32 mclk_rate;
+ bool bclk_ratio_fixed;
+ u32 bclk_ratio;
+
+};
+
+static void cv1800b_setup_dma_struct(struct cv1800b_i2s *i2s,
+ phys_addr_t phys_base)
+{
+ i2s->playback_dma.addr = phys_base + CV1800B_TX_WR_PORT;
+ i2s->playback_dma.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->playback_dma.fifo_size = TX_FIFO_SIZE;
+ i2s->playback_dma.maxburst = TX_MAX_BURST;
+
+ i2s->capture_dma.addr = phys_base + CV1800B_RX_RD_PORT;
+ i2s->capture_dma.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ i2s->capture_dma.fifo_size = RX_FIFO_SIZE;
+ i2s->capture_dma.maxburst = RX_MAX_BURST;
+}
+
+static const struct snd_dmaengine_pcm_config cv1800b_i2s_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static void cv1800b_reset_fifo(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_FIFO_RESET);
+ val = u32_replace_bits(val, 1, FIFO_RX_RESET_MASK);
+ val = u32_replace_bits(val, 1, FIFO_TX_RESET_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_RESET);
+
+ usleep_range(10, 20);
+
+ val = readl(i2s->base + CV1800B_FIFO_RESET);
+ val = u32_replace_bits(val, 0, FIFO_RX_RESET_MASK);
+ val = u32_replace_bits(val, 0, FIFO_TX_RESET_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_RESET);
+}
+
+static void cv1800b_reset_i2s(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_I2S_RESET);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_RX_MASK);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_TX_MASK);
+ writel(val, i2s->base + CV1800B_I2S_RESET);
+
+ usleep_range(10, 20);
+
+ val = readl(i2s->base + CV1800B_I2S_RESET);
+ val = u32_replace_bits(val, 0, RST_I2S_RESET_RX_MASK);
+ val = u32_replace_bits(val, 0, RST_I2S_RESET_TX_MASK);
+ writel(val, i2s->base + CV1800B_I2S_RESET);
+}
+
+static void cv1800b_set_mclk_div(struct cv1800b_i2s *i2s, u32 mclk_div)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL1);
+ val = u32_replace_bits(val, mclk_div, CLK_MCLK_DIV_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL1);
+ dev_dbg(i2s->dev, "mclk_div is set to %u\n", mclk_div);
+}
+
+static void cv1800b_set_tx_mode(struct cv1800b_i2s *i2s, bool is_tx)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_BLK_MODE_SETTING);
+ val = u32_replace_bits(val, is_tx, BLK_TX_MODE_MASK);
+ writel(val, i2s->base + CV1800B_BLK_MODE_SETTING);
+ dev_dbg(i2s->dev, "tx_mode is set to %u\n", is_tx);
+}
+
+static int cv1800b_set_bclk_div(struct cv1800b_i2s *i2s, u32 bclk_div)
+{
+ u32 val;
+
+ if (bclk_div == 0 || bclk_div > 0xFFFF)
+ return -EINVAL;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL1);
+ val = u32_replace_bits(val, bclk_div, CLK_BCLK_DIV_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL1);
+ dev_dbg(i2s->dev, "bclk_div is set to %u\n", bclk_div);
+ return 0;
+}
+
+/* set memory width of audio data , reg word_length */
+static int cv1800b_set_word_length(struct cv1800b_i2s *i2s,
+ unsigned int physical_width)
+{
+ u8 word_length_val;
+ u32 val;
+
+ switch (physical_width) {
+ case 8:
+ word_length_val = CV1800B_WORD_LENGTH_8_BIT;
+ break;
+ case 16:
+ word_length_val = CV1800B_WORD_LENGTH_16_BIT;
+ break;
+ case 32:
+ word_length_val = CV1800B_WORD_LENGTH_32_BIT;
+ break;
+ default:
+ dev_dbg(i2s->dev, "can't set word_length field\n");
+ return -EINVAL;
+ }
+
+ val = readl(i2s->base + CV1800B_DATA_FORMAT);
+ val = u32_replace_bits(val, word_length_val, DF_WORD_LENGTH_MASK);
+ writel(val, i2s->base + CV1800B_DATA_FORMAT);
+ return 0;
+}
+
+static void cv1800b_enable_clocks(struct cv1800b_i2s *i2s, bool enabled)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, enabled, CLK_AUD_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+}
+
+static int cv1800b_set_slot_settings(struct cv1800b_i2s *i2s, u32 slots,
+ u32 physical_width, u32 data_size)
+{
+ u32 slot_num;
+ u32 slot_size;
+ u32 frame_length;
+ u32 frame_active_length;
+ u32 val;
+
+ if (!slots || !physical_width || !data_size) {
+ dev_err(i2s->dev, "frame or slot settings are not valid\n");
+ return -EINVAL;
+ }
+ if (slots > 16 || physical_width > 64 || data_size > 32) {
+ dev_err(i2s->dev, "frame or slot settings are not valid\n");
+ return -EINVAL;
+ }
+
+ slot_num = slots - 1;
+ slot_size = physical_width - 1;
+ frame_length = (physical_width * slots) - 1;
+ frame_active_length = physical_width - 1;
+
+ if (frame_length > 511 || frame_active_length > 255) {
+ dev_err(i2s->dev, "frame or slot settings are not valid\n");
+ return -EINVAL;
+ }
+
+ val = readl(i2s->base + CV1800B_SLOT_SETTING1);
+ val = u32_replace_bits(val, slot_size, SLOT_SIZE_MASK);
+ val = u32_replace_bits(val, data_size - 1, DATA_SIZE_MASK);
+ val = u32_replace_bits(val, slot_num, SLOT_NUM_MASK);
+ writel(val, i2s->base + CV1800B_SLOT_SETTING1);
+
+ val = readl(i2s->base + CV1800B_FRAME_SETTING);
+ val = u32_replace_bits(val, frame_length, FRAME_LENGTH_MASK);
+ val = u32_replace_bits(val, frame_active_length, FS_ACTIVE_LENGTH_MASK);
+ writel(val, i2s->base + CV1800B_FRAME_SETTING);
+
+ dev_dbg(i2s->dev, "slot settings num: %u width: %u\n", slots, physical_width);
+ return 0;
+}
+
+/*
+ * calculate mclk_div.
+ * if requested value is bigger than optimal
+ * leave mclk_div as 1. cff clock is capable
+ * to handle it
+ */
+static int cv1800b_calc_mclk_div(unsigned int target_mclk, u32 *mclk_div)
+{
+ *mclk_div = 1;
+
+ if (target_mclk == 0)
+ return -EINVAL;
+
+ /* optimal parent frequency is close to CV1800B_DEF_FREQ */
+ if (target_mclk < CV1800B_DEF_FREQ) {
+ *mclk_div = DIV_ROUND_CLOSEST(CV1800B_DEF_FREQ, target_mclk);
+ if (!*mclk_div || *mclk_div > 0xFFFF)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * set CCF clock and divider for this clock
+ * mclk_clock = ccf_clock / mclk_div
+ */
+static int cv1800b_i2s_set_rate_for_mclk(struct cv1800b_i2s *i2s,
+ unsigned int target_mclk)
+{
+ u32 mclk_div = 1;
+ u64 tmp;
+ int ret;
+ unsigned long clk_rate;
+ unsigned long actual;
+
+ ret = cv1800b_calc_mclk_div(target_mclk, &mclk_div);
+ if (ret) {
+ dev_dbg(i2s->dev, "can't calc mclk_div for freq %u\n",
+ target_mclk);
+ return ret;
+ }
+
+ tmp = (u64)target_mclk * mclk_div;
+ if (tmp > ULONG_MAX) {
+ dev_err(i2s->dev, "clk_rate overflow: freq=%u div=%u\n",
+ target_mclk, mclk_div);
+ return -ERANGE;
+ }
+
+ clk_rate = (unsigned long)tmp;
+
+ cv1800b_enable_clocks(i2s, false);
+
+ ret = clk_set_rate(i2s->sysclk, clk_rate);
+ if (ret)
+ return ret;
+
+ actual = clk_get_rate(i2s->sysclk);
+ if (clk_rate != actual) {
+ dev_err_ratelimited(i2s->dev,
+ "clk_set_rate failed %lu, actual is %lu\n",
+ clk_rate, actual);
+ }
+
+ cv1800b_set_mclk_div(i2s, mclk_div);
+ cv1800b_enable_clocks(i2s, true);
+
+ return 0;
+}
+
+static int cv1800b_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ unsigned int physical_width = params_physical_width(params);
+ int data_width = params_width(params);
+ bool tx_mode = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+ int ret;
+ u32 bclk_div;
+ u32 bclk_ratio;
+ u32 mclk_rate;
+ u32 tmp;
+
+ if (data_width < 0)
+ return data_width;
+
+ if (!channels || !rate || !physical_width)
+ return -EINVAL;
+
+ ret = cv1800b_set_slot_settings(i2s, channels, physical_width, data_width);
+ if (ret)
+ return ret;
+
+ if (i2s->mclk_rate) {
+ mclk_rate = i2s->mclk_rate;
+ } else {
+ dev_dbg(i2s->dev, "mclk is not set by machine driver\n");
+ ret = cv1800b_i2s_set_rate_for_mclk(i2s,
+ rate * CV1800B_DEF_MCLK_FS_RATIO);
+ if (ret)
+ return ret;
+ mclk_rate = rate * CV1800B_DEF_MCLK_FS_RATIO;
+ }
+
+ bclk_ratio = (i2s->bclk_ratio_fixed) ? i2s->bclk_ratio :
+ (physical_width * channels);
+
+ if (check_mul_overflow(rate, bclk_ratio, &tmp))
+ return -EOVERFLOW;
+
+ if (!tmp)
+ return -EINVAL;
+ if (mclk_rate % tmp)
+ dev_warn(i2s->dev, "mclk rate is not aligned to bclk or rate\n");
+
+ bclk_div = DIV_ROUND_CLOSEST(mclk_rate, tmp);
+
+ ret = cv1800b_set_bclk_div(i2s, bclk_div);
+ if (ret)
+ return ret;
+
+ ret = cv1800b_set_word_length(i2s, physical_width);
+ if (ret)
+ return ret;
+
+ cv1800b_set_tx_mode(i2s, tx_mode);
+
+ cv1800b_reset_fifo(i2s);
+ cv1800b_reset_i2s(i2s);
+ return 0;
+}
+
+static int cv1800b_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_I2S_ENABLE);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = u32_replace_bits(val, 1, I2S_ENABLE_MASK);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = u32_replace_bits(val, 0, I2S_ENABLE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(val, i2s->base + CV1800B_I2S_ENABLE);
+ return 0;
+}
+
+static int cv1800b_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dev_dbg(i2s->dev, "%s: dai=%s substream=%d\n", __func__, dai->name,
+ substream->stream);
+ /**
+ * Ensure DMA is stopped before DAI
+ * shutdown (prevents DW AXI DMAC stop/busy on next open).
+ */
+ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
+ return 0;
+}
+
+static int cv1800b_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ if (!i2s) {
+ dev_err(dai->dev, "no drvdata in DAI probe\n");
+ return -ENODEV;
+ }
+
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma, &i2s->capture_dma);
+ return 0;
+}
+
+static int cv1800b_i2s_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ u32 master;
+
+ /* only i2s format is supported */
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(i2s->dev, "set to master mode\n");
+ master = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(i2s->dev, "set to slave mode\n");
+ master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(i2s->base + CV1800B_BLK_MODE_SETTING);
+ val = u32_replace_bits(val, master, BLK_MASTER_MODE_MASK);
+ writel(val, i2s->base + CV1800B_BLK_MODE_SETTING);
+ return 0;
+}
+
+static int cv1800b_i2s_dai_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ if (ratio == 0)
+ return -EINVAL;
+ i2s->bclk_ratio = ratio;
+ i2s->bclk_ratio_fixed = true;
+ return 0;
+}
+
+static int cv1800b_i2s_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct cv1800b_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ int ret;
+ u32 val;
+ bool output_enable = (dir == SND_SOC_CLOCK_OUT) ? true : false;
+
+ dev_dbg(i2s->dev, "%s called with %u\n", __func__, freq);
+ ret = cv1800b_i2s_set_rate_for_mclk(i2s, freq);
+ if (ret)
+ return ret;
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, output_enable, CLK_MCLK_OUT_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+
+ i2s->mclk_rate = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = {
+ .probe = cv1800b_i2s_dai_probe,
+ .startup = cv1800b_i2s_startup,
+ .hw_params = cv1800b_i2s_hw_params,
+ .trigger = cv1800b_i2s_trigger,
+ .set_fmt = cv1800b_i2s_dai_set_fmt,
+ .set_bclk_ratio = cv1800b_i2s_dai_set_bclk_ratio,
+ .set_sysclk = cv1800b_i2s_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = {
+ .name = "cv1800b-i2s",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &cv1800b_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver cv1800b_i2s_component = {
+ .name = "cv1800b-i2s",
+};
+
+static void cv1800b_i2s_hw_disable(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_I2S_ENABLE);
+ val = u32_replace_bits(val, 0, I2S_ENABLE_MASK);
+ writel(val, i2s->base + CV1800B_I2S_ENABLE);
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, 0, CLK_AUD_EN_MASK);
+ val = u32_replace_bits(val, 0, CLK_MCLK_OUT_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+
+ val = readl(i2s->base + CV1800B_I2S_RESET);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_RX_MASK);
+ val = u32_replace_bits(val, 1, RST_I2S_RESET_TX_MASK);
+ writel(val, i2s->base + CV1800B_I2S_RESET);
+
+ val = readl(i2s->base + CV1800B_FIFO_RESET);
+ val = u32_replace_bits(val, 1, FIFO_RX_RESET_MASK);
+ val = u32_replace_bits(val, 1, FIFO_TX_RESET_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_RESET);
+}
+
+static void cv1800b_i2s_setup_tdm(struct cv1800b_i2s *i2s)
+{
+ u32 val;
+
+ val = readl(i2s->base + CV1800B_BLK_MODE_SETTING);
+ val = u32_replace_bits(val, 1, BLK_DMA_MODE_MASK);
+ writel(val, i2s->base + CV1800B_BLK_MODE_SETTING);
+
+ val = readl(i2s->base + CV1800B_CLK_CTRL0);
+ val = u32_replace_bits(val, 0, CLK_AUD_CLK_SEL_MASK);
+ val = u32_replace_bits(val, 0, CLK_MCLK_OUT_EN_MASK);
+ val = u32_replace_bits(val, 0, CLK_AUD_EN_MASK);
+ writel(val, i2s->base + CV1800B_CLK_CTRL0);
+
+ val = readl(i2s->base + CV1800B_FIFO_THRESHOLD);
+ val = u32_replace_bits(val, 4, FIFO_RX_THRESHOLD_MASK);
+ val = u32_replace_bits(val, 4, FIFO_TX_THRESHOLD_MASK);
+ val = u32_replace_bits(val, 4, FIFO_TX_HIGH_THRESHOLD_MASK);
+ writel(val, i2s->base + CV1800B_FIFO_THRESHOLD);
+
+ val = readl(i2s->base + CV1800B_I2S_ENABLE);
+ val = u32_replace_bits(val, 0, I2S_ENABLE_MASK);
+ writel(val, i2s->base + CV1800B_I2S_ENABLE);
+}
+
+static int cv1800b_i2s_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_i2s *i2s;
+ struct resource *res;
+ void __iomem *regs;
+ struct snd_soc_dai_driver *dai;
+ int ret;
+
+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ i2s->dev = &pdev->dev;
+ i2s->base = regs;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ cv1800b_setup_dma_struct(i2s, res->start);
+
+ i2s->clk = devm_clk_get_enabled(dev, "i2s");
+ if (IS_ERR(i2s->clk))
+ return dev_err_probe(dev, PTR_ERR(i2s->clk),
+ "failed to get+enable i2s\n");
+ i2s->sysclk = devm_clk_get_enabled(dev, "mclk");
+ if (IS_ERR(i2s->sysclk))
+ return dev_err_probe(dev, PTR_ERR(i2s->sysclk),
+ "failed to get+enable mclk\n");
+
+ platform_set_drvdata(pdev, i2s);
+ cv1800b_i2s_setup_tdm(i2s);
+
+ dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai),
+ GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai,
+ 1);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0);
+ if (ret) {
+ dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cv1800b_i2s_remove(struct platform_device *pdev)
+{
+ struct cv1800b_i2s *i2s = platform_get_drvdata(pdev);
+
+ if (!i2s)
+ return;
+ cv1800b_i2s_hw_disable(i2s);
+}
+
+static const struct of_device_id cv1800b_i2s_of_match[] = {
+ { .compatible = "sophgo,cv1800b-i2s" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cv1800b_i2s_of_match);
+
+static struct platform_driver cv1800b_i2s_driver = {
+ .probe = cv1800b_i2s_probe,
+ .remove = cv1800b_i2s_remove,
+ .driver = {
+ .name = "cv1800b-i2s",
+ .of_match_table = cv1800b_i2s_of_match,
+ },
+};
+module_platform_driver(cv1800b_i2s_driver);
+
+MODULE_DESCRIPTION("Sophgo cv1800b I2S/TDM driver");
+MODULE_AUTHOR("Anton D. Stavinsky <stavinsky@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 1e755a716c63..65de03ca3ad2 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -684,6 +684,10 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun4i_spdif_regmap_config);
+ if (IS_ERR(host->regmap)) {
+ dev_err(&pdev->dev, "failed to initialise regmap.\n");
+ return PTR_ERR(host->regmap);
+ }
/* Clocks */
host->apb_clk = devm_clk_get(&pdev->dev, "apb");
diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c
index bab1e29c9988..eddfebe16616 100644
--- a/sound/soc/sunxi/sun50i-dmic.c
+++ b/sound/soc/sunxi/sun50i-dmic.c
@@ -358,6 +358,9 @@ static int sun50i_dmic_probe(struct platform_device *pdev)
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun50i_dmic_regmap_config);
+ if (IS_ERR(host->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap),
+ "failed to initialise regmap\n");
/* Clocks */
host->bus_clk = devm_clk_get(&pdev->dev, "bus");
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
index 2c0220e14a57..d2a5ec7c54cc 100644
--- a/sound/soc/tegra/tegra186_asrc.c
+++ b/sound/soc/tegra/tegra186_asrc.c
@@ -950,6 +950,7 @@ static const struct regmap_config tegra186_asrc_regmap_config = {
.volatile_reg = tegra186_asrc_volatile_reg,
.reg_defaults = tegra186_asrc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra186_asrc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c
index a762150db802..8816e4967331 100644
--- a/sound/soc/tegra/tegra186_dspk.c
+++ b/sound/soc/tegra/tegra186_dspk.c
@@ -467,6 +467,7 @@ static const struct regmap_config tegra186_dspk_regmap = {
.volatile_reg = tegra186_dspk_volatile_reg,
.reg_defaults = tegra186_dspk_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra186_dspk_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index f9f6040c4e34..0976779d29f2 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -241,6 +241,7 @@ static const struct regmap_config tegra210_admaif_regmap_config = {
.volatile_reg = tegra_admaif_volatile_reg,
.reg_defaults = tegra210_admaif_reg_defaults,
.num_reg_defaults = TEGRA210_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -254,6 +255,7 @@ static const struct regmap_config tegra186_admaif_regmap_config = {
.volatile_reg = tegra_admaif_volatile_reg,
.reg_defaults = tegra186_admaif_reg_defaults,
.num_reg_defaults = TEGRA186_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -267,6 +269,7 @@ static const struct regmap_config tegra264_admaif_regmap_config = {
.volatile_reg = tegra_admaif_volatile_reg,
.reg_defaults = tegra264_admaif_reg_defaults,
.num_reg_defaults = TEGRA264_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
index 6c9a410085bc..95875c75ddf8 100644
--- a/sound/soc/tegra/tegra210_adx.c
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -625,6 +625,7 @@ static const struct regmap_config tegra210_adx_regmap_config = {
.volatile_reg = tegra210_adx_volatile_reg,
.reg_defaults = tegra210_adx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -638,6 +639,7 @@ static const struct regmap_config tegra264_adx_regmap_config = {
.volatile_reg = tegra264_adx_volatile_reg,
.reg_defaults = tegra264_adx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra264_adx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index e795907a3963..43a45f785d5b 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -2049,6 +2049,61 @@ static const struct snd_soc_component_driver tegra264_ahub_component = {
.num_dapm_routes = ARRAY_SIZE(tegra264_ahub_routes),
};
+static bool tegra210_ahub_wr_reg(struct device *dev, unsigned int reg)
+{
+ int part;
+
+ if (reg % TEGRA210_XBAR_RX_STRIDE)
+ return false;
+
+ for (part = 0; part < TEGRA210_XBAR_UPDATE_MAX_REG; part++) {
+ switch (reg & ~(part * TEGRA210_XBAR_PART1_RX)) {
+ case TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 ... TEGRA210_AXBAR_PART_0_ADMAIF_RX10_0:
+ case TEGRA210_AXBAR_PART_0_I2S1_RX1_0 ... TEGRA210_AXBAR_PART_0_I2S5_RX1_0:
+ case TEGRA210_AXBAR_PART_0_SFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_SFC4_RX1_0:
+ case TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 ... TEGRA210_AXBAR_PART_0_MIXER1_RX10_0:
+ case TEGRA210_AXBAR_PART_0_SPDIF1_RX1_0 ... TEGRA210_AXBAR_PART_0_SPDIF1_RX2_0:
+ case TEGRA210_AXBAR_PART_0_AFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_AFC6_RX1_0:
+ case TEGRA210_AXBAR_PART_0_OPE1_RX1_0 ... TEGRA210_AXBAR_PART_0_OPE2_RX1_0:
+ case TEGRA210_AXBAR_PART_0_SPKPROT1_RX1_0:
+ case TEGRA210_AXBAR_PART_0_MVC1_RX1_0 ... TEGRA210_AXBAR_PART_0_MVC2_RX1_0:
+ case TEGRA210_AXBAR_PART_0_AMX1_RX1_0 ... TEGRA210_AXBAR_PART_0_ADX2_RX1_0:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+static bool tegra186_ahub_wr_reg(struct device *dev, unsigned int reg)
+{
+ int part;
+
+ if (reg % TEGRA210_XBAR_RX_STRIDE)
+ return false;
+
+ for (part = 0; part < TEGRA186_XBAR_UPDATE_MAX_REG; part++) {
+ switch (reg & ~(part * TEGRA210_XBAR_PART1_RX)) {
+ case TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 ... TEGRA186_AXBAR_PART_0_I2S6_RX1_0:
+ case TEGRA210_AXBAR_PART_0_SFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_SFC4_RX1_0:
+ case TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 ... TEGRA210_AXBAR_PART_0_MIXER1_RX10_0:
+ case TEGRA186_AXBAR_PART_0_DSPK1_RX1_0 ... TEGRA186_AXBAR_PART_0_DSPK2_RX1_0:
+ case TEGRA210_AXBAR_PART_0_AFC1_RX1_0 ... TEGRA210_AXBAR_PART_0_AFC6_RX1_0:
+ case TEGRA210_AXBAR_PART_0_OPE1_RX1_0:
+ case TEGRA186_AXBAR_PART_0_MVC1_RX1_0 ... TEGRA186_AXBAR_PART_0_MVC2_RX1_0:
+ case TEGRA186_AXBAR_PART_0_AMX1_RX1_0 ... TEGRA186_AXBAR_PART_0_AMX3_RX4_0:
+ case TEGRA210_AXBAR_PART_0_ADX1_RX1_0 ... TEGRA186_AXBAR_PART_0_ASRC1_RX7_0:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static bool tegra264_ahub_wr_reg(struct device *dev, unsigned int reg)
{
int part;
@@ -2076,7 +2131,9 @@ static const struct regmap_config tegra210_ahub_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
+ .writeable_reg = tegra210_ahub_wr_reg,
.max_register = TEGRA210_MAX_REGISTER_ADDR,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -2084,7 +2141,9 @@ static const struct regmap_config tegra186_ahub_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
+ .writeable_reg = tegra186_ahub_wr_reg,
.max_register = TEGRA186_MAX_REGISTER_ADDR,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -2094,6 +2153,7 @@ static const struct regmap_config tegra264_ahub_regmap_config = {
.reg_stride = 4,
.writeable_reg = tegra264_ahub_wr_reg,
.max_register = TEGRA264_MAX_REGISTER_ADDR,
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h
index f355b2cfd19b..acbe640dd3b5 100644
--- a/sound/soc/tegra/tegra210_ahub.h
+++ b/sound/soc/tegra/tegra210_ahub.h
@@ -68,6 +68,36 @@
#define TEGRA210_MAX_REGISTER_ADDR (TEGRA210_XBAR_PART2_RX + \
(TEGRA210_XBAR_RX_STRIDE * (TEGRA210_XBAR_AUDIO_RX_COUNT - 1)))
+/* AXBAR register offsets */
+#define TEGRA186_AXBAR_PART_0_AMX1_RX1_0 0x120
+#define TEGRA186_AXBAR_PART_0_AMX3_RX4_0 0x14c
+#define TEGRA186_AXBAR_PART_0_ASRC1_RX7_0 0x1a8
+#define TEGRA186_AXBAR_PART_0_DSPK1_RX1_0 0xc0
+#define TEGRA186_AXBAR_PART_0_DSPK2_RX1_0 0xc4
+#define TEGRA186_AXBAR_PART_0_I2S6_RX1_0 0x54
+#define TEGRA186_AXBAR_PART_0_MVC1_RX1_0 0x110
+#define TEGRA186_AXBAR_PART_0_MVC2_RX1_0 0x114
+#define TEGRA210_AXBAR_PART_0_ADMAIF_RX10_0 0x24
+#define TEGRA210_AXBAR_PART_0_ADMAIF_RX1_0 0x0
+#define TEGRA210_AXBAR_PART_0_ADX1_RX1_0 0x160
+#define TEGRA210_AXBAR_PART_0_ADX2_RX1_0 0x164
+#define TEGRA210_AXBAR_PART_0_AFC1_RX1_0 0xd0
+#define TEGRA210_AXBAR_PART_0_AFC6_RX1_0 0xe4
+#define TEGRA210_AXBAR_PART_0_AMX1_RX1_0 0x140
+#define TEGRA210_AXBAR_PART_0_I2S1_RX1_0 0x40
+#define TEGRA210_AXBAR_PART_0_I2S5_RX1_0 0x50
+#define TEGRA210_AXBAR_PART_0_MIXER1_RX10_0 0xa4
+#define TEGRA210_AXBAR_PART_0_MIXER1_RX1_0 0x80
+#define TEGRA210_AXBAR_PART_0_MVC1_RX1_0 0x120
+#define TEGRA210_AXBAR_PART_0_MVC2_RX1_0 0x124
+#define TEGRA210_AXBAR_PART_0_OPE1_RX1_0 0x100
+#define TEGRA210_AXBAR_PART_0_OPE2_RX1_0 0x104
+#define TEGRA210_AXBAR_PART_0_SFC1_RX1_0 0x60
+#define TEGRA210_AXBAR_PART_0_SFC4_RX1_0 0x6c
+#define TEGRA210_AXBAR_PART_0_SPDIF1_RX1_0 0xc0
+#define TEGRA210_AXBAR_PART_0_SPDIF1_RX2_0 0xc4
+#define TEGRA210_AXBAR_PART_0_SPKPROT1_RX1_0 0x110
+
#define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id))
#define MUX_VALUE(npart, nbit) (1 + (nbit) + (npart) * 32)
diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c
index c94f8c84e04f..bfda82505298 100644
--- a/sound/soc/tegra/tegra210_amx.c
+++ b/sound/soc/tegra/tegra210_amx.c
@@ -654,6 +654,7 @@ static const struct regmap_config tegra210_amx_regmap_config = {
.volatile_reg = tegra210_amx_volatile_reg,
.reg_defaults = tegra210_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -667,6 +668,7 @@ static const struct regmap_config tegra194_amx_regmap_config = {
.volatile_reg = tegra210_amx_volatile_reg,
.reg_defaults = tegra210_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_amx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -680,6 +682,7 @@ static const struct regmap_config tegra264_amx_regmap_config = {
.volatile_reg = tegra264_amx_volatile_reg,
.reg_defaults = tegra264_amx_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra264_amx_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c
index 66fff53aeaa6..93def7ac4fde 100644
--- a/sound/soc/tegra/tegra210_dmic.c
+++ b/sound/soc/tegra/tegra210_dmic.c
@@ -483,6 +483,7 @@ static const struct regmap_config tegra210_dmic_regmap_config = {
.volatile_reg = tegra210_dmic_volatile_reg,
.reg_defaults = tegra210_dmic_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_dmic_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index b91e0e6cd7fe..d8e02f0a3025 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -997,6 +997,7 @@ static const struct regmap_config tegra210_regmap_conf = {
.volatile_reg = tegra210_i2s_volatile_reg,
.reg_defaults = tegra210_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_i2s_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
@@ -1044,6 +1045,7 @@ static const struct regmap_config tegra264_regmap_conf = {
.volatile_reg = tegra264_i2s_volatile_reg,
.reg_defaults = tegra264_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra264_i2s_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c
index 09fe3c5cf540..6a268dbb7197 100644
--- a/sound/soc/tegra/tegra210_mbdrc.c
+++ b/sound/soc/tegra/tegra210_mbdrc.c
@@ -763,6 +763,7 @@ static const struct regmap_config tegra210_mbdrc_regmap_cfg = {
.precious_reg = tegra210_mbdrc_precious_reg,
.reg_defaults = tegra210_mbdrc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mbdrc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c
index ff8e9f2d7abf..6d3a2b76fd61 100644
--- a/sound/soc/tegra/tegra210_mixer.c
+++ b/sound/soc/tegra/tegra210_mixer.c
@@ -608,6 +608,7 @@ static const struct regmap_config tegra210_mixer_regmap_config = {
.precious_reg = tegra210_mixer_precious_reg,
.reg_defaults = tegra210_mixer_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c
index 779d4c199da9..6cdc5e1f5507 100644
--- a/sound/soc/tegra/tegra210_mvc.c
+++ b/sound/soc/tegra/tegra210_mvc.c
@@ -699,6 +699,7 @@ static const struct regmap_config tegra210_mvc_regmap_config = {
.volatile_reg = tegra210_mvc_volatile_reg,
.reg_defaults = tegra210_mvc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_ope.c b/sound/soc/tegra/tegra210_ope.c
index 27db70af2746..a440888dcdbd 100644
--- a/sound/soc/tegra/tegra210_ope.c
+++ b/sound/soc/tegra/tegra210_ope.c
@@ -297,6 +297,7 @@ static const struct regmap_config tegra210_ope_regmap_config = {
.volatile_reg = tegra210_ope_volatile_reg,
.reg_defaults = tegra210_ope_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_ope_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_peq.c b/sound/soc/tegra/tegra210_peq.c
index 9a05e6913276..2f72e9d541dc 100644
--- a/sound/soc/tegra/tegra210_peq.c
+++ b/sound/soc/tegra/tegra210_peq.c
@@ -306,6 +306,7 @@ static const struct regmap_config tegra210_peq_regmap_config = {
.precious_reg = tegra210_peq_precious_reg,
.reg_defaults = tegra210_peq_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_peq_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c
index d6341968bebe..b298bf0421b1 100644
--- a/sound/soc/tegra/tegra210_sfc.c
+++ b/sound/soc/tegra/tegra210_sfc.c
@@ -3569,6 +3569,7 @@ static const struct regmap_config tegra210_sfc_regmap_config = {
.precious_reg = tegra210_sfc_precious_reg,
.reg_defaults = tegra210_sfc_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_sfc_reg_defaults),
+ .reg_default_cb = regmap_default_zero_cb,
.cache_type = REGCACHE_FLAT,
};
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 621a9d5f9377..2d260fbc9b83 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -25,6 +25,7 @@
#include <linux/math64.h>
#include <linux/bitmap.h>
#include <linux/gpio/driver.h>
+#include <linux/property.h>
#include <sound/asoundef.h>
#include <sound/core.h>
@@ -70,6 +71,7 @@ struct davinci_mcasp_context {
struct davinci_mcasp_ruledata {
struct davinci_mcasp *mcasp;
int serializers;
+ int stream;
};
struct davinci_mcasp {
@@ -87,21 +89,27 @@ struct davinci_mcasp {
bool missing_audio_param;
/* McASP specific data */
- int tdm_slots;
+ int tdm_slots_tx;
+ int tdm_slots_rx;
u32 tdm_mask[2];
- int slot_width;
+ int slot_width_tx;
+ int slot_width_rx;
u8 op_mode;
u8 dismod;
u8 num_serializer;
u8 *serial_dir;
u8 version;
- u8 bclk_div;
+ u8 bclk_div_tx;
+ u8 bclk_div_rx;
int streams;
u32 irq_request[2];
- int sysclk_freq;
+ unsigned int sysclk_freq_tx;
+ unsigned int sysclk_freq_rx;
bool bclk_master;
- u32 auxclk_fs_ratio;
+ bool async_mode;
+ u32 auxclk_fs_ratio_tx;
+ u32 auxclk_fs_ratio_rx;
unsigned long pdir; /* Pin direction bitfield */
@@ -179,10 +187,16 @@ static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val)
static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
{
- u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
- return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
+ return !(aclkxctl & TX_ASYNC);
+}
+
+static bool mcasp_is_frame_producer(struct davinci_mcasp *mcasp)
+{
+ u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+
+ return rxfmctl & AFSRE;
}
static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable)
@@ -197,6 +211,27 @@ static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable)
}
}
+static inline void mcasp_set_clk_pdir_stream(struct davinci_mcasp *mcasp,
+ int stream, bool enable)
+{
+ u32 bit, bit_end;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bit = PIN_BIT_ACLKX;
+ bit_end = PIN_BIT_AFSX + 1;
+ } else {
+ bit = PIN_BIT_ACLKR;
+ bit_end = PIN_BIT_AFSR + 1;
+ }
+
+ for_each_set_bit_from(bit, &mcasp->pdir, bit_end) {
+ if (enable)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ }
+}
+
static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
{
u32 bit;
@@ -209,6 +244,36 @@ static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
}
}
+static inline int mcasp_get_tdm_slots(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->tdm_slots_tx : mcasp->tdm_slots_rx;
+}
+
+static inline int mcasp_get_slot_width(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->slot_width_tx : mcasp->slot_width_rx;
+}
+
+static inline unsigned int mcasp_get_sysclk_freq(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->sysclk_freq_tx : mcasp->sysclk_freq_rx;
+}
+
+static inline unsigned int mcasp_get_bclk_div(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->bclk_div_tx : mcasp->bclk_div_rx;
+}
+
+static inline unsigned int mcasp_get_auxclk_fs_ratio(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->auxclk_fs_ratio_tx : mcasp->auxclk_fs_ratio_rx;
+}
+
static void mcasp_start_rx(struct davinci_mcasp *mcasp)
{
if (mcasp->rxnumevt) { /* enable FIFO */
@@ -224,13 +289,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
/*
* When ASYNC == 0 the transmit and receive sections operate
* synchronously from the transmit clock and frame sync. We need to make
- * sure that the TX signlas are enabled when starting reception.
+ * sure that the TX signals are enabled when starting reception,
+ * when the McASP is the producer.
*/
- if (mcasp_is_synchronous(mcasp)) {
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) {
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_clk_pdir(mcasp, true);
}
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir(mcasp, true);
+ else
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, true);
/* Activate serializer(s) */
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
@@ -239,7 +308,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
/* Release Frame Sync generator */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
- if (mcasp_is_synchronous(mcasp))
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp))
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
/* enable receive IRQs */
@@ -261,7 +330,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp)
/* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_clk_pdir(mcasp, true);
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir(mcasp, true);
+ else
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, true);
/* Activate serializer(s) */
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
@@ -304,11 +376,19 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
/*
* In synchronous mode stop the TX clocks if no other stream is
* running
+ * Otherwise in async mode only stop RX clocks
*/
- if (mcasp_is_synchronous(mcasp) && !mcasp->streams) {
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_clk_pdir(mcasp, false);
+ else if (!mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, false);
+ /*
+ * When McASP is the producer and operating in synchronous mode,
+ * stop the transmit clocks if no other stream is running. As
+ * tx & rx operate synchronously from the transmit clock.
+ */
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
- }
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
@@ -331,11 +411,14 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
/*
* In synchronous mode keep TX clocks running if the capture stream is
* still running.
+ * Otherwise in async mode only stop TX clocks
*/
- if (mcasp_is_synchronous(mcasp) && mcasp->streams)
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams)
val = TXHCLKRST | TXCLKRST | TXFSRST;
- else
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_clk_pdir(mcasp, false);
+ else if (!mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, false);
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
@@ -619,13 +702,39 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
break;
+ case MCASP_CLKDIV_AUXCLK_TXONLY: /* MCLK divider for TX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+ break;
+
+ case MCASP_CLKDIV_AUXCLK_RXONLY: /* MCLK divider for RX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
+ break;
+
case MCASP_CLKDIV_BCLK: /* BCLK divider */
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
ACLKXDIV(div - 1), ACLKXDIV_MASK);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ if (explicit) {
+ mcasp->bclk_div_tx = div;
+ mcasp->bclk_div_rx = div;
+ }
+ break;
+
+ case MCASP_CLKDIV_BCLK_TXONLY: /* BCLK divider for TX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
+ ACLKXDIV(div - 1), ACLKXDIV_MASK);
if (explicit)
- mcasp->bclk_div = div;
+ mcasp->bclk_div_tx = div;
+ break;
+
+ case MCASP_CLKDIV_BCLK_RXONLY: /* BCLK divider for RX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
+ ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ if (explicit)
+ mcasp->bclk_div_rx = div;
break;
case MCASP_CLKDIV_BCLK_FS_RATIO:
@@ -639,11 +748,33 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
* tdm_slot width by dividing the ratio by the
* number of configured tdm slots.
*/
- mcasp->slot_width = div / mcasp->tdm_slots;
- if (div % mcasp->tdm_slots)
+ mcasp->slot_width_tx = div / mcasp->tdm_slots_tx;
+ if (div % mcasp->tdm_slots_tx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots",
+ __func__, div, mcasp->tdm_slots_tx);
+
+ mcasp->slot_width_rx = div / mcasp->tdm_slots_rx;
+ if (div % mcasp->tdm_slots_rx)
dev_warn(mcasp->dev,
- "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
- __func__, div, mcasp->tdm_slots);
+ "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots",
+ __func__, div, mcasp->tdm_slots_rx);
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY:
+ mcasp->slot_width_tx = div / mcasp->tdm_slots_tx;
+ if (div % mcasp->tdm_slots_tx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots",
+ __func__, div, mcasp->tdm_slots_tx);
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY:
+ mcasp->slot_width_rx = div / mcasp->tdm_slots_rx;
+ if (div % mcasp->tdm_slots_rx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots",
+ __func__, div, mcasp->tdm_slots_rx);
break;
default:
@@ -677,6 +808,20 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ case MCASP_CLK_HCLK_AHCLK_TXONLY:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AHCLK_RXONLY:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ clear_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
break;
case MCASP_CLK_HCLK_AUXCLK:
mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
@@ -684,22 +829,56 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_TXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_RXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
break;
default:
dev_err(mcasp->dev, "Invalid clk id: %d\n", clk_id);
goto out;
}
} else {
- /* Select AUXCLK as HCLK */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ /* McASP is clock master, select AUXCLK as HCLK */
+ switch (clk_id) {
+ case MCASP_CLK_HCLK_AUXCLK_TXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_RXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ default:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ }
}
/*
* When AHCLK X/R is selected to be output it means that the HCLK is
* the same clock - coming via AUXCLK.
*/
- mcasp->sysclk_freq = freq;
out:
pm_runtime_put(mcasp->dev);
return 0;
@@ -711,9 +890,11 @@ static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
{
struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
unsigned int *list = (unsigned int *) cl->list;
- int slots = mcasp->tdm_slots;
+ int slots;
int i, count = 0;
+ slots = mcasp_get_tdm_slots(mcasp, stream);
+
if (mcasp->tdm_mask[stream])
slots = hweight32(mcasp->tdm_mask[stream]);
@@ -778,27 +959,42 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- mcasp->tdm_slots = slots;
+ if (mcasp->async_mode) {
+ if (tx_mask) {
+ mcasp->tdm_slots_tx = slots;
+ mcasp->slot_width_tx = slot_width;
+ }
+ if (rx_mask) {
+ mcasp->tdm_slots_rx = slots;
+ mcasp->slot_width_rx = slot_width;
+ }
+ } else {
+ mcasp->tdm_slots_tx = slots;
+ mcasp->tdm_slots_rx = slots;
+ mcasp->slot_width_tx = slot_width;
+ mcasp->slot_width_rx = slot_width;
+ }
+
mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
- mcasp->slot_width = slot_width;
return davinci_mcasp_set_ch_constraints(mcasp);
}
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int sample_width)
+ int sample_width, int stream)
{
u32 fmt;
u32 tx_rotate, rx_rotate, slot_width;
u32 mask = (1ULL << sample_width) - 1;
- if (mcasp->slot_width)
- slot_width = mcasp->slot_width;
- else if (mcasp->max_format_width)
- slot_width = mcasp->max_format_width;
- else
- slot_width = sample_width;
+ slot_width = mcasp_get_slot_width(mcasp, stream);
+ if (!slot_width) {
+ if (mcasp->max_format_width)
+ slot_width = mcasp->max_format_width;
+ else
+ slot_width = sample_width;
+ }
/*
* TX rotation:
* right aligned formats: rotate w/ slot_width
@@ -821,17 +1017,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
fmt = (slot_width >> 1) - 1;
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
- RXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
- TXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
- TXROT(7));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
- RXROT(7));
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+ TXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+ TXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
+ }
+ if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+ RXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+ RXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ }
} else {
/*
+ * DIT mode only use TX serializers
* according to the TRM it should be TXROT=0, this one works:
* 16 bit to 23-8 (TXROT=6, rotate 24 bits)
* 24 bit to 23-0 (TXROT=0, rotate 0 bits)
@@ -844,10 +1046,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
TXROT(7));
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(15),
TXSSZ(0x0F));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
}
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
-
return 0;
}
@@ -858,11 +1059,13 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
int i;
u8 tx_ser = 0;
u8 rx_ser = 0;
- u8 slots = mcasp->tdm_slots;
+ int slots;
u8 max_active_serializers, max_rx_serializers, max_tx_serializers;
int active_serializers, numevt;
u32 reg;
+ slots = mcasp_get_tdm_slots(mcasp, stream);
+
/* In DIT mode we only allow maximum of one serializers for now */
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
max_active_serializers = 1;
@@ -990,7 +1193,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
u32 mask = 0;
u32 busel = 0;
- total_slots = mcasp->tdm_slots;
+ total_slots = mcasp_get_tdm_slots(mcasp, stream);
/*
* If more than one serializer is needed, then use them with
@@ -1021,7 +1224,10 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
mask |= (1 << i);
}
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ if (mcasp->async_mode)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port)
busel = TXSEL;
@@ -1041,7 +1247,8 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
* not running already we need to configure the TX slots in
* order to have correct FSX on the bus
*/
- if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) &&
+ !mcasp->channels)
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
FSXMOD(total_slots), FSXMOD(0x1FF));
}
@@ -1119,16 +1326,33 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
unsigned int sysclk_freq,
- unsigned int bclk_freq, bool set)
+ unsigned int bclk_freq,
+ int stream,
+ bool set)
{
- u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
int div = sysclk_freq / bclk_freq;
int rem = sysclk_freq % bclk_freq;
int error_ppm;
int aux_div = 1;
+ int bclk_div_id, auxclk_div_id;
+ bool auxclk_enabled;
+
+ if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE;
+ bclk_div_id = MCASP_CLKDIV_BCLK_RXONLY;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK_RXONLY;
+ } else if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE;
+ bclk_div_id = MCASP_CLKDIV_BCLK_TXONLY;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK_TXONLY;
+ } else {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE;
+ bclk_div_id = MCASP_CLKDIV_BCLK;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK;
+ }
if (div > (ACLKXDIV_MASK + 1)) {
- if (reg & AHCLKXE) {
+ if (auxclk_enabled) {
aux_div = div / (ACLKXDIV_MASK + 1);
if (div % (ACLKXDIV_MASK + 1))
aux_div++;
@@ -1158,10 +1382,10 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
error_ppm);
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
- if (reg & AHCLKXE)
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
- aux_div, 0);
+ __davinci_mcasp_set_clkdiv(mcasp, bclk_div_id, div, false);
+ if (auxclk_enabled)
+ __davinci_mcasp_set_clkdiv(mcasp, auxclk_div_id,
+ aux_div, false);
}
return error_ppm;
@@ -1212,6 +1436,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int channels = params_channels(params);
int period_size = params_period_size(params);
int ret;
+ unsigned int sysclk_freq = mcasp_get_sysclk_freq(mcasp, substream->stream);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
@@ -1252,22 +1477,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
* If mcasp is BCLK master, and a BCLK divider was not provided by
* the machine driver, we need to calculate the ratio.
*/
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
- int slots = mcasp->tdm_slots;
+ if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 &&
+ sysclk_freq) {
+ int slots, slot_width;
int rate = params_rate(params);
int sbits = params_width(params);
unsigned int bclk_target;
- if (mcasp->slot_width)
- sbits = mcasp->slot_width;
+ slots = mcasp_get_tdm_slots(mcasp, substream->stream);
+
+ slot_width = mcasp_get_slot_width(mcasp, substream->stream);
+ if (slot_width)
+ sbits = slot_width;
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
bclk_target = rate * sbits * slots;
else
bclk_target = rate * 128;
- davinci_mcasp_calc_clk_div(mcasp, mcasp->sysclk_freq,
- bclk_target, true);
+ davinci_mcasp_calc_clk_div(mcasp, sysclk_freq,
+ bclk_target, substream->stream, true);
}
ret = mcasp_common_hw_param(mcasp, substream->stream,
@@ -1284,9 +1513,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
- davinci_config_channel_size(mcasp, word_length);
+ davinci_config_channel_size(mcasp, word_length, substream->stream);
- if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+ /* Channel constraints are disabled for async mode */
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE && !mcasp->async_mode) {
mcasp->channels = channels;
if (!mcasp->max_format_width)
mcasp->max_format_width = word_length;
@@ -1330,7 +1560,7 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params,
snd_pcm_format_t i;
snd_mask_none(&nfmt);
- slot_width = rd->mcasp->slot_width;
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
pcm_for_each_format(i) {
if (snd_mask_test_format(fmt, i)) {
@@ -1380,12 +1610,15 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval *ri =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
int sbits = params_width(params);
- int slots = rd->mcasp->tdm_slots;
+ int slots, slot_width;
struct snd_interval range;
int i;
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
+ slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream);
+
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
+ if (slot_width)
+ sbits = slot_width;
snd_interval_any(&range);
range.empty = 1;
@@ -1395,16 +1628,17 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
uint bclk_freq = sbits * slots *
davinci_mcasp_dai_rates[i];
unsigned int sysclk_freq;
+ unsigned int ratio;
int ppm;
- if (rd->mcasp->auxclk_fs_ratio)
- sysclk_freq = davinci_mcasp_dai_rates[i] *
- rd->mcasp->auxclk_fs_ratio;
+ ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream);
+ if (ratio)
+ sysclk_freq = davinci_mcasp_dai_rates[i] * ratio;
else
- sysclk_freq = rd->mcasp->sysclk_freq;
+ sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream);
ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq,
- bclk_freq, false);
+ bclk_freq, rd->stream, false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
if (range.empty) {
range.min = davinci_mcasp_dai_rates[i];
@@ -1430,30 +1664,34 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_mask nfmt;
int rate = params_rate(params);
- int slots = rd->mcasp->tdm_slots;
+ int slots;
int count = 0;
snd_pcm_format_t i;
+ slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream);
+
snd_mask_none(&nfmt);
pcm_for_each_format(i) {
if (snd_mask_test_format(fmt, i)) {
uint sbits = snd_pcm_format_width(i);
unsigned int sysclk_freq;
- int ppm;
+ unsigned int ratio;
+ int ppm, slot_width;
- if (rd->mcasp->auxclk_fs_ratio)
- sysclk_freq = rate *
- rd->mcasp->auxclk_fs_ratio;
+ ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream);
+ if (ratio)
+ sysclk_freq = rate * ratio;
else
- sysclk_freq = rd->mcasp->sysclk_freq;
+ sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream);
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
+ if (slot_width)
+ sbits = slot_width;
ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq,
sbits * slots * rate,
- false);
+ rd->stream, false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set_format(&nfmt, i);
count++;
@@ -1490,7 +1728,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
&mcasp->ruledata[substream->stream];
u32 max_channels = 0;
int i, dir, ret;
- int tdm_slots = mcasp->tdm_slots;
+ int tdm_slots;
u8 *numevt;
/* Do not allow more then one stream per direction */
@@ -1499,6 +1737,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
mcasp->substreams[substream->stream] = substream;
+ tdm_slots = mcasp_get_tdm_slots(mcasp, substream->stream);
+
if (mcasp->tdm_mask[substream->stream])
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
@@ -1520,6 +1760,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
}
ruledata->serializers = max_channels;
ruledata->mcasp = mcasp;
+ ruledata->stream = substream->stream;
max_channels *= tdm_slots;
/*
* If the already active stream has less channels than the calculated
@@ -1527,9 +1768,13 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
* is in use we need to use that as a constraint for the second stream.
* Otherwise (first stream or less allowed channels or more than one
* serializer in use) we use the calculated constraint.
+ *
+ * However, in async mode, TX and RX have independent clocks and can
+ * use different configurations, so don't apply the constraint.
*/
if (mcasp->channels && mcasp->channels < max_channels &&
- ruledata->serializers == 1)
+ ruledata->serializers == 1 &&
+ !mcasp->async_mode)
max_channels = mcasp->channels;
/*
* But we can always allow channels upto the amount of
@@ -1546,10 +1791,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
0, SNDRV_PCM_HW_PARAM_CHANNELS,
&mcasp->chconstr[substream->stream]);
- if (mcasp->max_format_width) {
+ if (mcasp->max_format_width && !mcasp->async_mode) {
/*
* Only allow formats which require same amount of bits on the
- * bus as the currently running stream
+ * bus as the currently running stream to ensure sync mode
*/
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
@@ -1558,8 +1803,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (ret)
return ret;
- }
- else if (mcasp->slot_width) {
+ } else if (mcasp_get_slot_width(mcasp, substream->stream)) {
/* Only allow formats require <= slot_width bits on the bus */
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
@@ -1574,7 +1818,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
* If we rely on implicit BCLK divider setting we should
* set constraints based on what we can provide.
*/
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
+ if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 &&
+ mcasp_get_sysclk_freq(mcasp, substream->stream)) {
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
davinci_mcasp_hw_rule_rate,
@@ -1751,8 +1996,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
.formats = DAVINCI_MCASP_PCM_FMTS,
},
.ops = &davinci_mcasp_dai_ops,
-
- .symmetric_rate = 1,
},
{
.name = "davinci-mcasp.1",
@@ -1875,11 +2118,7 @@ err1:
static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp)
{
-#ifdef CONFIG_OF_GPIO
- return of_property_read_bool(mcasp->dev->of_node, "gpio-controller");
-#else
- return false;
-#endif
+ return device_property_present(mcasp->dev, "gpio-controller");
}
static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
@@ -1914,18 +2153,33 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
goto out;
}
+ /* Parse TX-specific TDM slot and use it as default for RX */
if (of_property_read_u32(np, "tdm-slots", &val) == 0) {
if (val < 2 || val > 32) {
- dev_err(&pdev->dev, "tdm-slots must be in rage [2-32]\n");
+ dev_err(&pdev->dev, "tdm-slots must be in range [2-32]\n");
return -EINVAL;
}
- pdata->tdm_slots = val;
+ pdata->tdm_slots_tx = val;
+ pdata->tdm_slots_rx = val;
} else if (pdata->op_mode == DAVINCI_MCASP_IIS_MODE) {
mcasp->missing_audio_param = true;
goto out;
}
+ /* Parse RX-specific TDM slot count if provided */
+ if (of_property_read_u32(np, "tdm-slots-rx", &val) == 0) {
+ if (val < 2 || val > 32) {
+ dev_err(&pdev->dev, "tdm-slots-rx must be in range [2-32]\n");
+ return -EINVAL;
+ }
+
+ pdata->tdm_slots_rx = val;
+ }
+
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE)
+ mcasp->async_mode = of_property_read_bool(np, "ti,async-mode");
+
of_serial_dir32 = of_get_property(np, "serial-dir", &val);
val /= sizeof(u32);
if (of_serial_dir32) {
@@ -1951,8 +2205,15 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp,
if (of_property_read_u32(np, "rx-num-evt", &val) == 0)
pdata->rxnumevt = val;
- if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0)
- mcasp->auxclk_fs_ratio = val;
+ /* Parse TX-specific auxclk/fs ratio and use it as default for RX */
+ if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) {
+ mcasp->auxclk_fs_ratio_tx = val;
+ mcasp->auxclk_fs_ratio_rx = val;
+ }
+
+ /* Parse RX-specific auxclk/fs ratio if provided */
+ if (of_property_read_u32(np, "auxclk-fs-ratio-rx", &val) == 0)
+ mcasp->auxclk_fs_ratio_rx = val;
if (of_property_read_u32(np, "dismod", &val) == 0) {
if (val == 0 || val == 2 || val == 3) {
@@ -1981,19 +2242,51 @@ out:
mcasp->op_mode = pdata->op_mode;
/* sanity check for tdm slots parameter */
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
- if (pdata->tdm_slots < 2) {
- dev_warn(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 2;
- } else if (pdata->tdm_slots > 32) {
- dev_warn(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 32;
+ if (pdata->tdm_slots_tx < 2) {
+ dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n",
+ pdata->tdm_slots_tx);
+ mcasp->tdm_slots_tx = 2;
+ } else if (pdata->tdm_slots_tx > 32) {
+ dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n",
+ pdata->tdm_slots_tx);
+ mcasp->tdm_slots_tx = 32;
} else {
- mcasp->tdm_slots = pdata->tdm_slots;
+ mcasp->tdm_slots_tx = pdata->tdm_slots_tx;
+ }
+
+ if (pdata->tdm_slots_rx < 2) {
+ dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n",
+ pdata->tdm_slots_rx);
+ mcasp->tdm_slots_rx = 2;
+ } else if (pdata->tdm_slots_rx > 32) {
+ dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n",
+ pdata->tdm_slots_rx);
+ mcasp->tdm_slots_rx = 32;
+ } else {
+ mcasp->tdm_slots_rx = pdata->tdm_slots_rx;
}
} else {
- mcasp->tdm_slots = 32;
+ mcasp->tdm_slots_tx = 32;
+ mcasp->tdm_slots_rx = 32;
+ }
+
+ /* Different TX/RX slot counts require async mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE &&
+ mcasp->tdm_slots_tx != mcasp->tdm_slots_rx && !mcasp->async_mode) {
+ dev_err(&pdev->dev,
+ "Different TX (%d) and RX (%d) TDM slots require ti,async-mode\n",
+ mcasp->tdm_slots_tx, mcasp->tdm_slots_rx);
+ return -EINVAL;
+ }
+
+ /* Different TX/RX auxclk-fs-ratio require async mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE &&
+ mcasp->auxclk_fs_ratio_tx && mcasp->auxclk_fs_ratio_rx &&
+ mcasp->auxclk_fs_ratio_tx != mcasp->auxclk_fs_ratio_rx && !mcasp->async_mode) {
+ dev_err(&pdev->dev,
+ "Different TX (%d) and RX (%d) auxclk-fs-ratio require ti,async-mode\n",
+ mcasp->auxclk_fs_ratio_tx, mcasp->auxclk_fs_ratio_rx);
+ return -EINVAL;
}
mcasp->num_serializer = pdata->num_serializer;
diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h
index 5de2b8a31061..83b3c67f4a2b 100644
--- a/sound/soc/ti/davinci-mcasp.h
+++ b/sound/soc/ti/davinci-mcasp.h
@@ -298,10 +298,20 @@
/* Source of High-frequency transmit/receive clock */
#define MCASP_CLK_HCLK_AHCLK 0 /* AHCLKX/R */
#define MCASP_CLK_HCLK_AUXCLK 1 /* Internal functional clock */
+#define MCASP_CLK_HCLK_AHCLK_TXONLY 2 /* AHCLKX for TX only */
+#define MCASP_CLK_HCLK_AHCLK_RXONLY 3 /* AHCLKR for RX only */
+#define MCASP_CLK_HCLK_AUXCLK_TXONLY 4 /* AUXCLK for TX only */
+#define MCASP_CLK_HCLK_AUXCLK_RXONLY 5 /* AUXCLK for RX only */
/* clock divider IDs */
#define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */
#define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */
#define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */
+#define MCASP_CLKDIV_AUXCLK_TXONLY 3 /* AUXCLK divider for TX only */
+#define MCASP_CLKDIV_AUXCLK_RXONLY 4 /* AUXCLK divider for RX only */
+#define MCASP_CLKDIV_BCLK_TXONLY 5 /* BCLK divider for TX only */
+#define MCASP_CLKDIV_BCLK_RXONLY 6 /* BCLK divider for RX only */
+#define MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY 7 /* BCLK/FS ratio for TX only */
+#define MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY 8 /* BCLK/FS ratio for RX only*/
#endif /* DAVINCI_MCASP_H */
diff --git a/tools/include/io_uring/mini_liburing.h b/tools/include/io_uring/mini_liburing.h
index 9ccb16074eb5..44be4446feda 100644
--- a/tools/include/io_uring/mini_liburing.h
+++ b/tools/include/io_uring/mini_liburing.h
@@ -6,6 +6,7 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <sys/uio.h>
struct io_sq_ring {
unsigned int *head;
@@ -55,6 +56,7 @@ struct io_uring {
struct io_uring_sq sq;
struct io_uring_cq cq;
int ring_fd;
+ unsigned flags;
};
#if defined(__x86_64) || defined(__i386__)
@@ -72,7 +74,14 @@ static inline int io_uring_mmap(int fd, struct io_uring_params *p,
void *ptr;
int ret;
- sq->ring_sz = p->sq_off.array + p->sq_entries * sizeof(unsigned int);
+ if (p->flags & IORING_SETUP_NO_SQARRAY) {
+ sq->ring_sz = p->cq_off.cqes;
+ sq->ring_sz += p->cq_entries * sizeof(struct io_uring_cqe);
+ } else {
+ sq->ring_sz = p->sq_off.array;
+ sq->ring_sz += p->sq_entries * sizeof(unsigned int);
+ }
+
ptr = mmap(0, sq->ring_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_SQ_RING);
if (ptr == MAP_FAILED)
@@ -83,7 +92,8 @@ static inline int io_uring_mmap(int fd, struct io_uring_params *p,
sq->kring_entries = ptr + p->sq_off.ring_entries;
sq->kflags = ptr + p->sq_off.flags;
sq->kdropped = ptr + p->sq_off.dropped;
- sq->array = ptr + p->sq_off.array;
+ if (!(p->flags & IORING_SETUP_NO_SQARRAY))
+ sq->array = ptr + p->sq_off.array;
size = p->sq_entries * sizeof(struct io_uring_sqe);
sq->sqes = mmap(0, size, PROT_READ | PROT_WRITE,
@@ -126,28 +136,39 @@ static inline int io_uring_enter(int fd, unsigned int to_submit,
flags, sig, _NSIG / 8);
}
-static inline int io_uring_queue_init(unsigned int entries,
- struct io_uring *ring,
- unsigned int flags)
+static inline int io_uring_queue_init_params(unsigned int entries,
+ struct io_uring *ring,
+ struct io_uring_params *p)
{
- struct io_uring_params p;
int fd, ret;
memset(ring, 0, sizeof(*ring));
- memset(&p, 0, sizeof(p));
- p.flags = flags;
- fd = io_uring_setup(entries, &p);
+ fd = io_uring_setup(entries, p);
if (fd < 0)
return fd;
- ret = io_uring_mmap(fd, &p, &ring->sq, &ring->cq);
- if (!ret)
+ ret = io_uring_mmap(fd, p, &ring->sq, &ring->cq);
+ if (!ret) {
ring->ring_fd = fd;
- else
+ ring->flags = p->flags;
+ } else {
close(fd);
+ }
return ret;
}
+static inline int io_uring_queue_init(unsigned int entries,
+ struct io_uring *ring,
+ unsigned int flags)
+{
+ struct io_uring_params p;
+
+ memset(&p, 0, sizeof(p));
+ p.flags = flags;
+
+ return io_uring_queue_init_params(entries, ring, &p);
+}
+
/* Get a sqe */
static inline struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring)
{
@@ -199,10 +220,18 @@ static inline int io_uring_submit(struct io_uring *ring)
ktail = *sq->ktail;
to_submit = sq->sqe_tail - sq->sqe_head;
- for (submitted = 0; submitted < to_submit; submitted++) {
- read_barrier();
- sq->array[ktail++ & mask] = sq->sqe_head++ & mask;
+
+ if (!(ring->flags & IORING_SETUP_NO_SQARRAY)) {
+ for (submitted = 0; submitted < to_submit; submitted++) {
+ read_barrier();
+ sq->array[ktail++ & mask] = sq->sqe_head++ & mask;
+ }
+ } else {
+ ktail += to_submit;
+ sq->sqe_head += to_submit;
+ submitted = to_submit;
}
+
if (!submitted)
return 0;
diff --git a/tools/net/ynl/Makefile b/tools/net/ynl/Makefile
index c2f3e8b3f2ac..9b692f368be7 100644
--- a/tools/net/ynl/Makefile
+++ b/tools/net/ynl/Makefile
@@ -41,7 +41,7 @@ clean distclean:
rm -rf pyynl.egg-info
rm -rf build
-install: libynl.a lib/*.h
+install: libynl.a lib/*.h ynltool
@echo -e "\tINSTALL libynl.a"
@$(INSTALL) -d $(DESTDIR)$(libdir)
@$(INSTALL) -m 0644 libynl.a $(DESTDIR)$(libdir)/libynl.a
@@ -51,6 +51,7 @@ install: libynl.a lib/*.h
@echo -e "\tINSTALL pyynl"
@pip install --prefix=$(DESTDIR)$(prefix) .
@make -C generated install
+ @make -C ynltool install
run_tests:
@$(MAKE) -C tests run_tests
diff --git a/tools/net/ynl/ynl-regen.sh b/tools/net/ynl/ynl-regen.sh
index 81b4ecd89100..d9809276db98 100755
--- a/tools/net/ynl/ynl-regen.sh
+++ b/tools/net/ynl/ynl-regen.sh
@@ -21,7 +21,7 @@ files=$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user\)')
for f in $files; do
# params: 0 1 2 3
# $YAML YNL-GEN kernel $mode
- params=( $(git grep -B1 -h '/\* YNL-GEN' $f | sed 's@/\*\(.*\)\*/@\1@') )
+ params=( $(git grep --no-line-number -B1 -h '/\* YNL-GEN' $f | sed 's@/\*\(.*\)\*/@\1@') )
args=$(sed -n 's@/\* YNL-ARG \(.*\) \*/@\1@p' $f)
if [ $f -nt ${params[0]} -a -z "$force" ]; then
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 9b4503113ce5..a40f30232929 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -77,8 +77,21 @@ HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
# We check using HOSTCC directly rather than the shared feature framework
# because objtool is a host tool that links against host libraries.
#
-HAVE_LIBOPCODES := $(shell echo 'int main(void) { return 0; }' | \
- $(HOSTCC) -xc - -o /dev/null -lopcodes 2>/dev/null && echo y)
+# When using shared libraries, -lopcodes is sufficient as dependencies are
+# resolved automatically. With static libraries, we must explicitly link
+# against libopcodes' dependencies: libbfd, libiberty, and sometimes libz.
+# Try each combination and use the first one that succeeds.
+#
+LIBOPCODES_LIBS := $(shell \
+ for libs in "-lopcodes" \
+ "-lopcodes -lbfd" \
+ "-lopcodes -lbfd -liberty" \
+ "-lopcodes -lbfd -liberty -lz"; do \
+ echo 'extern void disassemble_init_for_target(void *);' \
+ 'int main(void) { disassemble_init_for_target(0); return 0; }' | \
+ $(HOSTCC) -xc - -o /dev/null $$libs 2>/dev/null && \
+ echo "$$libs" && break; \
+ done)
# Styled disassembler support requires binutils >= 2.39
HAVE_DISASM_STYLED := $(shell echo '$(pound)include <dis-asm.h>' | \
@@ -86,10 +99,10 @@ HAVE_DISASM_STYLED := $(shell echo '$(pound)include <dis-asm.h>' | \
BUILD_DISAS := n
-ifeq ($(HAVE_LIBOPCODES),y)
+ifneq ($(LIBOPCODES_LIBS),)
BUILD_DISAS := y
OBJTOOL_CFLAGS += -DDISAS -DPACKAGE='"objtool"'
- OBJTOOL_LDFLAGS += -lopcodes
+ OBJTOOL_LDFLAGS += $(LIBOPCODES_LIBS)
ifeq ($(HAVE_DISASM_STYLED),y)
OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED
endif
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 3f7999317f4d..719ec727efd4 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -197,7 +197,8 @@ static bool is_rust_noreturn(const struct symbol *func)
* as well as changes to the source code itself between versions (since
* these come from the Rust standard library).
*/
- return str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") ||
+ return str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") ||
+ str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") ||
str_ends_with(func->name, "_4core6option13expect_failed") ||
str_ends_with(func->name, "_4core6option13unwrap_failed") ||
str_ends_with(func->name, "_4core6result13unwrap_failed") ||
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 2b5059f55e40..26f08d41f2b1 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -108,6 +108,8 @@ static int sprint_name(char *str, const char *name, unsigned long offset)
#define DINFO_FPRINTF(dinfo, ...) \
((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
+#define bfd_vma_fmt \
+ __builtin_choose_expr(sizeof(bfd_vma) == sizeof(unsigned long), "%#lx <%s>", "%#llx <%s>")
static int disas_result_fprintf(struct disas_context *dctx,
const char *fmt, va_list ap)
@@ -170,10 +172,10 @@ static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
if (sym) {
sprint_name(symstr, sym->name, addr - sym->offset);
- DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
+ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr);
} else {
str = offstr(sec, addr);
- DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
+ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str);
free(str);
}
}
@@ -252,7 +254,7 @@ static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
* example: "lea 0x0(%rip),%rdi". The kernel can reference
* the next IP with _THIS_IP_ macro.
*/
- DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr);
+ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, "_THIS_IP_");
return;
}
@@ -264,11 +266,11 @@ static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
*/
if (reloc->sym->type == STT_SECTION) {
str = offstr(reloc->sym->sec, reloc->sym->offset + offset);
- DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
+ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str);
free(str);
} else {
sprint_name(symstr, reloc->sym->name, offset);
- DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
+ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr);
}
}
@@ -311,7 +313,7 @@ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
*/
sym = insn_call_dest(insn);
if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) {
- DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name);
+ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, sym->name);
return;
}
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 6a8ed9c62323..2c02c7b49265 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -18,15 +18,14 @@
#include <errno.h>
#include <libgen.h>
#include <ctype.h>
+#include <linux/align.h>
+#include <linux/kernel.h>
#include <linux/interval_tree_generic.h>
+#include <linux/log2.h>
#include <objtool/builtin.h>
#include <objtool/elf.h>
#include <objtool/warn.h>
-#define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1))
-#define ALIGN_UP_POW2(x) (1U << ((8 * sizeof(x)) - __builtin_clz((x) - 1U)))
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-
static inline u32 str_hash(const char *str)
{
return jhash(str, strlen(str), 0);
@@ -1336,7 +1335,7 @@ unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char
return -1;
}
- offset = ALIGN_UP(strtab->sh.sh_size, strtab->sh.sh_addralign);
+ offset = ALIGN(strtab->sh.sh_size, strtab->sh.sh_addralign);
if (!elf_add_data(elf, strtab, str, strlen(str) + 1))
return -1;
@@ -1378,7 +1377,7 @@ void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_
sec->data->d_size = size;
sec->data->d_align = 1;
- offset = ALIGN_UP(sec->sh.sh_size, sec->sh.sh_addralign);
+ offset = ALIGN(sec->sh.sh_size, sec->sh.sh_addralign);
sec->sh.sh_size = offset + size;
mark_sec_changed(elf, sec, true);
@@ -1502,7 +1501,7 @@ static int elf_alloc_reloc(struct elf *elf, struct section *rsec)
rsec->data->d_size = nr_relocs_new * elf_rela_size(elf);
rsec->sh.sh_size = rsec->data->d_size;
- nr_alloc = MAX(64, ALIGN_UP_POW2(nr_relocs_new));
+ nr_alloc = max(64UL, roundup_pow_of_two(nr_relocs_new));
if (nr_alloc <= rsec->nr_alloc_relocs)
return 0;
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 4d1f9e9977eb..d94531e3f64e 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1425,9 +1425,6 @@ static int clone_special_sections(struct elfs *e)
{
struct section *patched_sec;
- if (create_fake_symbols(e->patched))
- return -1;
-
for_each_sec(e->patched, patched_sec) {
if (is_special_section(patched_sec)) {
if (clone_special_section(e, patched_sec))
@@ -1704,6 +1701,17 @@ int cmd_klp_diff(int argc, const char **argv)
if (!e.out)
return -1;
+ /*
+ * Special section fake symbols are needed so that individual special
+ * section entries can be extracted by clone_special_sections().
+ *
+ * Note the fake symbols are also needed by clone_included_functions()
+ * because __WARN_printf() call sites add references to bug table
+ * entries in the calling functions.
+ */
+ if (create_fake_symbols(e.patched))
+ return -1;
+
if (clone_included_functions(&e))
return -1;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 17c1c36a7bf9..000c89a1e50d 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -251,8 +251,11 @@ __add_event(struct list_head *list, int *idx,
event_attr_init(attr);
evsel = evsel__new_idx(attr, *idx);
- if (!evsel)
- goto out_err;
+ if (!evsel) {
+ perf_cpu_map__put(cpus);
+ perf_cpu_map__put(pmu_cpus);
+ return NULL;
+ }
if (name) {
evsel->name = strdup(name);
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index b66ba04f19d9..45c4ea381bc3 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -48,6 +48,7 @@ TEST_PROGS := \
ipv6_flowlabel.sh \
ipv6_force_forwarding.sh \
ipv6_route_update_soft_lockup.sh \
+ ipvtap_test.sh \
l2_tos_ttl_inherit.sh \
l2tp.sh \
link_netns.py \
diff --git a/tools/testing/selftests/net/amt.sh b/tools/testing/selftests/net/amt.sh
index 3ef209cacb8e..663744305e52 100755
--- a/tools/testing/selftests/net/amt.sh
+++ b/tools/testing/selftests/net/amt.sh
@@ -73,6 +73,8 @@
# +------------------------+
#==============================================================================
+source lib.sh
+
readonly LISTENER=$(mktemp -u listener-XXXXXXXX)
readonly GATEWAY=$(mktemp -u gateway-XXXXXXXX)
readonly RELAY=$(mktemp -u relay-XXXXXXXX)
@@ -246,14 +248,15 @@ test_ipv6_forward()
send_mcast4()
{
- sleep 2
+ sleep 5
+ wait_local_port_listen ${LISTENER} 4000 udp
ip netns exec "${SOURCE}" bash -c \
'printf "%s %128s" 172.17.0.2 | nc -w 1 -u 239.0.0.1 4000' &
}
send_mcast6()
{
- sleep 2
+ wait_local_port_listen ${LISTENER} 6000 udp
ip netns exec "${SOURCE}" bash -c \
'printf "%s %128s" 2001:db8:3::2 | nc -w 1 -u ff0e::5:6 6000' &
}
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 1e1f253118f5..b84362b9b508 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -48,6 +48,7 @@ CONFIG_IPV6_SEG6_LWTUNNEL=y
CONFIG_IPV6_SIT=y
CONFIG_IPV6_VTI=y
CONFIG_IPVLAN=m
+CONFIG_IPVTAP=m
CONFIG_KALLSYMS=y
CONFIG_L2TP=m
CONFIG_L2TP_ETH=m
@@ -116,6 +117,7 @@ CONFIG_PROC_SYSCTL=y
CONFIG_PSAMPLE=m
CONFIG_RPS=y
CONFIG_SYSFS=y
+CONFIG_TAP=m
CONFIG_TCP_MD5SIG=y
CONFIG_TEST_BLACKHOLE_DEV=m
CONFIG_TEST_BPF=m
diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh
index 844a580ae74e..890c3f8e51bb 100755
--- a/tools/testing/selftests/net/fcnal-test.sh
+++ b/tools/testing/selftests/net/fcnal-test.sh
@@ -2327,6 +2327,13 @@ ipv6_ping_novrf()
log_test_addr ${a} $? 2 "ping local, device bind"
done
+ for a in ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSA_DEV} ${NSA_IP6}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ::1 ${a}
+ log_test_addr ${a} $? 0 "ping local, from localhost"
+ done
+
#
# ip rule blocks address
#
diff --git a/tools/testing/selftests/net/fib-onlink-tests.sh b/tools/testing/selftests/net/fib-onlink-tests.sh
index ec2d6ceb1f08..c01be076b210 100755
--- a/tools/testing/selftests/net/fib-onlink-tests.sh
+++ b/tools/testing/selftests/net/fib-onlink-tests.sh
@@ -120,7 +120,7 @@ log_subsection()
run_cmd()
{
- local cmd="$*"
+ local cmd="$1"
local out
local rc
@@ -145,7 +145,7 @@ get_linklocal()
local pfx
local addr
- addr=$(${pfx} ip -6 -br addr show dev ${dev} | \
+ addr=$(${pfx} ${IP} -6 -br addr show dev ${dev} | \
awk '{
for (i = 3; i <= NF; ++i) {
if ($i ~ /^fe80/)
@@ -173,58 +173,48 @@ setup()
set -e
- # create namespace
- setup_ns PEER_NS
+ # create namespaces
+ setup_ns ns1
+ IP="ip -netns $ns1"
+ setup_ns ns2
# add vrf table
- ip li add ${VRF} type vrf table ${VRF_TABLE}
- ip li set ${VRF} up
- ip ro add table ${VRF_TABLE} unreachable default metric 8192
- ip -6 ro add table ${VRF_TABLE} unreachable default metric 8192
+ ${IP} li add ${VRF} type vrf table ${VRF_TABLE}
+ ${IP} li set ${VRF} up
+ ${IP} ro add table ${VRF_TABLE} unreachable default metric 8192
+ ${IP} -6 ro add table ${VRF_TABLE} unreachable default metric 8192
# create test interfaces
- ip li add ${NETIFS[p1]} type veth peer name ${NETIFS[p2]}
- ip li add ${NETIFS[p3]} type veth peer name ${NETIFS[p4]}
- ip li add ${NETIFS[p5]} type veth peer name ${NETIFS[p6]}
- ip li add ${NETIFS[p7]} type veth peer name ${NETIFS[p8]}
+ ${IP} li add ${NETIFS[p1]} type veth peer name ${NETIFS[p2]}
+ ${IP} li add ${NETIFS[p3]} type veth peer name ${NETIFS[p4]}
+ ${IP} li add ${NETIFS[p5]} type veth peer name ${NETIFS[p6]}
+ ${IP} li add ${NETIFS[p7]} type veth peer name ${NETIFS[p8]}
# enslave vrf interfaces
for n in 5 7; do
- ip li set ${NETIFS[p${n}]} vrf ${VRF}
+ ${IP} li set ${NETIFS[p${n}]} vrf ${VRF}
done
# add addresses
for n in 1 3 5 7; do
- ip li set ${NETIFS[p${n}]} up
- ip addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
- ip addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]} nodad
+ ${IP} li set ${NETIFS[p${n}]} up
+ ${IP} addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
+ ${IP} addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]} nodad
done
# move peer interfaces to namespace and add addresses
for n in 2 4 6 8; do
- ip li set ${NETIFS[p${n}]} netns ${PEER_NS} up
- ip -netns ${PEER_NS} addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
- ip -netns ${PEER_NS} addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]} nodad
+ ${IP} li set ${NETIFS[p${n}]} netns ${ns2} up
+ ip -netns $ns2 addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
+ ip -netns $ns2 addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]} nodad
done
- ip -6 ro add default via ${V6ADDRS[p3]/::[0-9]/::64}
- ip -6 ro add table ${VRF_TABLE} default via ${V6ADDRS[p7]/::[0-9]/::64}
+ ${IP} -6 ro add default via ${V6ADDRS[p3]/::[0-9]/::64}
+ ${IP} -6 ro add table ${VRF_TABLE} default via ${V6ADDRS[p7]/::[0-9]/::64}
set +e
}
-cleanup()
-{
- # make sure we start from a clean slate
- cleanup_ns ${PEER_NS} 2>/dev/null
- for n in 1 3 5 7; do
- ip link del ${NETIFS[p${n}]} 2>/dev/null
- done
- ip link del ${VRF} 2>/dev/null
- ip ro flush table ${VRF_TABLE}
- ip -6 ro flush table ${VRF_TABLE}
-}
-
################################################################################
# IPv4 tests
#
@@ -241,7 +231,7 @@ run_ip()
# dev arg may be empty
[ -n "${dev}" ] && dev="dev ${dev}"
- run_cmd ip ro add table "${table}" "${prefix}"/32 via "${gw}" "${dev}" onlink
+ run_cmd "${IP} ro add table ${table} ${prefix}/32 via ${gw} ${dev} onlink"
log_test $? ${exp_rc} "${desc}"
}
@@ -257,8 +247,8 @@ run_ip_mpath()
# dev arg may be empty
[ -n "${dev}" ] && dev="dev ${dev}"
- run_cmd ip ro add table "${table}" "${prefix}"/32 \
- nexthop via ${nh1} nexthop via ${nh2}
+ run_cmd "${IP} ro add table ${table} ${prefix}/32 \
+ nexthop via ${nh1} nexthop via ${nh2}"
log_test $? ${exp_rc} "${desc}"
}
@@ -339,7 +329,7 @@ run_ip6()
# dev arg may be empty
[ -n "${dev}" ] && dev="dev ${dev}"
- run_cmd ip -6 ro add table "${table}" "${prefix}"/128 via "${gw}" "${dev}" onlink
+ run_cmd "${IP} -6 ro add table ${table} ${prefix}/128 via ${gw} ${dev} onlink"
log_test $? ${exp_rc} "${desc}"
}
@@ -353,8 +343,8 @@ run_ip6_mpath()
local exp_rc="$6"
local desc="$7"
- run_cmd ip -6 ro add table "${table}" "${prefix}"/128 "${opts}" \
- nexthop via ${nh1} nexthop via ${nh2}
+ run_cmd "${IP} -6 ro add table ${table} ${prefix}/128 ${opts} \
+ nexthop via ${nh1} nexthop via ${nh2}"
log_test $? ${exp_rc} "${desc}"
}
@@ -491,10 +481,9 @@ do
esac
done
-cleanup
setup
run_onlink_tests
-cleanup
+cleanup_ns ${ns1} ${ns2}
if [ "$TESTS" != "none" ]; then
printf "\nTests passed: %3d\n" ${nsuccess}
diff --git a/tools/testing/selftests/net/ipvtap_test.sh b/tools/testing/selftests/net/ipvtap_test.sh
new file mode 100755
index 000000000000..354ca7ce8584
--- /dev/null
+++ b/tools/testing/selftests/net/ipvtap_test.sh
@@ -0,0 +1,168 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Simple tests for ipvtap
+
+
+#
+# The testing environment looks this way:
+#
+# |------HNS-------| |------PHY-------|
+# | veth<----------------->veth |
+# |------|--|------| |----------------|
+# | |
+# | | |-----TST0-------|
+# | |------------|----ipvlan |
+# | |----------------|
+# |
+# | |-----TST1-------|
+# |---------------|----ipvlan |
+# |----------------|
+#
+
+ALL_TESTS="
+ test_ip_set
+"
+
+source lib.sh
+
+DEBUG=0
+
+VETH_HOST=vethtst.h
+VETH_PHY=vethtst.p
+
+NS_COUNT=32
+IP_ITERATIONS=1024
+IPSET_TIMEOUT="60s"
+
+ns_run() {
+ ns=$1
+ shift
+ if [[ "$ns" == "global" ]]; then
+ "$@" >/dev/null
+ else
+ ip netns exec "$ns" "$@" >/dev/null
+ fi
+}
+
+test_ip_setup_env() {
+ setup_ns NS_PHY
+ setup_ns HST_NS
+
+ # setup simulated other-host (phy) and host itself
+ ns_run "$HST_NS" ip link add $VETH_HOST type veth peer name $VETH_PHY \
+ netns "$NS_PHY" >/dev/null
+ ns_run "$HST_NS" ip link set $VETH_HOST up
+ ns_run "$NS_PHY" ip link set $VETH_PHY up
+
+ for ((i=0; i<NS_COUNT; i++)); do
+ setup_ns ipvlan_ns_$i
+ ns="ipvlan_ns_$i"
+ if [ "$DEBUG" = "1" ]; then
+ echo "created NS ${!ns}"
+ fi
+ if ! ns_run "$HST_NS" ip link add netns ${!ns} ipvlan0 \
+ link $VETH_HOST \
+ type ipvtap mode l2 bridge; then
+ exit_error "FAIL: Failed to configure ipvlan link."
+ fi
+ done
+}
+
+test_ip_cleanup_env() {
+ ns_run "$HST_NS" ip link del $VETH_HOST
+ cleanup_all_ns
+}
+
+exit_error() {
+ echo "$1"
+ exit $ksft_fail
+}
+
+rnd() {
+ echo $(( RANDOM % 32 + 16 ))
+}
+
+test_ip_set_thread() {
+ # Here we are trying to create some IP conflicts between namespaces.
+ # If just add/remove IP, nothing interesting will happen.
+ # But if add random IP and then remove random IP,
+ # eventually conflicts start to apear.
+ ip link set ipvlan0 up
+ for ((i=0; i<IP_ITERATIONS; i++)); do
+ v=$(rnd)
+ ip a a "172.25.0.$v/24" dev ipvlan0 2>/dev/null
+ ip a a "fc00::$v/64" dev ipvlan0 2>/dev/null
+ v=$(rnd)
+ ip a d "172.25.0.$v/24" dev ipvlan0 2>/dev/null
+ ip a d "fc00::$v/64" dev ipvlan0 2>/dev/null
+ done
+}
+
+test_ip_set() {
+ RET=0
+
+ trap test_ip_cleanup_env EXIT
+
+ test_ip_setup_env
+
+ declare -A ns_pids
+ for ((i=0; i<NS_COUNT; i++)); do
+ ns="ipvlan_ns_$i"
+ ns_run ${!ns} timeout "$IPSET_TIMEOUT" \
+ bash -c "$0 test_ip_set_thread"&
+ ns_pids[$i]=$!
+ done
+
+ for ((i=0; i<NS_COUNT; i++)); do
+ wait "${ns_pids[$i]}"
+ done
+
+ declare -A all_ips
+ for ((i=0; i<NS_COUNT; i++)); do
+ ns="ipvlan_ns_$i"
+ ip_output=$(ip netns exec ${!ns} ip a l dev ipvlan0 | grep inet)
+ while IFS= read -r nsip_out; do
+ if [[ -z $nsip_out ]]; then
+ continue;
+ fi
+ nsip=$(awk '{print $2}' <<< "$nsip_out")
+ if [[ -v all_ips[$nsip] ]]; then
+ RET=$ksft_fail
+ log_test "conflict for $nsip"
+ return "$RET"
+ else
+ all_ips[$nsip]=$i
+ fi
+ done <<< "$ip_output"
+ done
+
+ if [ "$DEBUG" = "1" ]; then
+ for key in "${!all_ips[@]}"; do
+ echo "$key: ${all_ips[$key]}"
+ done
+ fi
+
+ trap - EXIT
+ test_ip_cleanup_env
+
+ log_test "test multithreaded ip set"
+}
+
+if [[ "$1" == "-d" ]]; then
+ DEBUG=1
+ shift
+fi
+
+if [[ "$1" == "-t" ]]; then
+ shift
+ TESTS="$*"
+fi
+
+if [[ "$1" == "test_ip_set_thread" ]]; then
+ test_ip_set_thread
+else
+ require_command ip
+
+ tests_run
+fi
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
index b2e6e548f796..e70d3420954f 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
@@ -2329,17 +2329,16 @@ signal_address_tests()
ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1
speed=slow \
run_tests $ns1 $ns2 10.0.1.1
+ chk_join_nr 3 3 3
# It is not directly linked to the commit introducing this
# symbol but for the parent one which is linked anyway.
- if ! mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then
- chk_join_nr 3 3 2
- chk_add_nr 4 4
- else
- chk_join_nr 3 3 3
+ if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then
# the server will not signal the address terminating
# the MPC subflow
chk_add_nr 3 3
+ else
+ chk_add_nr 4 4
fi
fi
}
@@ -3847,21 +3846,28 @@ userspace_pm_chk_get_addr()
fi
}
-# $1: ns ; $2: event type ; $3: count
+# $1: ns ; $2: event type ; $3: count ; [ $4: attr ; $5: attr count ]
chk_evt_nr()
{
local ns=${1}
local evt_name="${2}"
local exp="${3}"
+ local attr="${4}"
+ local attr_exp="${5}"
local evts="${evts_ns1}"
local evt="${!evt_name}"
+ local attr_name
local count
+ if [ -n "${attr}" ]; then
+ attr_name=", ${attr}: ${attr_exp}"
+ fi
+
evt_name="${evt_name:16}" # without MPTCP_LIB_EVENT_
[ "${ns}" == "ns2" ] && evts="${evts_ns2}"
- print_check "event ${ns} ${evt_name} (${exp})"
+ print_check "event ${ns} ${evt_name} (${exp}${attr_name})"
if [[ "${evt_name}" = "LISTENER_"* ]] &&
! mptcp_lib_kallsyms_has "mptcp_event_pm_listener$"; then
@@ -3872,11 +3878,42 @@ chk_evt_nr()
count=$(grep -cw "type:${evt}" "${evts}")
if [ "${count}" != "${exp}" ]; then
fail_test "got ${count} events, expected ${exp}"
+ cat "${evts}"
+ return
+ elif [ -z "${attr}" ]; then
+ print_ok
+ return
+ fi
+
+ count=$(grep -w "type:${evt}" "${evts}" | grep -c ",${attr}:")
+ if [ "${count}" != "${attr_exp}" ]; then
+ fail_test "got ${count} event attributes, expected ${attr_exp}"
+ grep -w "type:${evt}" "${evts}"
else
print_ok
fi
}
+# $1: ns ; $2: event type ; $3: expected count
+wait_event()
+{
+ local ns="${1}"
+ local evt_name="${2}"
+ local exp="${3}"
+
+ local evt="${!evt_name}"
+ local evts="${evts_ns1}"
+ local count
+
+ [ "${ns}" == "ns2" ] && evts="${evts_ns2}"
+
+ for _ in $(seq 100); do
+ count=$(grep -cw "type:${evt}" "${evts}")
+ [ "${count}" -ge "${exp}" ] && break
+ sleep 0.1
+ done
+}
+
userspace_tests()
{
# userspace pm type prevents add_addr
@@ -4085,6 +4122,36 @@ userspace_tests()
kill_events_pids
mptcp_lib_kill_group_wait $tests_pid
fi
+
+ # userspace pm no duplicated spurious close events after an error
+ if reset_with_events "userspace pm no dup close events after error" &&
+ continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
+ set_userspace_pm $ns2
+ pm_nl_set_limits $ns1 0 2
+ { timeout_test=120 test_linkfail=128 speed=slow \
+ run_tests $ns1 $ns2 10.0.1.1 & } 2>/dev/null
+ local tests_pid=$!
+ wait_event ns2 MPTCP_LIB_EVENT_ESTABLISHED 1
+ userspace_pm_add_sf $ns2 10.0.3.2 20
+ chk_mptcp_info subflows 1 subflows 1
+ chk_subflows_total 2 2
+
+ # force quick loss
+ ip netns exec $ns2 sysctl -q net.ipv4.tcp_syn_retries=1
+ if ip netns exec "${ns1}" ${iptables} -A INPUT -s "10.0.1.2" \
+ -p tcp --tcp-option 30 -j REJECT --reject-with tcp-reset &&
+ ip netns exec "${ns2}" ${iptables} -A INPUT -d "10.0.1.2" \
+ -p tcp --tcp-option 30 -j REJECT --reject-with tcp-reset; then
+ wait_event ns2 MPTCP_LIB_EVENT_SUB_CLOSED 1
+ wait_event ns1 MPTCP_LIB_EVENT_SUB_CLOSED 1
+ chk_subflows_total 1 1
+ userspace_pm_add_sf $ns2 10.0.1.2 0
+ wait_event ns2 MPTCP_LIB_EVENT_SUB_CLOSED 2
+ chk_evt_nr ns2 MPTCP_LIB_EVENT_SUB_CLOSED 2 error 2
+ fi
+ kill_events_pids
+ mptcp_lib_kill_group_wait $tests_pid
+ fi
}
endpoint_tests()
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json
index e5cc31f265f8..0179c57104ad 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json
@@ -81,5 +81,30 @@
"$TC qdisc del dev $DUMMY handle 1: root",
"$IP link del dev $DUMMY"
]
+ },
+ {
+ "id": "124e",
+ "name": "Try to add teql as a child qdisc",
+ "category": [
+ "qdisc",
+ "ets",
+ "tbf"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY root handle 1: qfq",
+ "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 15 maxpkt 16384"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1:1 handle 2:1 teql0",
+ "expExitCode": "2",
+ "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:1",
+ "matchJSON": [],
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1:"
+ ]
}
]
diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
index 185ba553686a..f197ad9cc262 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -753,7 +753,7 @@ static int ublk_thread_is_idle(struct ublk_thread *t)
static int ublk_thread_is_done(struct ublk_thread *t)
{
- return (t->state & UBLKS_T_STOPPING) && ublk_thread_is_idle(t);
+ return (t->state & UBLKS_T_STOPPING) && ublk_thread_is_idle(t) && !t->cmd_inflight;
}
static inline void ublksrv_handle_tgt_cqe(struct ublk_thread *t,
@@ -1054,7 +1054,9 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev)
}
if (ret < 0) {
ublk_err("%s: ublk_ctrl_start_dev failed: %d\n", __func__, ret);
- goto fail;
+ /* stop device so that inflight uring_cmd can be cancelled */
+ ublk_ctrl_stop_dev(dev);
+ goto fail_start;
}
ublk_ctrl_get_info(dev);
@@ -1062,7 +1064,7 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev)
ublk_ctrl_dump(dev);
else
ublk_send_dev_event(ctx, dev, dev->dev_info.dev_id);
-
+fail_start:
/* wait until we are terminated */
for (i = 0; i < dev->nthreads; i++)
pthread_join(tinfo[i].thread, &thread_ret);
@@ -1272,7 +1274,7 @@ static int __cmd_dev_add(const struct dev_ctx *ctx)
}
ret = ublk_start_daemon(ctx, dev);
- ublk_dbg(UBLK_DBG_DEV, "%s: daemon exit %d\b", ret);
+ ublk_dbg(UBLK_DBG_DEV, "%s: daemon exit %d\n", __func__, ret);
if (ret < 0)
ublk_ctrl_del_dev(dev);
@@ -1618,6 +1620,7 @@ int main(int argc, char *argv[])
int option_idx, opt;
const char *cmd = argv[1];
struct dev_ctx ctx = {
+ ._evtfd = -1,
.queue_depth = 128,
.nr_hw_queues = 2,
.dev_id = -1,
diff --git a/tools/testing/selftests/vDSO/vgetrandom-chacha.S b/tools/testing/selftests/vDSO/vgetrandom-chacha.S
index a4a82e1c28a9..8c3cbf4dfd6a 100644
--- a/tools/testing/selftests/vDSO/vgetrandom-chacha.S
+++ b/tools/testing/selftests/vDSO/vgetrandom-chacha.S
@@ -14,7 +14,7 @@
#elif defined(__riscv) && __riscv_xlen == 64
#include "../../../../arch/riscv/kernel/vdso/vgetrandom-chacha.S"
#elif defined(__s390x__)
-#include "../../../../arch/s390/kernel/vdso64/vgetrandom-chacha.S"
+#include "../../../../arch/s390/kernel/vdso/vgetrandom-chacha.S"
#elif defined(__x86_64__)
#include "../../../../arch/x86/entry/vdso/vgetrandom-chacha.S"
#endif
diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h
index 142c02a6834a..bf633cde82b0 100644
--- a/tools/testing/vsock/util.h
+++ b/tools/testing/vsock/util.h
@@ -25,7 +25,7 @@ enum transport {
};
static const char * const transport_ksyms[] = {
- #define x(name, symbol) "d " symbol "_transport",
+ #define x(name, symbol) " " symbol "_transport",
KNOWN_TRANSPORTS(x)
#undef x
};
diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
index bbe3723babdc..5bd20ccd9335 100644
--- a/tools/testing/vsock/vsock_test.c
+++ b/tools/testing/vsock/vsock_test.c
@@ -347,10 +347,12 @@ static void test_stream_msg_peek_server(const struct test_opts *opts)
}
#define SOCK_BUF_SIZE (2 * 1024 * 1024)
+#define SOCK_BUF_SIZE_SMALL (64 * 1024)
#define MAX_MSG_PAGES 4
static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
{
+ unsigned long long sock_buf_size;
unsigned long curr_hash;
size_t max_msg_size;
int page_size;
@@ -363,6 +365,16 @@ static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
exit(EXIT_FAILURE);
}
+ sock_buf_size = SOCK_BUF_SIZE;
+
+ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
+ sock_buf_size,
+ "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
+
+ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
+ sock_buf_size,
+ "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+
/* Wait, until receiver sets buffer size. */
control_expectln("SRVREADY");
@@ -2219,6 +2231,101 @@ static void test_stream_accepted_setsockopt_server(const struct test_opts *opts)
close(fd);
}
+static void test_stream_tx_credit_bounds_client(const struct test_opts *opts)
+{
+ unsigned long long sock_buf_size;
+ size_t total = 0;
+ char buf[4096];
+ int fd;
+
+ memset(buf, 'A', sizeof(buf));
+
+ fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
+ if (fd < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ sock_buf_size = SOCK_BUF_SIZE_SMALL;
+
+ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
+ sock_buf_size,
+ "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
+
+ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
+ sock_buf_size,
+ "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
+ perror("fcntl(F_SETFL)");
+ exit(EXIT_FAILURE);
+ }
+
+ control_expectln("SRVREADY");
+
+ for (;;) {
+ ssize_t sent = send(fd, buf, sizeof(buf), 0);
+
+ if (sent == 0) {
+ fprintf(stderr, "unexpected EOF while sending bytes\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sent < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+
+ perror("send");
+ exit(EXIT_FAILURE);
+ }
+
+ total += sent;
+ }
+
+ control_writeln("CLIDONE");
+ close(fd);
+
+ /* We should not be able to send more bytes than the value set as
+ * local buffer size.
+ */
+ if (total > sock_buf_size) {
+ fprintf(stderr,
+ "TX credit too large: queued %zu bytes (expected <= %llu)\n",
+ total, sock_buf_size);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_stream_tx_credit_bounds_server(const struct test_opts *opts)
+{
+ unsigned long long sock_buf_size;
+ int fd;
+
+ fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
+ if (fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ sock_buf_size = SOCK_BUF_SIZE;
+
+ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
+ sock_buf_size,
+ "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
+
+ setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
+ sock_buf_size,
+ "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
+
+ control_writeln("SRVREADY");
+ control_expectln("CLIDONE");
+
+ close(fd);
+}
+
static struct test_case test_cases[] = {
{
.name = "SOCK_STREAM connection reset",
@@ -2403,6 +2510,16 @@ static struct test_case test_cases[] = {
.run_client = test_stream_accepted_setsockopt_client,
.run_server = test_stream_accepted_setsockopt_server,
},
+ {
+ .name = "SOCK_STREAM virtio MSG_ZEROCOPY coalescence corruption",
+ .run_client = test_stream_msgzcopy_mangle_client,
+ .run_server = test_stream_msgzcopy_mangle_server,
+ },
+ {
+ .name = "SOCK_STREAM TX credit bounds",
+ .run_client = test_stream_tx_credit_bounds_client,
+ .run_server = test_stream_tx_credit_bounds_server,
+ },
{},
};
diff --git a/tools/testing/vsock/vsock_test_zerocopy.c b/tools/testing/vsock/vsock_test_zerocopy.c
index 9d9a6cb9614a..a31ddfc1cd0c 100644
--- a/tools/testing/vsock/vsock_test_zerocopy.c
+++ b/tools/testing/vsock/vsock_test_zerocopy.c
@@ -9,14 +9,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <poll.h>
#include <linux/errqueue.h>
#include <linux/kernel.h>
+#include <linux/sockios.h>
+#include <linux/time64.h>
#include <errno.h>
#include "control.h"
+#include "timeout.h"
#include "vsock_test_zerocopy.h"
#include "msg_zerocopy_common.h"
@@ -356,3 +360,73 @@ void test_stream_msgzcopy_empty_errq_server(const struct test_opts *opts)
control_expectln("DONE");
close(fd);
}
+
+#define GOOD_COPY_LEN 128 /* net/vmw_vsock/virtio_transport_common.c */
+
+void test_stream_msgzcopy_mangle_client(const struct test_opts *opts)
+{
+ char sbuf1[PAGE_SIZE + 1], sbuf2[GOOD_COPY_LEN];
+ unsigned long hash;
+ struct pollfd fds;
+ int fd, i;
+
+ fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
+ if (fd < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ enable_so_zerocopy_check(fd);
+
+ memset(sbuf1, 'x', sizeof(sbuf1));
+ send_buf(fd, sbuf1, sizeof(sbuf1), 0, sizeof(sbuf1));
+
+ for (i = 0; i < sizeof(sbuf2); i++)
+ sbuf2[i] = rand() & 0xff;
+
+ send_buf(fd, sbuf2, sizeof(sbuf2), MSG_ZEROCOPY, sizeof(sbuf2));
+
+ hash = hash_djb2(sbuf2, sizeof(sbuf2));
+ control_writeulong(hash);
+
+ fds.fd = fd;
+ fds.events = 0;
+
+ if (poll(&fds, 1, TIMEOUT * MSEC_PER_SEC) != 1 ||
+ !(fds.revents & POLLERR)) {
+ perror("poll");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+}
+
+void test_stream_msgzcopy_mangle_server(const struct test_opts *opts)
+{
+ unsigned long local_hash, remote_hash;
+ char rbuf[PAGE_SIZE + 1];
+ int fd;
+
+ fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
+ if (fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Wait, don't race the (buggy) skbs coalescence. */
+ vsock_ioctl_int(fd, SIOCINQ, PAGE_SIZE + 1 + GOOD_COPY_LEN);
+
+ /* Discard the first packet. */
+ recv_buf(fd, rbuf, PAGE_SIZE + 1, 0, PAGE_SIZE + 1);
+
+ recv_buf(fd, rbuf, GOOD_COPY_LEN, 0, GOOD_COPY_LEN);
+ remote_hash = control_readulong();
+ local_hash = hash_djb2(rbuf, GOOD_COPY_LEN);
+
+ if (local_hash != remote_hash) {
+ fprintf(stderr, "Data received corrupted\n");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+}
diff --git a/tools/testing/vsock/vsock_test_zerocopy.h b/tools/testing/vsock/vsock_test_zerocopy.h
index 3ef2579e024d..d46c91a69f16 100644
--- a/tools/testing/vsock/vsock_test_zerocopy.h
+++ b/tools/testing/vsock/vsock_test_zerocopy.h
@@ -12,4 +12,7 @@ void test_seqpacket_msgzcopy_server(const struct test_opts *opts);
void test_stream_msgzcopy_empty_errq_client(const struct test_opts *opts);
void test_stream_msgzcopy_empty_errq_server(const struct test_opts *opts);
+void test_stream_msgzcopy_mangle_client(const struct test_opts *opts);
+void test_stream_msgzcopy_mangle_server(const struct test_opts *opts);
+
#endif /* VSOCK_TEST_ZEROCOPY_H */