starfive: add patches for 6.6
authorZoltan HERPAI <wigyori@uid0.hu>
Sun, 1 Sep 2024 14:06:29 +0000 (14:06 +0000)
committerZoltan HERPAI <wigyori@uid0.hu>
Mon, 2 Sep 2024 22:03:19 +0000 (00:03 +0200)
Add updated patches for 6.6. DMA/cache-handling patches
have been reworked / backported from upstream.

Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
140 files changed:
target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0071-regulator-starfive-jh7110-Add-regulator-support-for-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0086-dt-bindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0087-media-i2c-Add-imx708-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0088-media-i2c-imx708-Delete-gain.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0089-dt-bindings-display-Add-yamls-for-JH7110-display-sys.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0090-soc-starfive-jh71xx_pmu-Add-EVENT_TURN_OFF-register-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0091-workqueue-Enable-flush_scheduled_work.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0092-riscv-Optimize-memcpy-with-aligned-version.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0093-riscv-purgatory-Change-memcpy-to-the-aligned-version.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0094-Add-16-ISP-controls-remove-the-frame-SYNC-event-to-v.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0095-Expand-2-bytes-after-the-SC-buffer-for-the-AE-AWB-fl.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0096-Add-ISP-control-for-video2-and-video3.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0097-media-starfive-Update-ISP-initialzation.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0098-crypto-jh7110-Comment-RSA-algo-register.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0099-riscv-dts-starfive-jh7110-evb-Add-qspi-norflash-part.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0100-driver-regulator-pmic-driver-support-kernel-6.6.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0101-spi-pl022-starfive-Add-platform-bus-register-to-adap.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0102-spi-pl022-starfive-Avoid-power-device-error-when-CON.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0103-spi-pl022-starfive-fix-the-problem-of-spi-overlay-re.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0104-spi-pl022-starfive-Enable-spi-to-be-compiled-into-mo.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0105-riscv-configs-add-visionfive2-defconfig-to-kernel-6..patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0106-riscv-dts-starfive-update-dts-to-kernel-6.6.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0107-riscv-dts-starfive-evb-overlay-Support-SPI-overlay.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0108-riscv-configs-visionfive2-Add-standard-partition-for.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0109-usb-xhci-To-improve-performance-usb-using-lowmem-for.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0110-usb-xhci-using-dma_alloc_noncoherent-to-alloc-low-me.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0111-riscv-dts-starfive-Add-vf2-overlay-dtso-subdir.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0112-riscv-dts-starfive-visionfive-2-Add-aliases-for-i2c-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0113-driver-bluetooth-add-aic8800-driver-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0114-riscv-dts-starfive-visionfive-2-Sync-the-sound-card-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0115-clk-starfive-jh7110-Change-uart3-uart5-clk-register-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/0116-riscv-dts-starfive-visionfive-2-Quote-corresponding-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1000-serial-8250_dw-Add-starfive-jh7100-hsuart-compatible.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1001-RISC-V-Add-StarFive-JH7100-audio-clock-node.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1002-drivers-tty-serial-8250-update-driver-for-JH7100.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1003-dmaengine-dw-axi-dmac-Handle-xfer-start-while-non-id.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1004-dmaengine-dw-axi-dmac-Add-StarFive-JH7100-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1005-pinctrl-starfive-Reset-pinmux-settings.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1006-clk-starfive-Add-flags-argument-to-JH71X0__MUX-macro.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1007-clk-starfive-jh7100-Add-CLK_SET_RATE_PARENT-to-gmac_.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1008-clk-starfive-jh7100-Keep-more-clocks-alive.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1009-net-stmmac-use-GFP_DMA32.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1010-hwrng-Add-StarFive-JH7100-Random-Number-Generator-dr.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1011-pwm-sifive-ptc-Add-SiFive-PWM-PTC-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1012-dt-bindings-reset-Add-StarFive-JH7100-audio-reset-de.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1013-dt-bindings-reset-Add-starfive-jh7100-audrst-binding.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1014-reset-starfive-Add-JH7100-audio-reset-driver.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1015-RISC-V-Add-StarFive-JH7100-audio-reset-node.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1016-soc-sifive-ccache-Add-StarFive-JH7100-support.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1017-riscv-errata-Add-StarFive-JH7100-errata.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1018-riscv-dts-starfive-Mark-the-JH7100-as-having-non-coh.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1019-riscv-dts-starfive-Add-JH7100-cache-controller.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1020-riscv-dts-starfive-Add-pool-for-coherent-DMA-memory-.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1021-riscv-dts-starfive-Add-JH7100-MMC-nodes.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1022-riscv-dts-starfive-Enable-SD-card-on-JH7100-boards.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1023-riscv-errata-Make-ERRATA_STARFIVE_JH7100-depend-on-D.patch [new file with mode: 0644]
target/linux/starfive/patches-6.6/1024-riscv-dts-Add-full-JH7100-Starlight-and-VisionFive-s.patch [new file with mode: 0644]

diff --git a/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch b/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch
new file mode 100644 (file)
index 0000000..d21f5ef
--- /dev/null
@@ -0,0 +1,76 @@
+From 69275b667bd930cf5d5f577ba0ab1987c9d13987 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Mon, 21 Aug 2023 23:29:15 +0800
+Subject: [PATCH 001/116] clk: starfive: jh7110-sys: Fix lower rate of CPUfreq
+ by setting PLL0 rate to 1.5GHz
+
+CPUfreq supports 4 cpu frequency loads on 375/500/750/1500MHz.
+But now PLL0 rate is 1GHz and the cpu frequency loads become
+333/500/500/1000MHz in fact.
+
+So PLL0 rate should be set to 1.5GHz. Change the parent of cpu_root clock
+and the divider of cpu_core before the setting.
+
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Fixes: e2c510d6d630 ("riscv: dts: starfive: Add cpu scaling for JH7110 SoC")
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+---
+ .../clk/starfive/clk-starfive-jh7110-sys.c    | 47 ++++++++++++++++++-
+ 1 file changed, 46 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c
+@@ -501,7 +501,52 @@ static int __init jh7110_syscrg_probe(st
+       if (ret)
+               return ret;
+-      return jh7110_reset_controller_register(priv, "rst-sys", 0);
++      ret = jh7110_reset_controller_register(priv, "rst-sys", 0);
++      if (ret)
++              return ret;
++
++      /*
++       * Set PLL0 rate to 1.5GHz
++       * In order to not affect the cpu when the PLL0 rate is changing,
++       * we need to switch the parent of cpu_root clock to osc clock first,
++       * and then switch back after setting the PLL0 rate.
++       */
++      pllclk = clk_get(priv->dev, "pll0_out");
++      if (!IS_ERR(pllclk)) {
++              struct clk *osc = clk_get(&pdev->dev, "osc");
++              struct clk *cpu_root = priv->reg[JH7110_SYSCLK_CPU_ROOT].hw.clk;
++              struct clk *cpu_core = priv->reg[JH7110_SYSCLK_CPU_CORE].hw.clk;
++
++              if (IS_ERR(osc)) {
++                      clk_put(pllclk);
++                      return PTR_ERR(osc);
++              }
++
++              /*
++               * CPU need voltage regulation by CPUfreq if set 1.5GHz.
++               * So in this driver, cpu_core need to be set the divider to be 2 first
++               * and will be 750M after setting parent.
++               */
++              ret = clk_set_rate(cpu_core, clk_get_rate(cpu_core) / 2);
++              if (ret)
++                      goto failed_set;
++
++              ret = clk_set_parent(cpu_root, osc);
++              if (ret)
++                      goto failed_set;
++
++              ret = clk_set_rate(pllclk, 1500000000);
++              if (ret)
++                      goto failed_set;
++
++              ret = clk_set_parent(cpu_root, pllclk);
++
++failed_set:
++              clk_put(pllclk);
++              clk_put(osc);
++      }
++
++      return ret;
+ }
+ static const struct of_device_id jh7110_syscrg_match[] = {
diff --git a/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch b/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch
new file mode 100644 (file)
index 0000000..2b5318b
--- /dev/null
@@ -0,0 +1,114 @@
+From 7d0dbcbc079e4f72b69f53442b7759da6ebc4f87 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Thu, 19 Oct 2023 13:34:59 +0800
+Subject: [PATCH 002/116] dt-bindings: timer: Add timer for StarFive JH7110 SoC
+
+Add bindings for the timer on the JH7110 RISC-V SoC
+by StarFive Technology Ltd.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+---
+ .../bindings/timer/starfive,jh7110-timer.yaml | 96 +++++++++++++++++++
+ 1 file changed, 96 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml
+@@ -0,0 +1,96 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7110 Timer
++
++maintainers:
++  - Xingyu Wu <xingyu.wu@starfivetech.com>
++  - Samin Guo <samin.guo@starfivetech.com>
++
++description:
++  This timer has four free-running 32 bit counters in StarFive JH7110 SoC.
++  And each channel(counter) triggers an interrupt when timeout. They support
++  one-shot mode and continuous-run mode.
++
++properties:
++  compatible:
++    const: starfive,jh7110-timer
++
++  reg:
++    maxItems: 1
++
++  interrupts:
++    items:
++      - description: channel 0
++      - description: channel 1
++      - description: channel 2
++      - description: channel 3
++
++  clocks:
++    items:
++      - description: timer APB
++      - description: channel 0
++      - description: channel 1
++      - description: channel 2
++      - description: channel 3
++
++  clock-names:
++    items:
++      - const: apb
++      - const: ch0
++      - const: ch1
++      - const: ch2
++      - const: ch3
++
++  resets:
++    items:
++      - description: timer APB
++      - description: channel 0
++      - description: channel 1
++      - description: channel 2
++      - description: channel 3
++
++  reset-names:
++    items:
++      - const: apb
++      - const: ch0
++      - const: ch1
++      - const: ch2
++      - const: ch3
++
++required:
++  - compatible
++  - reg
++  - interrupts
++  - clocks
++  - clock-names
++  - resets
++  - reset-names
++
++additionalProperties: false
++
++examples:
++  - |
++    timer@13050000 {
++        compatible = "starfive,jh7110-timer";
++        reg = <0x13050000 0x10000>;
++        interrupts = <69>, <70>, <71> ,<72>;
++        clocks = <&clk 124>,
++                 <&clk 125>,
++                 <&clk 126>,
++                 <&clk 127>,
++                 <&clk 128>;
++        clock-names = "apb", "ch0", "ch1",
++                      "ch2", "ch3";
++        resets = <&rst 117>,
++                 <&rst 118>,
++                 <&rst 119>,
++                 <&rst 120>,
++                 <&rst 121>;
++        reset-names = "apb", "ch0", "ch1",
++                      "ch2", "ch3";
++    };
++
diff --git a/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch b/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch
new file mode 100644 (file)
index 0000000..68b9c38
--- /dev/null
@@ -0,0 +1,428 @@
+From 7cb47848f8a10aed6e050c0ea483b4bb5eaa62a4 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Thu, 19 Oct 2023 13:35:00 +0800
+Subject: [PATCH 003/116] clocksource: Add JH7110 timer driver
+
+Add timer driver for the StarFive JH7110 SoC.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+---
+ drivers/clocksource/Kconfig        |  11 +
+ drivers/clocksource/Makefile       |   1 +
+ drivers/clocksource/timer-jh7110.c | 380 +++++++++++++++++++++++++++++
+ 3 files changed, 392 insertions(+)
+ create mode 100644 drivers/clocksource/timer-jh7110.c
+
+--- a/drivers/clocksource/Kconfig
++++ b/drivers/clocksource/Kconfig
+@@ -641,6 +641,17 @@ config RISCV_TIMER
+         is accessed via both the SBI and the rdcycle instruction.  This is
+         required for all RISC-V systems.
++config STARFIVE_JH7110_TIMER
++      bool "Timer for the STARFIVE JH7110 SoC"
++      depends on ARCH_STARFIVE || COMPILE_TEST
++      select TIMER_OF
++      select CLKSRC_MMIO
++      default ARCH_STARFIVE
++      help
++        This enables the timer for StarFive JH7110 SoC. On RISC-V platform,
++        the system has started RISCV_TIMER, but you can also use this timer
++        which can provide four channels to do a lot more things on JH7110 SoC.
++
+ config CLINT_TIMER
+       bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST
+       depends on GENERIC_SCHED_CLOCK && RISCV
+--- a/drivers/clocksource/Makefile
++++ b/drivers/clocksource/Makefile
+@@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER)          += ingenic-
+ obj-$(CONFIG_CLKSRC_ST_LPC)           += clksrc_st_lpc.o
+ obj-$(CONFIG_X86_NUMACHIP)            += numachip.o
+ obj-$(CONFIG_RISCV_TIMER)             += timer-riscv.o
++obj-$(CONFIG_STARFIVE_JH7110_TIMER)   += timer-jh7110.o
+ obj-$(CONFIG_CLINT_TIMER)             += timer-clint.o
+ obj-$(CONFIG_CSKY_MP_TIMER)           += timer-mp-csky.o
+ obj-$(CONFIG_GX6605S_TIMER)           += timer-gx6605s.o
+--- /dev/null
++++ b/drivers/clocksource/timer-jh7110.c
+@@ -0,0 +1,380 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Starfive JH7110 Timer driver
++ *
++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
++ *
++ * Author:
++ * Xingyu Wu <xingyu.wu@starfivetech.com>
++ * Samin Guo <samin.guo@starfivetech.com>
++ */
++
++#include <linux/clk.h>
++#include <linux/clockchips.h>
++#include <linux/clocksource.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/irq.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/sched_clock.h>
++
++/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */
++#define JH7110_TIMER_CH_LEN           0x40
++#define JH7110_TIMER_CH_BASE(x)               ((x) * JH7110_TIMER_CH_LEN)
++#define JH7110_TIMER_CH_MAX           4
++
++#define JH7110_CLOCK_SOURCE_RATING    200
++#define JH7110_VALID_BITS             32
++#define JH7110_DELAY_US                       0
++#define JH7110_TIMEOUT_US             10000
++#define JH7110_CLOCKEVENT_RATING      300
++#define JH7110_TIMER_MAX_TICKS                0xffffffff
++#define JH7110_TIMER_MIN_TICKS                0xf
++#define JH7110_TIMER_RELOAD_VALUE     0
++
++#define JH7110_TIMER_INT_STATUS               0x00 /* RO[0:4]: Interrupt Status for channel0~4 */
++#define JH7110_TIMER_CTL              0x04 /* RW[0]: 0-continuous run, 1-single run */
++#define JH7110_TIMER_LOAD             0x08 /* RW: load value to counter */
++#define JH7110_TIMER_ENABLE           0x10 /* RW[0]: timer enable register */
++#define JH7110_TIMER_RELOAD           0x14 /* RW: write 1 or 0 both reload counter */
++#define JH7110_TIMER_VALUE            0x18 /* RO: timer value register */
++#define JH7110_TIMER_INT_CLR          0x20 /* RW: timer interrupt clear register */
++#define JH7110_TIMER_INT_MASK         0x24 /* RW[0]: timer interrupt mask register */
++
++#define JH7110_TIMER_INT_CLR_ENA      BIT(0)
++#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1)
++
++struct jh7110_clkevt {
++      struct clock_event_device evt;
++      struct clocksource cs;
++      bool cs_is_valid;
++      struct clk *clk;
++      struct reset_control *rst;
++      u32 rate;
++      u32 reload_val;
++      void __iomem *base;
++      char name[sizeof("jh7110-timer.chX")];
++};
++
++struct jh7110_timer_priv {
++      struct clk *pclk;
++      struct reset_control *prst;
++      struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX];
++};
++
++/* 0:continuous-run mode, 1:single-run mode */
++enum jh7110_timer_mode {
++      JH7110_TIMER_MODE_CONTIN,
++      JH7110_TIMER_MODE_SINGLE,
++};
++
++/* Interrupt Mask, 0:Unmask, 1:Mask */
++enum jh7110_timer_int_mask {
++      JH7110_TIMER_INT_ENA,
++      JH7110_TIMER_INT_DIS,
++};
++
++enum jh7110_timer_enable {
++      JH7110_TIMER_DIS,
++      JH7110_TIMER_ENA,
++};
++
++static inline struct jh7110_clkevt *to_jh7110_clkevt(struct clock_event_device *evt)
++{
++      return container_of(evt, struct jh7110_clkevt, evt);
++}
++
++/*
++ * BIT(0): Read value represent channel int status.
++ * Write 1 to this bit to clear interrupt. Write 0 has no effects.
++ * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written.
++ */
++static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt)
++{
++      u32 value;
++      int ret;
++
++      /* Waiting interrupt can be cleared */
++      ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, value,
++                                      !(value & JH7110_TIMER_INT_CLR_AVA_MASK),
++                                      JH7110_DELAY_US, JH7110_TIMEOUT_US);
++      if (!ret)
++              writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + JH7110_TIMER_INT_CLR);
++
++      return ret;
++}
++
++static int jh7110_timer_start(struct jh7110_clkevt *clkevt)
++{
++      int ret;
++
++      /* Disable and clear interrupt first */
++      writel(JH7110_TIMER_INT_DIS, clkevt->base + JH7110_TIMER_INT_MASK);
++      ret = jh7110_timer_int_clear(clkevt);
++      if (ret)
++              return ret;
++
++      writel(JH7110_TIMER_INT_ENA, clkevt->base + JH7110_TIMER_INT_MASK);
++      writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE);
++
++      return 0;
++}
++
++static int jh7110_timer_shutdown(struct clock_event_device *evt)
++{
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++      writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE);
++      return jh7110_timer_int_clear(clkevt);
++}
++
++static void jh7110_timer_suspend(struct clock_event_device *evt)
++{
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++      clkevt->reload_val = readl(clkevt->base + JH7110_TIMER_LOAD);
++      jh7110_timer_shutdown(evt);
++}
++
++static void jh7110_timer_resume(struct clock_event_device *evt)
++{
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++      writel(clkevt->reload_val, clkevt->base + JH7110_TIMER_LOAD);
++      writel(JH7110_TIMER_RELOAD_VALUE, clkevt->base + JH7110_TIMER_RELOAD);
++      jh7110_timer_start(clkevt);
++}
++
++static int jh7110_timer_tick_resume(struct clock_event_device *evt)
++{
++      jh7110_timer_resume(evt);
++
++      return 0;
++}
++
++/* IRQ handler for the timer */
++static irqreturn_t jh7110_timer_interrupt(int irq, void *priv)
++{
++      struct clock_event_device *evt = (struct clock_event_device *)priv;
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++      if (jh7110_timer_int_clear(clkevt))
++              return IRQ_NONE;
++
++      if (evt->event_handler)
++              evt->event_handler(evt);
++
++      return IRQ_HANDLED;
++}
++
++static int jh7110_timer_set_periodic(struct clock_event_device *evt)
++{
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++      u32 periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ);
++
++      writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL);
++      writel(periodic, clkevt->base + JH7110_TIMER_LOAD);
++
++      return jh7110_timer_start(clkevt);
++}
++
++static int jh7110_timer_set_oneshot(struct clock_event_device *evt)
++{
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++      writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL);
++      writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD);
++
++      return jh7110_timer_start(clkevt);
++}
++
++static int jh7110_timer_set_next_event(unsigned long next,
++                                     struct clock_event_device *evt)
++{
++      struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt);
++
++      writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL);
++      writel(next, clkevt->base + JH7110_TIMER_LOAD);
++
++      return jh7110_timer_start(clkevt);
++}
++
++static void jh7110_set_clockevent(struct clock_event_device *evt)
++{
++      evt->features = CLOCK_EVT_FEAT_PERIODIC |
++                      CLOCK_EVT_FEAT_ONESHOT |
++                      CLOCK_EVT_FEAT_DYNIRQ;
++      evt->set_state_shutdown = jh7110_timer_shutdown;
++      evt->set_state_periodic = jh7110_timer_set_periodic;
++      evt->set_state_oneshot = jh7110_timer_set_oneshot;
++      evt->set_state_oneshot_stopped = jh7110_timer_shutdown;
++      evt->tick_resume = jh7110_timer_tick_resume;
++      evt->set_next_event = jh7110_timer_set_next_event;
++      evt->suspend = jh7110_timer_suspend;
++      evt->resume = jh7110_timer_resume;
++      evt->rating = JH7110_CLOCKEVENT_RATING;
++}
++
++static u64 jh7110_timer_clocksource_read(struct clocksource *cs)
++{
++      struct jh7110_clkevt *clkevt = container_of(cs, struct jh7110_clkevt, cs);
++
++      return (u64)readl(clkevt->base + JH7110_TIMER_VALUE);
++}
++
++static int jh7110_clocksource_init(struct jh7110_clkevt *clkevt)
++{
++      int ret;
++
++      clkevt->cs.name = clkevt->name;
++      clkevt->cs.rating = JH7110_CLOCK_SOURCE_RATING;
++      clkevt->cs.read = jh7110_timer_clocksource_read;
++      clkevt->cs.mask = CLOCKSOURCE_MASK(JH7110_VALID_BITS);
++      clkevt->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
++
++      ret = clocksource_register_hz(&clkevt->cs, clkevt->rate);
++      if (ret)
++              return ret;
++
++      clkevt->cs_is_valid = true; /* clocksource register done */
++      writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL);
++      writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD);
++
++      return jh7110_timer_start(clkevt);
++}
++
++static void jh7110_clockevents_register(struct jh7110_clkevt *clkevt)
++{
++      clkevt->rate = clk_get_rate(clkevt->clk);
++
++      jh7110_set_clockevent(&clkevt->evt);
++      clkevt->evt.name = clkevt->name;
++      clkevt->evt.cpumask = cpu_possible_mask;
++
++      clockevents_config_and_register(&clkevt->evt, clkevt->rate,
++                                      JH7110_TIMER_MIN_TICKS, JH7110_TIMER_MAX_TICKS);
++}
++
++static void jh7110_timer_release(void *data)
++{
++      struct jh7110_timer_priv *priv = data;
++      int i;
++
++      for (i = 0; i < JH7110_TIMER_CH_MAX; i++) {
++              /* Disable each channel of timer */
++              if (priv->clkevt[i].base)
++                      writel(JH7110_TIMER_DIS, priv->clkevt[i].base + JH7110_TIMER_ENABLE);
++
++              /* Avoid no initialization in the loop of the probe */
++              if (!IS_ERR_OR_NULL(priv->clkevt[i].rst))
++                      reset_control_assert(priv->clkevt[i].rst);
++
++              if (priv->clkevt[i].cs_is_valid)
++                      clocksource_unregister(&priv->clkevt[i].cs);
++      }
++
++      reset_control_assert(priv->prst);
++}
++
++static int jh7110_timer_probe(struct platform_device *pdev)
++{
++      struct jh7110_timer_priv *priv;
++      struct jh7110_clkevt *clkevt;
++      char name[sizeof("chX")];
++      int ch;
++      int ret;
++      void __iomem *base;
++
++      priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(base))
++              return dev_err_probe(&pdev->dev, PTR_ERR(base),
++                                   "failed to map registers\n");
++
++      priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb");
++      if (IS_ERR(priv->prst))
++              return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst),
++                                   "failed to get apb reset\n");
++
++      priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb");
++      if (IS_ERR(priv->pclk))
++              return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk),
++                                   "failed to get & enable apb clock\n");
++
++      ret = reset_control_deassert(priv->prst);
++      if (ret)
++              return dev_err_probe(&pdev->dev, ret, "failed to deassert apb reset\n");
++
++      ret = devm_add_action_or_reset(&pdev->dev, jh7110_timer_release, priv);
++      if (ret)
++              return ret;
++
++      for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) {
++              clkevt = &priv->clkevt[ch];
++              snprintf(name, sizeof(name), "ch%d", ch);
++
++              clkevt->base = base + JH7110_TIMER_CH_BASE(ch);
++              /* Ensure timer is disabled */
++              writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE);
++
++              clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name);
++              if (IS_ERR(clkevt->rst))
++                      return PTR_ERR(clkevt->rst);
++
++              clkevt->clk = devm_clk_get_enabled(&pdev->dev, name);
++              if (IS_ERR(clkevt->clk))
++                      return PTR_ERR(clkevt->clk);
++
++              ret = reset_control_deassert(clkevt->rst);
++              if (ret)
++                      return ret;
++
++              clkevt->evt.irq = platform_get_irq(pdev, ch);
++              if (clkevt->evt.irq < 0)
++                      return clkevt->evt.irq;
++
++              snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", ch);
++              jh7110_clockevents_register(clkevt);
++
++              ret = devm_request_irq(&pdev->dev, clkevt->evt.irq, jh7110_timer_interrupt,
++                                     IRQF_TIMER | IRQF_IRQPOLL,
++                                     clkevt->name, &clkevt->evt);
++              if (ret)
++                      return ret;
++
++              ret = jh7110_clocksource_init(clkevt);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static const struct of_device_id jh7110_timer_match[] = {
++      { .compatible = "starfive,jh7110-timer", },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, jh7110_timer_match);
++
++static struct platform_driver jh7110_timer_driver = {
++      .probe = jh7110_timer_probe,
++      .driver = {
++              .name = "jh7110-timer",
++              .of_match_table = jh7110_timer_match,
++      },
++};
++module_platform_driver(jh7110_timer_driver);
++
++MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
++MODULE_DESCRIPTION("StarFive JH7110 timer driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch b/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch
new file mode 100644 (file)
index 0000000..9c1e78b
--- /dev/null
@@ -0,0 +1,34 @@
+From b513eb2cabee212ba1a23839f18c0026a21e653e Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Sep 2023 14:28:32 +0800
+Subject: [PATCH 004/116] dt-bindings: mmc: starfive: Remove properties from
+ required
+
+Due to the change of tuning implementation, it's no longer necessary to
+use the "starfive,sysreg" property in dts, so remove it from required.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Link: https://lore.kernel.org/r/20230922062834.39212-2-william.qiu@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml
++++ b/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml
+@@ -55,7 +55,6 @@ required:
+   - clocks
+   - clock-names
+   - interrupts
+-  - starfive,sysreg
+ unevaluatedProperties: false
+@@ -73,5 +72,4 @@ examples:
+         fifo-depth = <32>;
+         fifo-watermark-aligned;
+         data-addr = <0>;
+-        starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>;
+     };
diff --git a/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch b/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch
new file mode 100644 (file)
index 0000000..b8ea96f
--- /dev/null
@@ -0,0 +1,199 @@
+From 3ae8cec8fd28e18847edb67dfea04718c2f3369f Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Sep 2023 14:28:33 +0800
+Subject: [PATCH 005/116] mmc: starfive: Change tuning implementation
+
+Before, we used syscon to achieve tuning, but the actual measurement
+showed little effect, so the tuning implementation was modified here,
+and it was realized by reading and writing the UHS_REG_EXT register.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Reviewed-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Link: https://lore.kernel.org/r/20230922062834.39212-3-william.qiu@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/mmc/host/dw_mmc-starfive.c | 137 +++++++++--------------------
+ 1 file changed, 40 insertions(+), 97 deletions(-)
+
+--- a/drivers/mmc/host/dw_mmc-starfive.c
++++ b/drivers/mmc/host/dw_mmc-starfive.c
+@@ -5,6 +5,7 @@
+  * Copyright (c) 2022 StarFive Technology Co., Ltd.
+  */
++#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/mfd/syscon.h>
+@@ -20,13 +21,7 @@
+ #define ALL_INT_CLR           0x1ffff
+ #define MAX_DELAY_CHAIN               32
+-struct starfive_priv {
+-      struct device *dev;
+-      struct regmap *reg_syscon;
+-      u32 syscon_offset;
+-      u32 syscon_shift;
+-      u32 syscon_mask;
+-};
++#define STARFIVE_SMPL_PHASE     GENMASK(20, 16)
+ static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+ {
+@@ -44,117 +39,65 @@ static void dw_mci_starfive_set_ios(stru
+       }
+ }
++static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase)
++{
++      /* change driver phase and sample phase */
++      u32 reg_value = mci_readl(host, UHS_REG_EXT);
++
++      /* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */
++      reg_value &= ~STARFIVE_SMPL_PHASE;
++      reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase);
++      mci_writel(host, UHS_REG_EXT, reg_value);
++
++      /* We should delay 1ms wait for timing setting finished. */
++      mdelay(1);
++}
++
+ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
+                                            u32 opcode)
+ {
+       static const int grade  = MAX_DELAY_CHAIN;
+       struct dw_mci *host = slot->host;
+-      struct starfive_priv *priv = host->priv;
+-      int rise_point = -1, fall_point = -1;
+-      int err, prev_err = 0;
+-      int i;
+-      bool found = 0;
+-      u32 regval;
+-
+-      /*
+-       * Use grade as the max delay chain, and use the rise_point and
+-       * fall_point to ensure the best sampling point of a data input
+-       * signals.
+-       */
+-      for (i = 0; i < grade; i++) {
+-              regval = i << priv->syscon_shift;
+-              err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
+-                                              priv->syscon_mask, regval);
+-              if (err)
+-                      return err;
+-              mci_writel(host, RINTSTS, ALL_INT_CLR);
+-
+-              err = mmc_send_tuning(slot->mmc, opcode, NULL);
+-              if (!err)
+-                      found = 1;
+-
+-              if (i > 0) {
+-                      if (err && !prev_err)
+-                              fall_point = i - 1;
+-                      if (!err && prev_err)
+-                              rise_point = i;
+-              }
++      int smpl_phase, smpl_raise = -1, smpl_fall = -1;
++      int ret;
+-              if (rise_point != -1 && fall_point != -1)
+-                      goto tuning_out;
++      for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) {
++              dw_mci_starfive_set_sample_phase(host, smpl_phase);
++              mci_writel(host, RINTSTS, ALL_INT_CLR);
+-              prev_err = err;
+-              err = 0;
+-      }
++              ret = mmc_send_tuning(slot->mmc, opcode, NULL);
+-tuning_out:
+-      if (found) {
+-              if (rise_point == -1)
+-                      rise_point = 0;
+-              if (fall_point == -1)
+-                      fall_point = grade - 1;
+-              if (fall_point < rise_point) {
+-                      if ((rise_point + fall_point) >
+-                          (grade - 1))
+-                              i = fall_point / 2;
+-                      else
+-                              i = (rise_point + grade - 1) / 2;
+-              } else {
+-                      i = (rise_point + fall_point) / 2;
++              if (!ret && smpl_raise < 0) {
++                      smpl_raise = smpl_phase;
++              } else if (ret && smpl_raise >= 0) {
++                      smpl_fall = smpl_phase - 1;
++                      break;
+               }
+-
+-              regval = i << priv->syscon_shift;
+-              err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
+-                                              priv->syscon_mask, regval);
+-              if (err)
+-                      return err;
+-              mci_writel(host, RINTSTS, ALL_INT_CLR);
+-
+-              dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i);
+-      } else {
+-              dev_err(host->dev, "No valid delay chain! use default\n");
+-              err = -EINVAL;
+       }
+-      mci_writel(host, RINTSTS, ALL_INT_CLR);
+-      return err;
+-}
+-
+-static int dw_mci_starfive_parse_dt(struct dw_mci *host)
+-{
+-      struct of_phandle_args args;
+-      struct starfive_priv *priv;
+-      int ret;
++      if (smpl_phase >= grade)
++              smpl_fall = grade - 1;
+-      priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+-      if (!priv)
+-              return -ENOMEM;
+-
+-      ret = of_parse_phandle_with_fixed_args(host->dev->of_node,
+-                                              "starfive,sysreg", 3, 0, &args);
+-      if (ret) {
+-              dev_err(host->dev, "Failed to parse starfive,sysreg\n");
+-              return -EINVAL;
++      if (smpl_raise < 0) {
++              smpl_phase = 0;
++              dev_err(host->dev, "No valid delay chain! use default\n");
++              ret = -EINVAL;
++              goto out;
+       }
+-      priv->reg_syscon = syscon_node_to_regmap(args.np);
+-      of_node_put(args.np);
+-      if (IS_ERR(priv->reg_syscon))
+-              return PTR_ERR(priv->reg_syscon);
+-
+-      priv->syscon_offset = args.args[0];
+-      priv->syscon_shift  = args.args[1];
+-      priv->syscon_mask   = args.args[2];
+-
+-      host->priv = priv;
++      smpl_phase = (smpl_raise + smpl_fall) / 2;
++      dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase);
++      ret = 0;
+-      return 0;
++out:
++      dw_mci_starfive_set_sample_phase(host, smpl_phase);
++      mci_writel(host, RINTSTS, ALL_INT_CLR);
++      return ret;
+ }
+ static const struct dw_mci_drv_data starfive_data = {
+       .common_caps            = MMC_CAP_CMD23,
+       .set_ios                = dw_mci_starfive_set_ios,
+-      .parse_dt               = dw_mci_starfive_parse_dt,
+       .execute_tuning         = dw_mci_starfive_execute_tuning,
+ };
diff --git a/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch b/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch
new file mode 100644 (file)
index 0000000..aafbee5
--- /dev/null
@@ -0,0 +1,79 @@
+From e366df2ff64e9f93a5b35eea6a198b005d5a0911 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Dec 2023 17:45:45 +0800
+Subject: [PATCH 006/116] dt-bindings: pwm: Add bindings for OpenCores PWM
+ Controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add bindings for OpenCores PWM Controller.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+---
+ .../bindings/pwm/opencores,pwm.yaml           | 55 +++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
+@@ -0,0 +1,55 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: OpenCores PWM controller
++
++maintainers:
++  - William Qiu <william.qiu@starfivetech.com>
++
++description:
++  The OpenCores PTC ip core contains a PWM controller. When operating in PWM
++  mode, the PTC core generates binary signal with user-programmable low and
++  high periods. All PTC counters and registers are 32-bit.
++
++allOf:
++  - $ref: pwm.yaml#
++
++properties:
++  compatible:
++    items:
++      - enum:
++          - starfive,jh7100-pwm
++          - starfive,jh7110-pwm
++      - const: opencores,pwm-v1
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    maxItems: 1
++
++  resets:
++    maxItems: 1
++
++  "#pwm-cells":
++    const: 3
++
++required:
++  - compatible
++  - reg
++  - clocks
++
++additionalProperties: false
++
++examples:
++  - |
++    pwm@12490000 {
++        compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
++        reg = <0x12490000 0x10000>;
++        clocks = <&clkgen 181>;
++        resets = <&rstgen 109>;
++        #pwm-cells = <3>;
++    };
diff --git a/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch b/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch
new file mode 100644 (file)
index 0000000..27e510d
--- /dev/null
@@ -0,0 +1,285 @@
+From 644bfe581dde9b762460a4916da4d71c148be06e Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Fri, 22 Dec 2023 17:45:46 +0800
+Subject: [PATCH 007/116] pwm: opencores: Add PWM driver support
+
+Add driver for OpenCores PWM Controller. And add compatibility code
+which based on StarFive SoC.
+
+Co-developed-by: Hal Feng <hal.feng@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+---
+ drivers/pwm/Kconfig      |  12 ++
+ drivers/pwm/Makefile     |   1 +
+ drivers/pwm/pwm-ocores.c | 233 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 246 insertions(+)
+ create mode 100644 drivers/pwm/pwm-ocores.c
+
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -434,6 +434,18 @@ config PWM_NTXEC
+         controller found in certain e-book readers designed by the original
+         design manufacturer Netronix.
++config PWM_OCORES
++      tristate "OpenCores PWM support"
++      depends on HAS_IOMEM && OF
++      depends on COMMON_CLK
++      depends on ARCH_STARFIVE || COMPILE_TEST
++      help
++        If you say yes to this option, support will be included for the
++        OpenCores PWM. For details see https://opencores.org/projects/ptc.
++
++        To compile this driver as a module, choose M here: the module
++        will be called pwm-ocores.
++
+ config PWM_OMAP_DMTIMER
+       tristate "OMAP Dual-Mode Timer PWM support"
+       depends on OF
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -39,6 +39,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE)     += pwm-
+ obj-$(CONFIG_PWM_MTK_DISP)    += pwm-mtk-disp.o
+ obj-$(CONFIG_PWM_MXS)         += pwm-mxs.o
+ obj-$(CONFIG_PWM_NTXEC)               += pwm-ntxec.o
++obj-$(CONFIG_PWM_OCORES)      += pwm-ocores.o
+ obj-$(CONFIG_PWM_OMAP_DMTIMER)        += pwm-omap-dmtimer.o
+ obj-$(CONFIG_PWM_PCA9685)     += pwm-pca9685.o
+ obj-$(CONFIG_PWM_PXA)         += pwm-pxa.o
+--- /dev/null
++++ b/drivers/pwm/pwm-ocores.c
+@@ -0,0 +1,233 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * OpenCores PWM Driver
++ *
++ * https://opencores.org/projects/ptc
++ *
++ * Copyright (C) 2018-2023 StarFive Technology Co., Ltd.
++ *
++ * Limitations:
++ * - The hardware only do inverted polarity.
++ * - The hardware minimum period / duty_cycle is (1 / pwm_apb clock frequency) ns.
++ * - The hardware maximum period / duty_cycle is (U32_MAX / pwm_apb clock frequency) ns.
++ */
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++
++/* OCPWM_CTRL register bits*/
++#define REG_OCPWM_EN      BIT(0)
++#define REG_OCPWM_ECLK    BIT(1)
++#define REG_OCPWM_NEC     BIT(2)
++#define REG_OCPWM_OE      BIT(3)
++#define REG_OCPWM_SIGNLE  BIT(4)
++#define REG_OCPWM_INTE    BIT(5)
++#define REG_OCPWM_INT     BIT(6)
++#define REG_OCPWM_CNTRRST BIT(7)
++#define REG_OCPWM_CAPTE   BIT(8)
++
++struct ocores_pwm_device {
++      struct pwm_chip chip;
++      struct clk *clk;
++      struct reset_control *rst;
++      const struct ocores_pwm_data *data;
++      void __iomem *regs;
++      u32 clk_rate; /* PWM APB clock frequency */
++};
++
++struct ocores_pwm_data {
++      void __iomem *(*get_ch_base)(void __iomem *base, unsigned int channel);
++};
++
++static inline u32 ocores_readl(struct ocores_pwm_device *ddata,
++                             unsigned int channel,
++                             unsigned int offset)
++{
++      void __iomem *base = ddata->data->get_ch_base ?
++                           ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs;
++
++      return readl(base + offset);
++}
++
++static inline void ocores_writel(struct ocores_pwm_device *ddata,
++                               unsigned int channel,
++                               unsigned int offset, u32 val)
++{
++      void __iomem *base = ddata->data->get_ch_base ?
++                           ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs;
++
++      writel(val, base + offset);
++}
++
++static inline struct ocores_pwm_device *chip_to_ocores(struct pwm_chip *chip)
++{
++      return container_of(chip, struct ocores_pwm_device, chip);
++}
++
++static void __iomem *starfive_jh71x0_get_ch_base(void __iomem *base,
++                                               unsigned int channel)
++{
++      unsigned int offset = (channel > 3 ? 1 << 15 : 0) + (channel & 3) * 0x10;
++
++      return base + offset;
++}
++
++static int ocores_pwm_get_state(struct pwm_chip *chip,
++                              struct pwm_device *pwm,
++                              struct pwm_state *state)
++{
++      struct ocores_pwm_device *ddata = chip_to_ocores(chip);
++      u32 period_data, duty_data, ctrl_data;
++
++      period_data = ocores_readl(ddata, pwm->hwpwm, 0x8);
++      duty_data = ocores_readl(ddata, pwm->hwpwm, 0x4);
++      ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC);
++
++      state->period = DIV_ROUND_UP_ULL((u64)period_data * NSEC_PER_SEC, ddata->clk_rate);
++      state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty_data * NSEC_PER_SEC, ddata->clk_rate);
++      state->polarity = PWM_POLARITY_INVERSED;
++      state->enabled = (ctrl_data & REG_OCPWM_EN) ? true : false;
++
++      return 0;
++}
++
++static int ocores_pwm_apply(struct pwm_chip *chip,
++                          struct pwm_device *pwm,
++                          const struct pwm_state *state)
++{
++      struct ocores_pwm_device *ddata = chip_to_ocores(chip);
++      u32 ctrl_data = 0;
++      u64 period_data, duty_data;
++
++      if (state->polarity != PWM_POLARITY_INVERSED)
++              return -EINVAL;
++
++      ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC);
++      ocores_writel(ddata, pwm->hwpwm, 0xC, 0);
++
++      period_data = DIV_ROUND_DOWN_ULL(state->period * ddata->clk_rate, NSEC_PER_SEC);
++      if (period_data <= U32_MAX)
++              ocores_writel(ddata, pwm->hwpwm, 0x8, (u32)period_data);
++      else
++              return -EINVAL;
++
++      duty_data = DIV_ROUND_DOWN_ULL(state->duty_cycle * ddata->clk_rate, NSEC_PER_SEC);
++      if (duty_data <= U32_MAX)
++              ocores_writel(ddata, pwm->hwpwm, 0x4, (u32)duty_data);
++      else
++              return -EINVAL;
++
++      ocores_writel(ddata, pwm->hwpwm, 0xC, 0);
++
++      if (state->enabled) {
++              ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC);
++              ocores_writel(ddata, pwm->hwpwm, 0xC, ctrl_data | REG_OCPWM_EN | REG_OCPWM_OE);
++      }
++
++      return 0;
++}
++
++static const struct pwm_ops ocores_pwm_ops = {
++      .get_state      = ocores_pwm_get_state,
++      .apply          = ocores_pwm_apply,
++};
++
++static const struct ocores_pwm_data jh7100_pwm_data = {
++      .get_ch_base = starfive_jh71x0_get_ch_base,
++};
++
++static const struct ocores_pwm_data jh7110_pwm_data = {
++      .get_ch_base = starfive_jh71x0_get_ch_base,
++};
++
++static const struct of_device_id ocores_pwm_of_match[] = {
++      { .compatible = "opencores,pwm-v1" },
++      { .compatible = "starfive,jh7100-pwm", .data = &jh7100_pwm_data},
++      { .compatible = "starfive,jh7110-pwm", .data = &jh7110_pwm_data},
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ocores_pwm_of_match);
++
++static void ocores_reset_control_assert(void *data)
++{
++      reset_control_assert(data);
++}
++
++static int ocores_pwm_probe(struct platform_device *pdev)
++{
++      const struct of_device_id *id;
++      struct device *dev = &pdev->dev;
++      struct ocores_pwm_device *ddata;
++      struct pwm_chip *chip;
++      int ret;
++
++      id = of_match_device(ocores_pwm_of_match, dev);
++      if (!id)
++              return -EINVAL;
++
++      ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
++      if (!ddata)
++              return -ENOMEM;
++
++      ddata->data = id->data;
++      chip = &ddata->chip;
++      chip->dev = dev;
++      chip->ops = &ocores_pwm_ops;
++      chip->npwm = 8;
++      chip->of_pwm_n_cells = 3;
++
++      ddata->regs = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(ddata->regs))
++              return dev_err_probe(dev, PTR_ERR(ddata->regs),
++                                   "Unable to map IO resources\n");
++
++      ddata->clk = devm_clk_get_enabled(dev, NULL);
++      if (IS_ERR(ddata->clk))
++              return dev_err_probe(dev, PTR_ERR(ddata->clk),
++                                   "Unable to get pwm's clock\n");
++
++      ddata->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
++      if (IS_ERR(ddata->rst))
++              return dev_err_probe(dev, PTR_ERR(ddata->rst),
++                                   "Unable to get pwm's reset\n");
++
++      reset_control_deassert(ddata->rst);
++
++      ret = devm_add_action_or_reset(dev, ocores_reset_control_assert, ddata->rst);
++      if (ret)
++              return ret;
++
++      ddata->clk_rate = clk_get_rate(ddata->clk);
++      if (ddata->clk_rate <= 0)
++              return dev_err_probe(dev, ddata->clk_rate,
++                                   "Unable to get clock's rate\n");
++
++      ret = devm_pwmchip_add(dev, chip);
++      if (ret < 0)
++              return dev_err_probe(dev, ret, "Could not register PWM chip\n");
++
++      platform_set_drvdata(pdev, ddata);
++
++      return ret;
++}
++
++static struct platform_driver ocores_pwm_driver = {
++      .probe = ocores_pwm_probe,
++      .driver = {
++              .name = "ocores-pwm",
++              .of_match_table = ocores_pwm_of_match,
++      },
++};
++module_platform_driver(ocores_pwm_driver);
++
++MODULE_AUTHOR("Jieqin Chen");
++MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
++MODULE_DESCRIPTION("OpenCores PWM PTC driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch b/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch
new file mode 100644 (file)
index 0000000..a6ede31
--- /dev/null
@@ -0,0 +1,117 @@
+From 7d9521cad6474d45e9056176982e6da54d40bc19 Mon Sep 17 00:00:00 2001
+From: Eric Biggers <ebiggers@google.com>
+Date: Sun, 22 Oct 2023 01:10:42 -0700
+Subject: [PATCH 008/116] crypto: starfive - remove unnecessary alignmask for
+ ahashes
+
+The crypto API's support for alignmasks for ahash algorithms is nearly
+useless, as its only effect is to cause the API to align the key and
+result buffers.  The drivers that happen to be specifying an alignmask
+for ahash rarely actually need it.  When they do, it's easily fixable,
+especially considering that these buffers cannot be used for DMA.
+
+In preparation for removing alignmask support from ahash, this patch
+makes the starfive driver no longer use it.  This driver did actually
+rely on it, but only for storing to the result buffer using int stores
+in starfive_hash_copy_hash().  This patch makes
+starfive_hash_copy_hash() use put_unaligned() instead.  (It really
+should use a specific endianness, but that's an existing bug.)
+
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-hash.c | 13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-hash.c
++++ b/drivers/crypto/starfive/jh7110-hash.c
+@@ -209,7 +209,8 @@ static int starfive_hash_copy_hash(struc
+       data = (u32 *)req->result;
+       for (count = 0; count < mlen; count++)
+-              data[count] = readl(ctx->cryp->base + STARFIVE_HASH_SHARDR);
++              put_unaligned(readl(ctx->cryp->base + STARFIVE_HASH_SHARDR),
++                            &data[count]);
+       return 0;
+ }
+@@ -628,7 +629,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA224_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -658,7 +658,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA224_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -687,7 +686,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA256_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -717,7 +715,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA256_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -746,7 +743,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA384_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -776,7 +772,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA384_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -805,7 +800,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA512_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -835,7 +829,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SHA512_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -864,7 +857,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SM3_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
+@@ -894,7 +886,6 @@ static struct ahash_engine_alg algs_sha2
+                                                 CRYPTO_ALG_NEED_FALLBACK,
+                       .cra_blocksize          = SM3_BLOCK_SIZE,
+                       .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-                      .cra_alignmask          = 3,
+                       .cra_module             = THIS_MODULE,
+               }
+       },
diff --git a/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch b/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch
new file mode 100644 (file)
index 0000000..145c013
--- /dev/null
@@ -0,0 +1,25 @@
+From 52de0270ed6453727936b5a793dc367d75280e84 Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Wed, 15 Nov 2023 01:12:13 +0800
+Subject: [PATCH 009/116] crypto: starfive - Update driver dependencies
+
+Change AMBA_PL08X to required dependency as the hash ops depends on it
+for data transfer.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/crypto/starfive/Kconfig
++++ b/drivers/crypto/starfive/Kconfig
+@@ -4,7 +4,7 @@
+ config CRYPTO_DEV_JH7110
+       tristate "StarFive JH7110 cryptographic engine driver"
+-      depends on SOC_STARFIVE || AMBA_PL08X || COMPILE_TEST
++      depends on (SOC_STARFIVE && AMBA_PL08X) || COMPILE_TEST
+       depends on HAS_DMA
+       select CRYPTO_ENGINE
+       select CRYPTO_HMAC
diff --git a/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch b/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch
new file mode 100644 (file)
index 0000000..c94009d
--- /dev/null
@@ -0,0 +1,200 @@
+From 68a1bbb99455fd5ea80b7e21ec726f369abc9572 Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Wed, 15 Nov 2023 01:12:14 +0800
+Subject: [PATCH 010/116] crypto: starfive - RSA poll csr for done status
+
+Hardware could not clear irq status without resetting the entire module.
+Driver receives irq immediately when mask bit is cleared causing
+intermittent errors in RSA calculations. Switch to use csr polling for
+done status instead.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-cryp.c |  8 -----
+ drivers/crypto/starfive/jh7110-cryp.h | 10 +++++-
+ drivers/crypto/starfive/jh7110-rsa.c  | 49 +++++++--------------------
+ 3 files changed, 22 insertions(+), 45 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-cryp.c
++++ b/drivers/crypto/starfive/jh7110-cryp.c
+@@ -109,12 +109,6 @@ static irqreturn_t starfive_cryp_irq(int
+               tasklet_schedule(&cryp->hash_done);
+       }
+-      if (status & STARFIVE_IE_FLAG_PKA_DONE) {
+-              mask |= STARFIVE_IE_MASK_PKA_DONE;
+-              writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET);
+-              complete(&cryp->pka_done);
+-      }
+-
+       return IRQ_HANDLED;
+ }
+@@ -159,8 +153,6 @@ static int starfive_cryp_probe(struct pl
+               return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst),
+                                    "Error getting hardware reset line\n");
+-      init_completion(&cryp->pka_done);
+-
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+--- a/drivers/crypto/starfive/jh7110-cryp.h
++++ b/drivers/crypto/starfive/jh7110-cryp.h
+@@ -125,6 +125,15 @@ union starfive_pka_cacr {
+       };
+ };
++union starfive_pka_casr {
++      u32 v;
++      struct {
++#define STARFIVE_PKA_DONE                     BIT(0)
++              u32 done                        :1;
++              u32 rsvd_0                      :31;
++      };
++};
++
+ struct starfive_rsa_key {
+       u8      *n;
+       u8      *e;
+@@ -183,7 +192,6 @@ struct starfive_cryp_dev {
+       struct crypto_engine                    *engine;
+       struct tasklet_struct                   aes_done;
+       struct tasklet_struct                   hash_done;
+-      struct completion                       pka_done;
+       size_t                                  assoclen;
+       size_t                                  total_in;
+       size_t                                  total_out;
+--- a/drivers/crypto/starfive/jh7110-rsa.c
++++ b/drivers/crypto/starfive/jh7110-rsa.c
+@@ -6,13 +6,7 @@
+  */
+ #include <linux/crypto.h>
+-#include <linux/delay.h>
+-#include <linux/device.h>
+-#include <linux/dma-direct.h>
+-#include <linux/interrupt.h>
+ #include <linux/iopoll.h>
+-#include <linux/io.h>
+-#include <linux/mod_devicetable.h>
+ #include <crypto/akcipher.h>
+ #include <crypto/algapi.h>
+ #include <crypto/internal/akcipher.h>
+@@ -28,13 +22,13 @@
+ #define STARFIVE_PKA_CAER_OFFSET      (STARFIVE_PKA_REGS_OFFSET + 0x108)
+ #define STARFIVE_PKA_CANR_OFFSET      (STARFIVE_PKA_REGS_OFFSET + 0x208)
+-// R^2 mod N and N0'
++/* R ^ 2 mod N and N0' */
+ #define CRYPTO_CMD_PRE                        0x0
+-// A * R mod N   ==> A
++/* A * R mod N   ==> A */
+ #define CRYPTO_CMD_ARN                        0x5
+-// A * E * R mod N ==> A
++/* A * E * R mod N ==> A */
+ #define CRYPTO_CMD_AERN                       0x6
+-// A * A * R mod N ==> A
++/* A * A * R mod N ==> A */
+ #define CRYPTO_CMD_AARN                       0x7
+ #define STARFIVE_RSA_MAX_KEYSZ                256
+@@ -43,21 +37,10 @@
+ static inline int starfive_pka_wait_done(struct starfive_cryp_ctx *ctx)
+ {
+       struct starfive_cryp_dev *cryp = ctx->cryp;
++      u32 status;
+-      return wait_for_completion_timeout(&cryp->pka_done,
+-                                         usecs_to_jiffies(100000));
+-}
+-
+-static inline void starfive_pka_irq_mask_clear(struct starfive_cryp_ctx *ctx)
+-{
+-      struct starfive_cryp_dev *cryp = ctx->cryp;
+-      u32 stat;
+-
+-      stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET);
+-      stat &= ~STARFIVE_IE_MASK_PKA_DONE;
+-      writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET);
+-
+-      reinit_completion(&cryp->pka_done);
++      return readl_relaxed_poll_timeout(cryp->base + STARFIVE_PKA_CASR_OFFSET, status,
++                                        status & STARFIVE_PKA_DONE, 10, 100000);
+ }
+ static void starfive_rsa_free_key(struct starfive_rsa_key *key)
+@@ -114,10 +97,9 @@ static int starfive_rsa_montgomery_form(
+               rctx->csr.pka.not_r2 = 1;
+               rctx->csr.pka.ie = 1;
+-              starfive_pka_irq_mask_clear(ctx);
+               writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+-              if (!starfive_pka_wait_done(ctx))
++              if (starfive_pka_wait_done(ctx))
+                       return -ETIMEDOUT;
+               for (loop = 0; loop <= opsize; loop++)
+@@ -136,10 +118,9 @@ static int starfive_rsa_montgomery_form(
+               rctx->csr.pka.start = 1;
+               rctx->csr.pka.ie = 1;
+-              starfive_pka_irq_mask_clear(ctx);
+               writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+-              if (!starfive_pka_wait_done(ctx))
++              if (starfive_pka_wait_done(ctx))
+                       return -ETIMEDOUT;
+       } else {
+               rctx->csr.pka.v = 0;
+@@ -151,10 +132,9 @@ static int starfive_rsa_montgomery_form(
+               rctx->csr.pka.pre_expf = 1;
+               rctx->csr.pka.ie = 1;
+-              starfive_pka_irq_mask_clear(ctx);
+               writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+-              if (!starfive_pka_wait_done(ctx))
++              if (starfive_pka_wait_done(ctx))
+                       return -ETIMEDOUT;
+               for (loop = 0; loop <= count; loop++)
+@@ -172,10 +152,9 @@ static int starfive_rsa_montgomery_form(
+               rctx->csr.pka.start = 1;
+               rctx->csr.pka.ie = 1;
+-              starfive_pka_irq_mask_clear(ctx);
+               writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+-              if (!starfive_pka_wait_done(ctx))
++              if (starfive_pka_wait_done(ctx))
+                       return -ETIMEDOUT;
+       }
+@@ -226,11 +205,10 @@ static int starfive_rsa_cpu_start(struct
+               rctx->csr.pka.start = 1;
+               rctx->csr.pka.ie = 1;
+-              starfive_pka_irq_mask_clear(ctx);
+               writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+               ret = -ETIMEDOUT;
+-              if (!starfive_pka_wait_done(ctx))
++              if (starfive_pka_wait_done(ctx))
+                       goto rsa_err;
+               if (mlen) {
+@@ -242,10 +220,9 @@ static int starfive_rsa_cpu_start(struct
+                       rctx->csr.pka.start = 1;
+                       rctx->csr.pka.ie = 1;
+-                      starfive_pka_irq_mask_clear(ctx);
+                       writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET);
+-                      if (!starfive_pka_wait_done(ctx))
++                      if (starfive_pka_wait_done(ctx))
+                               goto rsa_err;
+               }
+       }
diff --git a/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch b/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch
new file mode 100644 (file)
index 0000000..268e405
--- /dev/null
@@ -0,0 +1,46 @@
+From eea9f2c55cf944bbd5cdd43eb655416a867846af Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Mon, 20 Nov 2023 11:12:42 +0800
+Subject: [PATCH 011/116] crypto: starfive - Pad adata with zeroes
+
+Aad requires padding with zeroes up to 15 bytes in some cases. This
+patch increases the allocated buffer size for aad and prevents the
+driver accessing uninitialized memory region.
+
+v1->v2: Specify reason for alloc size change in descriptions.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-aes.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-aes.c
++++ b/drivers/crypto/starfive/jh7110-aes.c
+@@ -500,7 +500,7 @@ static int starfive_aes_prepare_req(stru
+       scatterwalk_start(&cryp->out_walk, rctx->out_sg);
+       if (cryp->assoclen) {
+-              rctx->adata = kzalloc(ALIGN(cryp->assoclen, AES_BLOCK_SIZE), GFP_KERNEL);
++              rctx->adata = kzalloc(cryp->assoclen + AES_BLOCK_SIZE, GFP_KERNEL);
+               if (!rctx->adata)
+                       return dev_err_probe(cryp->dev, -ENOMEM,
+                                            "Failed to alloc memory for adata");
+@@ -569,7 +569,7 @@ static int starfive_aes_aead_do_one_req(
+       struct starfive_cryp_ctx *ctx =
+               crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct starfive_cryp_dev *cryp = ctx->cryp;
+-      struct starfive_cryp_request_ctx *rctx = ctx->rctx;
++      struct starfive_cryp_request_ctx *rctx;
+       u32 block[AES_BLOCK_32];
+       u32 stat;
+       int err;
+@@ -579,6 +579,8 @@ static int starfive_aes_aead_do_one_req(
+       if (err)
+               return err;
++      rctx = ctx->rctx;
++
+       if (!cryp->assoclen)
+               goto write_text;
diff --git a/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch b/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch
new file mode 100644 (file)
index 0000000..f376edb
--- /dev/null
@@ -0,0 +1,125 @@
+From 922b213ad22f93fb2788ce119084622ab3d25bf8 Mon Sep 17 00:00:00 2001
+From: Herbert Xu <herbert@gondor.apana.org.au>
+Date: Thu, 30 Nov 2023 18:12:55 +0800
+Subject: [PATCH 012/116] crypto: starfive - Remove cfb and ofb
+
+Remove the unused CFB/OFB implementation.
+
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-aes.c  | 71 +--------------------------
+ drivers/crypto/starfive/jh7110-cryp.h |  2 -
+ 2 files changed, 1 insertion(+), 72 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-aes.c
++++ b/drivers/crypto/starfive/jh7110-aes.c
+@@ -262,12 +262,7 @@ static int starfive_aes_hw_init(struct s
+       rctx->csr.aes.mode  = hw_mode;
+       rctx->csr.aes.cmode = !is_encrypt(cryp);
+       rctx->csr.aes.ie = 1;
+-
+-      if (hw_mode == STARFIVE_AES_MODE_CFB ||
+-          hw_mode == STARFIVE_AES_MODE_OFB)
+-              rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_128;
+-      else
+-              rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1;
++      rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1;
+       if (cryp->side_chan) {
+               rctx->csr.aes.delay_aes = 1;
+@@ -294,8 +289,6 @@ static int starfive_aes_hw_init(struct s
+               starfive_aes_ccm_init(ctx);
+               starfive_aes_aead_hw_start(ctx, hw_mode);
+               break;
+-      case STARFIVE_AES_MODE_OFB:
+-      case STARFIVE_AES_MODE_CFB:
+       case STARFIVE_AES_MODE_CBC:
+       case STARFIVE_AES_MODE_CTR:
+               starfive_aes_write_iv(ctx, (void *)cryp->req.sreq->iv);
+@@ -785,26 +778,6 @@ static int starfive_aes_cbc_decrypt(stru
+       return starfive_aes_crypt(req, STARFIVE_AES_MODE_CBC);
+ }
+-static int starfive_aes_cfb_encrypt(struct skcipher_request *req)
+-{
+-      return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB | FLG_ENCRYPT);
+-}
+-
+-static int starfive_aes_cfb_decrypt(struct skcipher_request *req)
+-{
+-      return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB);
+-}
+-
+-static int starfive_aes_ofb_encrypt(struct skcipher_request *req)
+-{
+-      return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB | FLG_ENCRYPT);
+-}
+-
+-static int starfive_aes_ofb_decrypt(struct skcipher_request *req)
+-{
+-      return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB);
+-}
+-
+ static int starfive_aes_ctr_encrypt(struct skcipher_request *req)
+ {
+       return starfive_aes_crypt(req, STARFIVE_AES_MODE_CTR | FLG_ENCRYPT);
+@@ -903,48 +876,6 @@ static struct skcipher_engine_alg skciph
+               .cra_priority           = 200,
+               .cra_flags              = CRYPTO_ALG_ASYNC,
+               .cra_blocksize          = 1,
+-              .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-              .cra_alignmask          = 0xf,
+-              .cra_module             = THIS_MODULE,
+-      },
+-      .op = {
+-              .do_one_request = starfive_aes_do_one_req,
+-      },
+-}, {
+-      .base.init                      = starfive_aes_init_tfm,
+-      .base.setkey                    = starfive_aes_setkey,
+-      .base.encrypt                   = starfive_aes_cfb_encrypt,
+-      .base.decrypt                   = starfive_aes_cfb_decrypt,
+-      .base.min_keysize               = AES_MIN_KEY_SIZE,
+-      .base.max_keysize               = AES_MAX_KEY_SIZE,
+-      .base.ivsize                    = AES_BLOCK_SIZE,
+-      .base.base = {
+-              .cra_name               = "cfb(aes)",
+-              .cra_driver_name        = "starfive-cfb-aes",
+-              .cra_priority           = 200,
+-              .cra_flags              = CRYPTO_ALG_ASYNC,
+-              .cra_blocksize          = 1,
+-              .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+-              .cra_alignmask          = 0xf,
+-              .cra_module             = THIS_MODULE,
+-      },
+-      .op = {
+-              .do_one_request = starfive_aes_do_one_req,
+-      },
+-}, {
+-      .base.init                      = starfive_aes_init_tfm,
+-      .base.setkey                    = starfive_aes_setkey,
+-      .base.encrypt                   = starfive_aes_ofb_encrypt,
+-      .base.decrypt                   = starfive_aes_ofb_decrypt,
+-      .base.min_keysize               = AES_MIN_KEY_SIZE,
+-      .base.max_keysize               = AES_MAX_KEY_SIZE,
+-      .base.ivsize                    = AES_BLOCK_SIZE,
+-      .base.base = {
+-              .cra_name               = "ofb(aes)",
+-              .cra_driver_name        = "starfive-ofb-aes",
+-              .cra_priority           = 200,
+-              .cra_flags              = CRYPTO_ALG_ASYNC,
+-              .cra_blocksize          = 1,
+               .cra_ctxsize            = sizeof(struct starfive_cryp_ctx),
+               .cra_alignmask          = 0xf,
+               .cra_module             = THIS_MODULE,
+--- a/drivers/crypto/starfive/jh7110-cryp.h
++++ b/drivers/crypto/starfive/jh7110-cryp.h
+@@ -50,8 +50,6 @@ union starfive_aes_csr {
+               u32 ccm_start                   :1;
+ #define STARFIVE_AES_MODE_ECB                 0x0
+ #define STARFIVE_AES_MODE_CBC                 0x1
+-#define STARFIVE_AES_MODE_CFB                 0x2
+-#define STARFIVE_AES_MODE_OFB                 0x3
+ #define STARFIVE_AES_MODE_CTR                 0x4
+ #define STARFIVE_AES_MODE_CCM                 0x5
+ #define STARFIVE_AES_MODE_GCM                 0x6
diff --git a/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch b/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch
new file mode 100644 (file)
index 0000000..2044b72
--- /dev/null
@@ -0,0 +1,33 @@
+From 0dbdc763f10c5cfa968dffc290a7c060ee740172 Mon Sep 17 00:00:00 2001
+From: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Date: Mon, 4 Dec 2023 11:02:39 +0800
+Subject: [PATCH 013/116] crypto: starfive - Remove unneeded NULL checks
+
+NULL check before kfree_sensitive function is not needed.
+
+Signed-off-by: Jia Jie Ho <jiajie.ho@starfivetech.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202311301702.LxswfETY-lkp@intel.com/
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ drivers/crypto/starfive/jh7110-rsa.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-rsa.c
++++ b/drivers/crypto/starfive/jh7110-rsa.c
+@@ -45,12 +45,9 @@ static inline int starfive_pka_wait_done
+ static void starfive_rsa_free_key(struct starfive_rsa_key *key)
+ {
+-      if (key->d)
+-              kfree_sensitive(key->d);
+-      if (key->e)
+-              kfree_sensitive(key->e);
+-      if (key->n)
+-              kfree_sensitive(key->n);
++      kfree_sensitive(key->d);
++      kfree_sensitive(key->e);
++      kfree_sensitive(key->n);
+       memset(key, 0, sizeof(*key));
+ }
diff --git a/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch b/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch
new file mode 100644 (file)
index 0000000..4305715
--- /dev/null
@@ -0,0 +1,183 @@
+From 708695ebf1a779de9a1fd2f72f7938afa6c14ada Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:51 +0800
+Subject: [PATCH 014/116] dt-bindings: PCI: Add PLDA XpressRICH PCIe host
+ common properties
+
+Add PLDA XpressRICH PCIe host common properties dt-binding doc.
+PolarFire PCIe host using PLDA IP. Move common properties from Microchip
+PolarFire PCIe host to PLDA files.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Tested-by: John Clark <inindev@gmail.com>
+---
+ .../bindings/pci/microchip,pcie-host.yaml     | 55 +-------------
+ .../pci/plda,xpressrich3-axi-common.yaml      | 75 +++++++++++++++++++
+ 2 files changed, 76 insertions(+), 54 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
+
+--- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
++++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
+@@ -10,21 +10,13 @@ maintainers:
+   - Daire McNamara <daire.mcnamara@microchip.com>
+ allOf:
+-  - $ref: /schemas/pci/pci-bus.yaml#
++  - $ref: plda,xpressrich3-axi-common.yaml#
+   - $ref: /schemas/interrupt-controller/msi-controller.yaml#
+ properties:
+   compatible:
+     const: microchip,pcie-host-1.0 # PolarFire
+-  reg:
+-    maxItems: 2
+-
+-  reg-names:
+-    items:
+-      - const: cfg
+-      - const: apb
+-
+   clocks:
+     description:
+       Fabric Interface Controllers, FICs, are the interface between the FPGA
+@@ -52,18 +44,6 @@ properties:
+     items:
+       pattern: '^fic[0-3]$'
+-  interrupts:
+-    minItems: 1
+-    items:
+-      - description: PCIe host controller
+-      - description: builtin MSI controller
+-
+-  interrupt-names:
+-    minItems: 1
+-    items:
+-      - const: pcie
+-      - const: msi
+-
+   ranges:
+     maxItems: 1
+@@ -71,39 +51,6 @@ properties:
+     minItems: 1
+     maxItems: 6
+-  msi-controller:
+-    description: Identifies the node as an MSI controller.
+-
+-  msi-parent:
+-    description: MSI controller the device is capable of using.
+-
+-  interrupt-controller:
+-    type: object
+-    properties:
+-      '#address-cells':
+-        const: 0
+-
+-      '#interrupt-cells':
+-        const: 1
+-
+-      interrupt-controller: true
+-
+-    required:
+-      - '#address-cells'
+-      - '#interrupt-cells'
+-      - interrupt-controller
+-
+-    additionalProperties: false
+-
+-required:
+-  - reg
+-  - reg-names
+-  - "#interrupt-cells"
+-  - interrupts
+-  - interrupt-map-mask
+-  - interrupt-map
+-  - msi-controller
+-
+ unevaluatedProperties: false
+ examples:
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
+@@ -0,0 +1,75 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pci/plda,xpressrich3-axi-common.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: PLDA XpressRICH PCIe host common properties
++
++maintainers:
++  - Daire McNamara <daire.mcnamara@microchip.com>
++  - Kevin Xie <kevin.xie@starfivetech.com>
++
++description:
++  Generic PLDA XpressRICH PCIe host common properties.
++
++allOf:
++  - $ref: /schemas/pci/pci-bus.yaml#
++
++properties:
++  reg:
++    maxItems: 2
++
++  reg-names:
++    items:
++      - const: cfg
++      - const: apb
++
++  interrupts:
++    minItems: 1
++    items:
++      - description: PCIe host controller
++      - description: builtin MSI controller
++
++  interrupt-names:
++    minItems: 1
++    items:
++      - const: pcie
++      - const: msi
++
++  msi-controller:
++    description: Identifies the node as an MSI controller.
++
++  msi-parent:
++    description: MSI controller the device is capable of using.
++
++  interrupt-controller:
++    type: object
++    properties:
++      '#address-cells':
++        const: 0
++
++      '#interrupt-cells':
++        const: 1
++
++      interrupt-controller: true
++
++    required:
++      - '#address-cells'
++      - '#interrupt-cells'
++      - interrupt-controller
++
++    additionalProperties: false
++
++required:
++  - reg
++  - reg-names
++  - interrupts
++  - msi-controller
++  - "#interrupt-cells"
++  - interrupt-map-mask
++  - interrupt-map
++
++additionalProperties: true
++
++...
diff --git a/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch b/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch
new file mode 100644 (file)
index 0000000..9e7717f
--- /dev/null
@@ -0,0 +1,2523 @@
+From df67154aa92efdc774a8536ece6f431e7850aca2 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:52 +0800
+Subject: [PATCH 015/116] PCI: microchip: Move pcie-microchip-host.c to plda
+ directory
+
+For Microchip Polarfire PCIe host is PLDA XpressRich IP, move to plda
+directory. Prepare for refactoring the codes.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/Kconfig                     |  9 +--------
+ drivers/pci/controller/Makefile                    |  2 +-
+ drivers/pci/controller/plda/Kconfig                | 14 ++++++++++++++
+ drivers/pci/controller/plda/Makefile               |  2 ++
+ .../controller/{ => plda}/pcie-microchip-host.c    |  2 +-
+ 5 files changed, 19 insertions(+), 10 deletions(-)
+ create mode 100644 drivers/pci/controller/plda/Kconfig
+ create mode 100644 drivers/pci/controller/plda/Makefile
+ rename drivers/pci/controller/{ => plda}/pcie-microchip-host.c (99%)
+
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -215,14 +215,6 @@ config PCIE_MT7621
+       help
+         This selects a driver for the MediaTek MT7621 PCIe Controller.
+-config PCIE_MICROCHIP_HOST
+-      tristate "Microchip AXI PCIe controller"
+-      depends on PCI_MSI && OF
+-      select PCI_HOST_COMMON
+-      help
+-        Say Y here if you want kernel to support the Microchip AXI PCIe
+-        Host Bridge driver.
+-
+ config PCI_HYPERV_INTERFACE
+       tristate "Microsoft Hyper-V PCI Interface"
+       depends on ((X86 && X86_64) || ARM64) && HYPERV && PCI_MSI
+@@ -345,4 +337,5 @@ config PCIE_XILINX_CPM
+ source "drivers/pci/controller/cadence/Kconfig"
+ source "drivers/pci/controller/dwc/Kconfig"
+ source "drivers/pci/controller/mobiveil/Kconfig"
++source "drivers/pci/controller/plda/Kconfig"
+ endmenu
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -32,7 +32,6 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-r
+ obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
+ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
+ obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o
+-obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
+ obj-$(CONFIG_VMD) += vmd.o
+ obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
+ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
+@@ -43,6 +42,7 @@ obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621
+ # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
+ obj-y                         += dwc/
+ obj-y                         += mobiveil/
++obj-y                         += plda/
+ # The following drivers are for devices that use the generic ACPI
+--- /dev/null
++++ b/drivers/pci/controller/plda/Kconfig
+@@ -0,0 +1,14 @@
++# SPDX-License-Identifier: GPL-2.0
++
++menu "PLDA-based PCIe controllers"
++      depends on PCI
++
++config PCIE_MICROCHIP_HOST
++      tristate "Microchip AXI PCIe controller"
++      depends on PCI_MSI && OF
++      select PCI_HOST_COMMON
++      help
++        Say Y here if you want kernel to support the Microchip AXI PCIe
++        Host Bridge driver.
++
++endmenu
+--- /dev/null
++++ b/drivers/pci/controller/plda/Makefile
+@@ -0,0 +1,2 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
+--- a/drivers/pci/controller/pcie-microchip-host.c
++++ /dev/null
+@@ -1,1216 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * Microchip AXI PCIe Bridge host controller driver
+- *
+- * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
+- *
+- * Author: Daire McNamara <daire.mcnamara@microchip.com>
+- */
+-
+-#include <linux/bitfield.h>
+-#include <linux/clk.h>
+-#include <linux/irqchip/chained_irq.h>
+-#include <linux/irqdomain.h>
+-#include <linux/module.h>
+-#include <linux/msi.h>
+-#include <linux/of_address.h>
+-#include <linux/of_pci.h>
+-#include <linux/pci-ecam.h>
+-#include <linux/platform_device.h>
+-
+-#include "../pci.h"
+-
+-/* Number of MSI IRQs */
+-#define MC_MAX_NUM_MSI_IRQS                   32
+-
+-/* PCIe Bridge Phy and Controller Phy offsets */
+-#define MC_PCIE1_BRIDGE_ADDR                  0x00008000u
+-#define MC_PCIE1_CTRL_ADDR                    0x0000a000u
+-
+-#define MC_PCIE_BRIDGE_ADDR                   (MC_PCIE1_BRIDGE_ADDR)
+-#define MC_PCIE_CTRL_ADDR                     (MC_PCIE1_CTRL_ADDR)
+-
+-/* PCIe Bridge Phy Regs */
+-#define PCIE_PCI_IRQ_DW0                      0xa8
+-#define  MSIX_CAP_MASK                                BIT(31)
+-#define  NUM_MSI_MSGS_MASK                    GENMASK(6, 4)
+-#define  NUM_MSI_MSGS_SHIFT                   4
+-
+-#define IMASK_LOCAL                           0x180
+-#define  DMA_END_ENGINE_0_MASK                        0x00000000u
+-#define  DMA_END_ENGINE_0_SHIFT                       0
+-#define  DMA_END_ENGINE_1_MASK                        0x00000000u
+-#define  DMA_END_ENGINE_1_SHIFT                       1
+-#define  DMA_ERROR_ENGINE_0_MASK              0x00000100u
+-#define  DMA_ERROR_ENGINE_0_SHIFT             8
+-#define  DMA_ERROR_ENGINE_1_MASK              0x00000200u
+-#define  DMA_ERROR_ENGINE_1_SHIFT             9
+-#define  A_ATR_EVT_POST_ERR_MASK              0x00010000u
+-#define  A_ATR_EVT_POST_ERR_SHIFT             16
+-#define  A_ATR_EVT_FETCH_ERR_MASK             0x00020000u
+-#define  A_ATR_EVT_FETCH_ERR_SHIFT            17
+-#define  A_ATR_EVT_DISCARD_ERR_MASK           0x00040000u
+-#define  A_ATR_EVT_DISCARD_ERR_SHIFT          18
+-#define  A_ATR_EVT_DOORBELL_MASK              0x00000000u
+-#define  A_ATR_EVT_DOORBELL_SHIFT             19
+-#define  P_ATR_EVT_POST_ERR_MASK              0x00100000u
+-#define  P_ATR_EVT_POST_ERR_SHIFT             20
+-#define  P_ATR_EVT_FETCH_ERR_MASK             0x00200000u
+-#define  P_ATR_EVT_FETCH_ERR_SHIFT            21
+-#define  P_ATR_EVT_DISCARD_ERR_MASK           0x00400000u
+-#define  P_ATR_EVT_DISCARD_ERR_SHIFT          22
+-#define  P_ATR_EVT_DOORBELL_MASK              0x00000000u
+-#define  P_ATR_EVT_DOORBELL_SHIFT             23
+-#define  PM_MSI_INT_INTA_MASK                 0x01000000u
+-#define  PM_MSI_INT_INTA_SHIFT                        24
+-#define  PM_MSI_INT_INTB_MASK                 0x02000000u
+-#define  PM_MSI_INT_INTB_SHIFT                        25
+-#define  PM_MSI_INT_INTC_MASK                 0x04000000u
+-#define  PM_MSI_INT_INTC_SHIFT                        26
+-#define  PM_MSI_INT_INTD_MASK                 0x08000000u
+-#define  PM_MSI_INT_INTD_SHIFT                        27
+-#define  PM_MSI_INT_INTX_MASK                 0x0f000000u
+-#define  PM_MSI_INT_INTX_SHIFT                        24
+-#define  PM_MSI_INT_MSI_MASK                  0x10000000u
+-#define  PM_MSI_INT_MSI_SHIFT                 28
+-#define  PM_MSI_INT_AER_EVT_MASK              0x20000000u
+-#define  PM_MSI_INT_AER_EVT_SHIFT             29
+-#define  PM_MSI_INT_EVENTS_MASK                       0x40000000u
+-#define  PM_MSI_INT_EVENTS_SHIFT              30
+-#define  PM_MSI_INT_SYS_ERR_MASK              0x80000000u
+-#define  PM_MSI_INT_SYS_ERR_SHIFT             31
+-#define  NUM_LOCAL_EVENTS                     15
+-#define ISTATUS_LOCAL                         0x184
+-#define IMASK_HOST                            0x188
+-#define ISTATUS_HOST                          0x18c
+-#define IMSI_ADDR                             0x190
+-#define ISTATUS_MSI                           0x194
+-
+-/* PCIe Master table init defines */
+-#define ATR0_PCIE_WIN0_SRCADDR_PARAM          0x600u
+-#define  ATR0_PCIE_ATR_SIZE                   0x25
+-#define  ATR0_PCIE_ATR_SIZE_SHIFT             1
+-#define ATR0_PCIE_WIN0_SRC_ADDR                       0x604u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB          0x608u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW          0x60cu
+-#define ATR0_PCIE_WIN0_TRSL_PARAM             0x610u
+-
+-/* PCIe AXI slave table init defines */
+-#define ATR0_AXI4_SLV0_SRCADDR_PARAM          0x800u
+-#define  ATR_SIZE_SHIFT                               1
+-#define  ATR_IMPL_ENABLE                      1
+-#define ATR0_AXI4_SLV0_SRC_ADDR                       0x804u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB          0x808u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW          0x80cu
+-#define ATR0_AXI4_SLV0_TRSL_PARAM             0x810u
+-#define  PCIE_TX_RX_INTERFACE                 0x00000000u
+-#define  PCIE_CONFIG_INTERFACE                        0x00000001u
+-
+-#define ATR_ENTRY_SIZE                                32
+-
+-/* PCIe Controller Phy Regs */
+-#define SEC_ERROR_EVENT_CNT                   0x20
+-#define DED_ERROR_EVENT_CNT                   0x24
+-#define SEC_ERROR_INT                         0x28
+-#define  SEC_ERROR_INT_TX_RAM_SEC_ERR_INT     GENMASK(3, 0)
+-#define  SEC_ERROR_INT_RX_RAM_SEC_ERR_INT     GENMASK(7, 4)
+-#define  SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT       GENMASK(11, 8)
+-#define  SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT       GENMASK(15, 12)
+-#define  SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT    GENMASK(15, 0)
+-#define  NUM_SEC_ERROR_INTS                   (4)
+-#define SEC_ERROR_INT_MASK                    0x2c
+-#define DED_ERROR_INT                         0x30
+-#define  DED_ERROR_INT_TX_RAM_DED_ERR_INT     GENMASK(3, 0)
+-#define  DED_ERROR_INT_RX_RAM_DED_ERR_INT     GENMASK(7, 4)
+-#define  DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT       GENMASK(11, 8)
+-#define  DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT       GENMASK(15, 12)
+-#define  DED_ERROR_INT_ALL_RAM_DED_ERR_INT    GENMASK(15, 0)
+-#define  NUM_DED_ERROR_INTS                   (4)
+-#define DED_ERROR_INT_MASK                    0x34
+-#define ECC_CONTROL                           0x38
+-#define  ECC_CONTROL_TX_RAM_INJ_ERROR_0               BIT(0)
+-#define  ECC_CONTROL_TX_RAM_INJ_ERROR_1               BIT(1)
+-#define  ECC_CONTROL_TX_RAM_INJ_ERROR_2               BIT(2)
+-#define  ECC_CONTROL_TX_RAM_INJ_ERROR_3               BIT(3)
+-#define  ECC_CONTROL_RX_RAM_INJ_ERROR_0               BIT(4)
+-#define  ECC_CONTROL_RX_RAM_INJ_ERROR_1               BIT(5)
+-#define  ECC_CONTROL_RX_RAM_INJ_ERROR_2               BIT(6)
+-#define  ECC_CONTROL_RX_RAM_INJ_ERROR_3               BIT(7)
+-#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8)
+-#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9)
+-#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10)
+-#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11)
+-#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12)
+-#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13)
+-#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14)
+-#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15)
+-#define  ECC_CONTROL_TX_RAM_ECC_BYPASS                BIT(24)
+-#define  ECC_CONTROL_RX_RAM_ECC_BYPASS                BIT(25)
+-#define  ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS  BIT(26)
+-#define  ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS  BIT(27)
+-#define PCIE_EVENT_INT                                0x14c
+-#define  PCIE_EVENT_INT_L2_EXIT_INT           BIT(0)
+-#define  PCIE_EVENT_INT_HOTRST_EXIT_INT               BIT(1)
+-#define  PCIE_EVENT_INT_DLUP_EXIT_INT         BIT(2)
+-#define  PCIE_EVENT_INT_MASK                  GENMASK(2, 0)
+-#define  PCIE_EVENT_INT_L2_EXIT_INT_MASK      BIT(16)
+-#define  PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK  BIT(17)
+-#define  PCIE_EVENT_INT_DLUP_EXIT_INT_MASK    BIT(18)
+-#define  PCIE_EVENT_INT_ENB_MASK              GENMASK(18, 16)
+-#define  PCIE_EVENT_INT_ENB_SHIFT             16
+-#define  NUM_PCIE_EVENTS                      (3)
+-
+-/* PCIe Config space MSI capability structure */
+-#define MC_MSI_CAP_CTRL_OFFSET                        0xe0u
+-
+-/* Events */
+-#define EVENT_PCIE_L2_EXIT                    0
+-#define EVENT_PCIE_HOTRST_EXIT                        1
+-#define EVENT_PCIE_DLUP_EXIT                  2
+-#define EVENT_SEC_TX_RAM_SEC_ERR              3
+-#define EVENT_SEC_RX_RAM_SEC_ERR              4
+-#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR                5
+-#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR                6
+-#define EVENT_DED_TX_RAM_DED_ERR              7
+-#define EVENT_DED_RX_RAM_DED_ERR              8
+-#define EVENT_DED_PCIE2AXI_RAM_DED_ERR                9
+-#define EVENT_DED_AXI2PCIE_RAM_DED_ERR                10
+-#define EVENT_LOCAL_DMA_END_ENGINE_0          11
+-#define EVENT_LOCAL_DMA_END_ENGINE_1          12
+-#define EVENT_LOCAL_DMA_ERROR_ENGINE_0                13
+-#define EVENT_LOCAL_DMA_ERROR_ENGINE_1                14
+-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR                15
+-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR               16
+-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR     17
+-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL                18
+-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR                19
+-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR               20
+-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR     21
+-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL                22
+-#define EVENT_LOCAL_PM_MSI_INT_INTX           23
+-#define EVENT_LOCAL_PM_MSI_INT_MSI            24
+-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT                25
+-#define EVENT_LOCAL_PM_MSI_INT_EVENTS         26
+-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR                27
+-#define NUM_EVENTS                            28
+-
+-#define PCIE_EVENT_CAUSE(x, s)        \
+-      [EVENT_PCIE_ ## x] = { __stringify(x), s }
+-
+-#define SEC_ERROR_CAUSE(x, s) \
+-      [EVENT_SEC_ ## x] = { __stringify(x), s }
+-
+-#define DED_ERROR_CAUSE(x, s) \
+-      [EVENT_DED_ ## x] = { __stringify(x), s }
+-
+-#define LOCAL_EVENT_CAUSE(x, s) \
+-      [EVENT_LOCAL_ ## x] = { __stringify(x), s }
+-
+-#define PCIE_EVENT(x) \
+-      .base = MC_PCIE_CTRL_ADDR, \
+-      .offset = PCIE_EVENT_INT, \
+-      .mask_offset = PCIE_EVENT_INT, \
+-      .mask_high = 1, \
+-      .mask = PCIE_EVENT_INT_ ## x ## _INT, \
+-      .enb_mask = PCIE_EVENT_INT_ENB_MASK
+-
+-#define SEC_EVENT(x) \
+-      .base = MC_PCIE_CTRL_ADDR, \
+-      .offset = SEC_ERROR_INT, \
+-      .mask_offset = SEC_ERROR_INT_MASK, \
+-      .mask = SEC_ERROR_INT_ ## x ## _INT, \
+-      .mask_high = 1, \
+-      .enb_mask = 0
+-
+-#define DED_EVENT(x) \
+-      .base = MC_PCIE_CTRL_ADDR, \
+-      .offset = DED_ERROR_INT, \
+-      .mask_offset = DED_ERROR_INT_MASK, \
+-      .mask_high = 1, \
+-      .mask = DED_ERROR_INT_ ## x ## _INT, \
+-      .enb_mask = 0
+-
+-#define LOCAL_EVENT(x) \
+-      .base = MC_PCIE_BRIDGE_ADDR, \
+-      .offset = ISTATUS_LOCAL, \
+-      .mask_offset = IMASK_LOCAL, \
+-      .mask_high = 0, \
+-      .mask = x ## _MASK, \
+-      .enb_mask = 0
+-
+-#define PCIE_EVENT_TO_EVENT_MAP(x) \
+-      { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x }
+-
+-#define SEC_ERROR_TO_EVENT_MAP(x) \
+-      { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x }
+-
+-#define DED_ERROR_TO_EVENT_MAP(x) \
+-      { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x }
+-
+-#define LOCAL_STATUS_TO_EVENT_MAP(x) \
+-      { x ## _MASK, EVENT_LOCAL_ ## x }
+-
+-struct event_map {
+-      u32 reg_mask;
+-      u32 event_bit;
+-};
+-
+-struct mc_msi {
+-      struct mutex lock;              /* Protect used bitmap */
+-      struct irq_domain *msi_domain;
+-      struct irq_domain *dev_domain;
+-      u32 num_vectors;
+-      u64 vector_phy;
+-      DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
+-};
+-
+-struct mc_pcie {
+-      void __iomem *axi_base_addr;
+-      struct device *dev;
+-      struct irq_domain *intx_domain;
+-      struct irq_domain *event_domain;
+-      raw_spinlock_t lock;
+-      struct mc_msi msi;
+-};
+-
+-struct cause {
+-      const char *sym;
+-      const char *str;
+-};
+-
+-static const struct cause event_cause[NUM_EVENTS] = {
+-      PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"),
+-      PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"),
+-      PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"),
+-      SEC_ERROR_CAUSE(TX_RAM_SEC_ERR,  "sec error in tx buffer"),
+-      SEC_ERROR_CAUSE(RX_RAM_SEC_ERR,  "sec error in rx buffer"),
+-      SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR,  "sec error in pcie2axi buffer"),
+-      SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR,  "sec error in axi2pcie buffer"),
+-      DED_ERROR_CAUSE(TX_RAM_DED_ERR,  "ded error in tx buffer"),
+-      DED_ERROR_CAUSE(RX_RAM_DED_ERR,  "ded error in rx buffer"),
+-      DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR,  "ded error in pcie2axi buffer"),
+-      DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR,  "ded error in axi2pcie buffer"),
+-      LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"),
+-      LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"),
+-      LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"),
+-      LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"),
+-      LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"),
+-      LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"),
+-      LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"),
+-      LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"),
+-      LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"),
+-      LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"),
+-      LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),
+-};
+-
+-static struct event_map pcie_event_to_event[] = {
+-      PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),
+-      PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),
+-      PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT),
+-};
+-
+-static struct event_map sec_error_to_event[] = {
+-      SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),
+-      SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),
+-      SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),
+-      SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR),
+-};
+-
+-static struct event_map ded_error_to_event[] = {
+-      DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),
+-      DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),
+-      DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),
+-      DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR),
+-};
+-
+-static struct event_map local_status_to_event[] = {
+-      LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),
+-      LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),
+-      LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0),
+-      LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1),
+-      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR),
+-      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR),
+-      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR),
+-      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL),
+-      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR),
+-      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR),
+-      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR),
+-      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL),
+-      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX),
+-      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI),
+-      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT),
+-      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS),
+-      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
+-};
+-
+-static struct {
+-      u32 base;
+-      u32 offset;
+-      u32 mask;
+-      u32 shift;
+-      u32 enb_mask;
+-      u32 mask_high;
+-      u32 mask_offset;
+-} event_descs[] = {
+-      { PCIE_EVENT(L2_EXIT) },
+-      { PCIE_EVENT(HOTRST_EXIT) },
+-      { PCIE_EVENT(DLUP_EXIT) },
+-      { SEC_EVENT(TX_RAM_SEC_ERR) },
+-      { SEC_EVENT(RX_RAM_SEC_ERR) },
+-      { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) },
+-      { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) },
+-      { DED_EVENT(TX_RAM_DED_ERR) },
+-      { DED_EVENT(RX_RAM_DED_ERR) },
+-      { DED_EVENT(PCIE2AXI_RAM_DED_ERR) },
+-      { DED_EVENT(AXI2PCIE_RAM_DED_ERR) },
+-      { LOCAL_EVENT(DMA_END_ENGINE_0) },
+-      { LOCAL_EVENT(DMA_END_ENGINE_1) },
+-      { LOCAL_EVENT(DMA_ERROR_ENGINE_0) },
+-      { LOCAL_EVENT(DMA_ERROR_ENGINE_1) },
+-      { LOCAL_EVENT(A_ATR_EVT_POST_ERR) },
+-      { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) },
+-      { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) },
+-      { LOCAL_EVENT(A_ATR_EVT_DOORBELL) },
+-      { LOCAL_EVENT(P_ATR_EVT_POST_ERR) },
+-      { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) },
+-      { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) },
+-      { LOCAL_EVENT(P_ATR_EVT_DOORBELL) },
+-      { LOCAL_EVENT(PM_MSI_INT_INTX) },
+-      { LOCAL_EVENT(PM_MSI_INT_MSI) },
+-      { LOCAL_EVENT(PM_MSI_INT_AER_EVT) },
+-      { LOCAL_EVENT(PM_MSI_INT_EVENTS) },
+-      { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) },
+-};
+-
+-static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" };
+-
+-static struct mc_pcie *port;
+-
+-static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam)
+-{
+-      struct mc_msi *msi = &port->msi;
+-      u16 reg;
+-      u8 queue_size;
+-
+-      /* Fixup MSI enable flag */
+-      reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
+-      reg |= PCI_MSI_FLAGS_ENABLE;
+-      writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
+-
+-      /* Fixup PCI MSI queue flags */
+-      queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg);
+-      reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size);
+-      writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
+-
+-      /* Fixup MSI addr fields */
+-      writel_relaxed(lower_32_bits(msi->vector_phy),
+-                     ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO);
+-      writel_relaxed(upper_32_bits(msi->vector_phy),
+-                     ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
+-}
+-
+-static void mc_handle_msi(struct irq_desc *desc)
+-{
+-      struct mc_pcie *port = irq_desc_get_handler_data(desc);
+-      struct irq_chip *chip = irq_desc_get_chip(desc);
+-      struct device *dev = port->dev;
+-      struct mc_msi *msi = &port->msi;
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      unsigned long status;
+-      u32 bit;
+-      int ret;
+-
+-      chained_irq_enter(chip, desc);
+-
+-      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+-      if (status & PM_MSI_INT_MSI_MASK) {
+-              writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
+-              status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
+-              for_each_set_bit(bit, &status, msi->num_vectors) {
+-                      ret = generic_handle_domain_irq(msi->dev_domain, bit);
+-                      if (ret)
+-                              dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
+-                                                  bit);
+-              }
+-      }
+-
+-      chained_irq_exit(chip, desc);
+-}
+-
+-static void mc_msi_bottom_irq_ack(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      u32 bitpos = data->hwirq;
+-
+-      writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+-}
+-
+-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      phys_addr_t addr = port->msi.vector_phy;
+-
+-      msg->address_lo = lower_32_bits(addr);
+-      msg->address_hi = upper_32_bits(addr);
+-      msg->data = data->hwirq;
+-
+-      dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
+-              (int)data->hwirq, msg->address_hi, msg->address_lo);
+-}
+-
+-static int mc_msi_set_affinity(struct irq_data *irq_data,
+-                             const struct cpumask *mask, bool force)
+-{
+-      return -EINVAL;
+-}
+-
+-static struct irq_chip mc_msi_bottom_irq_chip = {
+-      .name = "Microchip MSI",
+-      .irq_ack = mc_msi_bottom_irq_ack,
+-      .irq_compose_msi_msg = mc_compose_msi_msg,
+-      .irq_set_affinity = mc_msi_set_affinity,
+-};
+-
+-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+-                                 unsigned int nr_irqs, void *args)
+-{
+-      struct mc_pcie *port = domain->host_data;
+-      struct mc_msi *msi = &port->msi;
+-      unsigned long bit;
+-
+-      mutex_lock(&msi->lock);
+-      bit = find_first_zero_bit(msi->used, msi->num_vectors);
+-      if (bit >= msi->num_vectors) {
+-              mutex_unlock(&msi->lock);
+-              return -ENOSPC;
+-      }
+-
+-      set_bit(bit, msi->used);
+-
+-      irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip,
+-                          domain->host_data, handle_edge_irq, NULL, NULL);
+-
+-      mutex_unlock(&msi->lock);
+-
+-      return 0;
+-}
+-
+-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+-                                 unsigned int nr_irqs)
+-{
+-      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(d);
+-      struct mc_msi *msi = &port->msi;
+-
+-      mutex_lock(&msi->lock);
+-
+-      if (test_bit(d->hwirq, msi->used))
+-              __clear_bit(d->hwirq, msi->used);
+-      else
+-              dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
+-
+-      mutex_unlock(&msi->lock);
+-}
+-
+-static const struct irq_domain_ops msi_domain_ops = {
+-      .alloc  = mc_irq_msi_domain_alloc,
+-      .free   = mc_irq_msi_domain_free,
+-};
+-
+-static struct irq_chip mc_msi_irq_chip = {
+-      .name = "Microchip PCIe MSI",
+-      .irq_ack = irq_chip_ack_parent,
+-      .irq_mask = pci_msi_mask_irq,
+-      .irq_unmask = pci_msi_unmask_irq,
+-};
+-
+-static struct msi_domain_info mc_msi_domain_info = {
+-      .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+-                MSI_FLAG_PCI_MSIX),
+-      .chip = &mc_msi_irq_chip,
+-};
+-
+-static int mc_allocate_msi_domains(struct mc_pcie *port)
+-{
+-      struct device *dev = port->dev;
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+-      struct mc_msi *msi = &port->msi;
+-
+-      mutex_init(&port->msi.lock);
+-
+-      msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
+-                                              &msi_domain_ops, port);
+-      if (!msi->dev_domain) {
+-              dev_err(dev, "failed to create IRQ domain\n");
+-              return -ENOMEM;
+-      }
+-
+-      msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info,
+-                                                  msi->dev_domain);
+-      if (!msi->msi_domain) {
+-              dev_err(dev, "failed to create MSI domain\n");
+-              irq_domain_remove(msi->dev_domain);
+-              return -ENOMEM;
+-      }
+-
+-      return 0;
+-}
+-
+-static void mc_handle_intx(struct irq_desc *desc)
+-{
+-      struct mc_pcie *port = irq_desc_get_handler_data(desc);
+-      struct irq_chip *chip = irq_desc_get_chip(desc);
+-      struct device *dev = port->dev;
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      unsigned long status;
+-      u32 bit;
+-      int ret;
+-
+-      chained_irq_enter(chip, desc);
+-
+-      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+-      if (status & PM_MSI_INT_INTX_MASK) {
+-              status &= PM_MSI_INT_INTX_MASK;
+-              status >>= PM_MSI_INT_INTX_SHIFT;
+-              for_each_set_bit(bit, &status, PCI_NUM_INTX) {
+-                      ret = generic_handle_domain_irq(port->intx_domain, bit);
+-                      if (ret)
+-                              dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
+-                                                  bit);
+-              }
+-      }
+-
+-      chained_irq_exit(chip, desc);
+-}
+-
+-static void mc_ack_intx_irq(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-
+-      writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+-}
+-
+-static void mc_mask_intx_irq(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      unsigned long flags;
+-      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-      u32 val;
+-
+-      raw_spin_lock_irqsave(&port->lock, flags);
+-      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+-      val &= ~mask;
+-      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+-      raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static void mc_unmask_intx_irq(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      unsigned long flags;
+-      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-      u32 val;
+-
+-      raw_spin_lock_irqsave(&port->lock, flags);
+-      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+-      val |= mask;
+-      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+-      raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static struct irq_chip mc_intx_irq_chip = {
+-      .name = "Microchip PCIe INTx",
+-      .irq_ack = mc_ack_intx_irq,
+-      .irq_mask = mc_mask_intx_irq,
+-      .irq_unmask = mc_unmask_intx_irq,
+-};
+-
+-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+-                          irq_hw_number_t hwirq)
+-{
+-      irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
+-      irq_set_chip_data(irq, domain->host_data);
+-
+-      return 0;
+-}
+-
+-static const struct irq_domain_ops intx_domain_ops = {
+-      .map = mc_pcie_intx_map,
+-};
+-
+-static inline u32 reg_to_event(u32 reg, struct event_map field)
+-{
+-      return (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
+-}
+-
+-static u32 pcie_events(struct mc_pcie *port)
+-{
+-      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-      u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT);
+-      u32 val = 0;
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++)
+-              val |= reg_to_event(reg, pcie_event_to_event[i]);
+-
+-      return val;
+-}
+-
+-static u32 sec_errors(struct mc_pcie *port)
+-{
+-      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-      u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT);
+-      u32 val = 0;
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++)
+-              val |= reg_to_event(reg, sec_error_to_event[i]);
+-
+-      return val;
+-}
+-
+-static u32 ded_errors(struct mc_pcie *port)
+-{
+-      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-      u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT);
+-      u32 val = 0;
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++)
+-              val |= reg_to_event(reg, ded_error_to_event[i]);
+-
+-      return val;
+-}
+-
+-static u32 local_events(struct mc_pcie *port)
+-{
+-      void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+-      u32 val = 0;
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++)
+-              val |= reg_to_event(reg, local_status_to_event[i]);
+-
+-      return val;
+-}
+-
+-static u32 get_events(struct mc_pcie *port)
+-{
+-      u32 events = 0;
+-
+-      events |= pcie_events(port);
+-      events |= sec_errors(port);
+-      events |= ded_errors(port);
+-      events |= local_events(port);
+-
+-      return events;
+-}
+-
+-static irqreturn_t mc_event_handler(int irq, void *dev_id)
+-{
+-      struct mc_pcie *port = dev_id;
+-      struct device *dev = port->dev;
+-      struct irq_data *data;
+-
+-      data = irq_domain_get_irq_data(port->event_domain, irq);
+-
+-      if (event_cause[data->hwirq].str)
+-              dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str);
+-      else
+-              dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq);
+-
+-      return IRQ_HANDLED;
+-}
+-
+-static void mc_handle_event(struct irq_desc *desc)
+-{
+-      struct mc_pcie *port = irq_desc_get_handler_data(desc);
+-      unsigned long events;
+-      u32 bit;
+-      struct irq_chip *chip = irq_desc_get_chip(desc);
+-
+-      chained_irq_enter(chip, desc);
+-
+-      events = get_events(port);
+-
+-      for_each_set_bit(bit, &events, NUM_EVENTS)
+-              generic_handle_domain_irq(port->event_domain, bit);
+-
+-      chained_irq_exit(chip, desc);
+-}
+-
+-static void mc_ack_event_irq(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      u32 event = data->hwirq;
+-      void __iomem *addr;
+-      u32 mask;
+-
+-      addr = port->axi_base_addr + event_descs[event].base +
+-              event_descs[event].offset;
+-      mask = event_descs[event].mask;
+-      mask |= event_descs[event].enb_mask;
+-
+-      writel_relaxed(mask, addr);
+-}
+-
+-static void mc_mask_event_irq(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      u32 event = data->hwirq;
+-      void __iomem *addr;
+-      u32 mask;
+-      u32 val;
+-
+-      addr = port->axi_base_addr + event_descs[event].base +
+-              event_descs[event].mask_offset;
+-      mask = event_descs[event].mask;
+-      if (event_descs[event].enb_mask) {
+-              mask <<= PCIE_EVENT_INT_ENB_SHIFT;
+-              mask &= PCIE_EVENT_INT_ENB_MASK;
+-      }
+-
+-      if (!event_descs[event].mask_high)
+-              mask = ~mask;
+-
+-      raw_spin_lock(&port->lock);
+-      val = readl_relaxed(addr);
+-      if (event_descs[event].mask_high)
+-              val |= mask;
+-      else
+-              val &= mask;
+-
+-      writel_relaxed(val, addr);
+-      raw_spin_unlock(&port->lock);
+-}
+-
+-static void mc_unmask_event_irq(struct irq_data *data)
+-{
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      u32 event = data->hwirq;
+-      void __iomem *addr;
+-      u32 mask;
+-      u32 val;
+-
+-      addr = port->axi_base_addr + event_descs[event].base +
+-              event_descs[event].mask_offset;
+-      mask = event_descs[event].mask;
+-
+-      if (event_descs[event].enb_mask)
+-              mask <<= PCIE_EVENT_INT_ENB_SHIFT;
+-
+-      if (event_descs[event].mask_high)
+-              mask = ~mask;
+-
+-      if (event_descs[event].enb_mask)
+-              mask &= PCIE_EVENT_INT_ENB_MASK;
+-
+-      raw_spin_lock(&port->lock);
+-      val = readl_relaxed(addr);
+-      if (event_descs[event].mask_high)
+-              val &= mask;
+-      else
+-              val |= mask;
+-      writel_relaxed(val, addr);
+-      raw_spin_unlock(&port->lock);
+-}
+-
+-static struct irq_chip mc_event_irq_chip = {
+-      .name = "Microchip PCIe EVENT",
+-      .irq_ack = mc_ack_event_irq,
+-      .irq_mask = mc_mask_event_irq,
+-      .irq_unmask = mc_unmask_event_irq,
+-};
+-
+-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+-                           irq_hw_number_t hwirq)
+-{
+-      irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
+-      irq_set_chip_data(irq, domain->host_data);
+-
+-      return 0;
+-}
+-
+-static const struct irq_domain_ops event_domain_ops = {
+-      .map = mc_pcie_event_map,
+-};
+-
+-static inline void mc_pcie_deinit_clk(void *data)
+-{
+-      struct clk *clk = data;
+-
+-      clk_disable_unprepare(clk);
+-}
+-
+-static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id)
+-{
+-      struct clk *clk;
+-      int ret;
+-
+-      clk = devm_clk_get_optional(dev, id);
+-      if (IS_ERR(clk))
+-              return clk;
+-      if (!clk)
+-              return clk;
+-
+-      ret = clk_prepare_enable(clk);
+-      if (ret)
+-              return ERR_PTR(ret);
+-
+-      devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk);
+-
+-      return clk;
+-}
+-
+-static int mc_pcie_init_clks(struct device *dev)
+-{
+-      int i;
+-      struct clk *fic;
+-
+-      /*
+-       * PCIe may be clocked via Fabric Interface using between 1 and 4
+-       * clocks. Scan DT for clocks and enable them if present
+-       */
+-      for (i = 0; i < ARRAY_SIZE(poss_clks); i++) {
+-              fic = mc_pcie_init_clk(dev, poss_clks[i]);
+-              if (IS_ERR(fic))
+-                      return PTR_ERR(fic);
+-      }
+-
+-      return 0;
+-}
+-
+-static int mc_pcie_init_irq_domains(struct mc_pcie *port)
+-{
+-      struct device *dev = port->dev;
+-      struct device_node *node = dev->of_node;
+-      struct device_node *pcie_intc_node;
+-
+-      /* Setup INTx */
+-      pcie_intc_node = of_get_next_child(node, NULL);
+-      if (!pcie_intc_node) {
+-              dev_err(dev, "failed to find PCIe Intc node\n");
+-              return -EINVAL;
+-      }
+-
+-      port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
+-                                                 &event_domain_ops, port);
+-      if (!port->event_domain) {
+-              dev_err(dev, "failed to get event domain\n");
+-              of_node_put(pcie_intc_node);
+-              return -ENOMEM;
+-      }
+-
+-      irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
+-
+-      port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+-                                                &intx_domain_ops, port);
+-      if (!port->intx_domain) {
+-              dev_err(dev, "failed to get an INTx IRQ domain\n");
+-              of_node_put(pcie_intc_node);
+-              return -ENOMEM;
+-      }
+-
+-      irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
+-
+-      of_node_put(pcie_intc_node);
+-      raw_spin_lock_init(&port->lock);
+-
+-      return mc_allocate_msi_domains(port);
+-}
+-
+-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+-                               phys_addr_t axi_addr, phys_addr_t pci_addr,
+-                               size_t size)
+-{
+-      u32 atr_sz = ilog2(size) - 1;
+-      u32 val;
+-
+-      if (index == 0)
+-              val = PCIE_CONFIG_INTERFACE;
+-      else
+-              val = PCIE_TX_RX_INTERFACE;
+-
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_TRSL_PARAM);
+-
+-      val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
+-                          ATR_IMPL_ENABLE;
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_SRCADDR_PARAM);
+-
+-      val = upper_32_bits(axi_addr);
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_SRC_ADDR);
+-
+-      val = lower_32_bits(pci_addr);
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
+-
+-      val = upper_32_bits(pci_addr);
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
+-
+-      val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+-      val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
+-      writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+-      writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+-}
+-
+-static int mc_pcie_setup_windows(struct platform_device *pdev,
+-                               struct mc_pcie *port)
+-{
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+-      struct resource_entry *entry;
+-      u64 pci_addr;
+-      u32 index = 1;
+-
+-      resource_list_for_each_entry(entry, &bridge->windows) {
+-              if (resource_type(entry->res) == IORESOURCE_MEM) {
+-                      pci_addr = entry->res->start - entry->offset;
+-                      mc_pcie_setup_window(bridge_base_addr, index,
+-                                           entry->res->start, pci_addr,
+-                                           resource_size(entry->res));
+-                      index++;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static inline void mc_clear_secs(struct mc_pcie *port)
+-{
+-      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-
+-      writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
+-                     SEC_ERROR_INT);
+-      writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT);
+-}
+-
+-static inline void mc_clear_deds(struct mc_pcie *port)
+-{
+-      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-
+-      writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
+-                     DED_ERROR_INT);
+-      writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT);
+-}
+-
+-static void mc_disable_interrupts(struct mc_pcie *port)
+-{
+-      void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+-      u32 val;
+-
+-      /* Ensure ECC bypass is enabled */
+-      val = ECC_CONTROL_TX_RAM_ECC_BYPASS |
+-            ECC_CONTROL_RX_RAM_ECC_BYPASS |
+-            ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS |
+-            ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS;
+-      writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
+-
+-      /* Disable SEC errors and clear any outstanding */
+-      writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
+-                     SEC_ERROR_INT_MASK);
+-      mc_clear_secs(port);
+-
+-      /* Disable DED errors and clear any outstanding */
+-      writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
+-                     DED_ERROR_INT_MASK);
+-      mc_clear_deds(port);
+-
+-      /* Disable local interrupts and clear any outstanding */
+-      writel_relaxed(0, bridge_base_addr + IMASK_LOCAL);
+-      writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL);
+-      writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI);
+-
+-      /* Disable PCIe events and clear any outstanding */
+-      val = PCIE_EVENT_INT_L2_EXIT_INT |
+-            PCIE_EVENT_INT_HOTRST_EXIT_INT |
+-            PCIE_EVENT_INT_DLUP_EXIT_INT |
+-            PCIE_EVENT_INT_L2_EXIT_INT_MASK |
+-            PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK |
+-            PCIE_EVENT_INT_DLUP_EXIT_INT_MASK;
+-      writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
+-
+-      /* Disable host interrupts and clear any outstanding */
+-      writel_relaxed(0, bridge_base_addr + IMASK_HOST);
+-      writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+-}
+-
+-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port)
+-{
+-      struct device *dev = &pdev->dev;
+-      int irq;
+-      int i, intx_irq, msi_irq, event_irq;
+-      int ret;
+-
+-      ret = mc_pcie_init_irq_domains(port);
+-      if (ret) {
+-              dev_err(dev, "failed creating IRQ domains\n");
+-              return ret;
+-      }
+-
+-      irq = platform_get_irq(pdev, 0);
+-      if (irq < 0)
+-              return -ENODEV;
+-
+-      for (i = 0; i < NUM_EVENTS; i++) {
+-              event_irq = irq_create_mapping(port->event_domain, i);
+-              if (!event_irq) {
+-                      dev_err(dev, "failed to map hwirq %d\n", i);
+-                      return -ENXIO;
+-              }
+-
+-              ret = devm_request_irq(dev, event_irq, mc_event_handler,
+-                                     0, event_cause[i].sym, port);
+-              if (ret) {
+-                      dev_err(dev, "failed to request IRQ %d\n", event_irq);
+-                      return ret;
+-              }
+-      }
+-
+-      intx_irq = irq_create_mapping(port->event_domain,
+-                                    EVENT_LOCAL_PM_MSI_INT_INTX);
+-      if (!intx_irq) {
+-              dev_err(dev, "failed to map INTx interrupt\n");
+-              return -ENXIO;
+-      }
+-
+-      /* Plug the INTx chained handler */
+-      irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
+-
+-      msi_irq = irq_create_mapping(port->event_domain,
+-                                   EVENT_LOCAL_PM_MSI_INT_MSI);
+-      if (!msi_irq)
+-              return -ENXIO;
+-
+-      /* Plug the MSI chained handler */
+-      irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
+-
+-      /* Plug the main event chained handler */
+-      irq_set_chained_handler_and_data(irq, mc_handle_event, port);
+-
+-      return 0;
+-}
+-
+-static int mc_platform_init(struct pci_config_window *cfg)
+-{
+-      struct device *dev = cfg->parent;
+-      struct platform_device *pdev = to_platform_device(dev);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      int ret;
+-
+-      /* Configure address translation table 0 for PCIe config space */
+-      mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
+-                           cfg->res.start,
+-                           resource_size(&cfg->res));
+-
+-      /* Need some fixups in config space */
+-      mc_pcie_enable_msi(port, cfg->win);
+-
+-      /* Configure non-config space outbound ranges */
+-      ret = mc_pcie_setup_windows(pdev, port);
+-      if (ret)
+-              return ret;
+-
+-      /* Address translation is up; safe to enable interrupts */
+-      ret = mc_init_interrupts(pdev, port);
+-      if (ret)
+-              return ret;
+-
+-      return 0;
+-}
+-
+-static int mc_host_probe(struct platform_device *pdev)
+-{
+-      struct device *dev = &pdev->dev;
+-      void __iomem *bridge_base_addr;
+-      int ret;
+-      u32 val;
+-
+-      port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+-      if (!port)
+-              return -ENOMEM;
+-
+-      port->dev = dev;
+-
+-      port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
+-      if (IS_ERR(port->axi_base_addr))
+-              return PTR_ERR(port->axi_base_addr);
+-
+-      mc_disable_interrupts(port);
+-
+-      bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-
+-      /* Allow enabling MSI by disabling MSI-X */
+-      val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+-      val &= ~MSIX_CAP_MASK;
+-      writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0);
+-
+-      /* Pick num vectors from bitfile programmed onto FPGA fabric */
+-      val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+-      val &= NUM_MSI_MSGS_MASK;
+-      val >>= NUM_MSI_MSGS_SHIFT;
+-
+-      port->msi.num_vectors = 1 << val;
+-
+-      /* Pick vector address from design */
+-      port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
+-
+-      ret = mc_pcie_init_clks(dev);
+-      if (ret) {
+-              dev_err(dev, "failed to get clock resources, error %d\n", ret);
+-              return -ENODEV;
+-      }
+-
+-      return pci_host_common_probe(pdev);
+-}
+-
+-static const struct pci_ecam_ops mc_ecam_ops = {
+-      .init = mc_platform_init,
+-      .pci_ops = {
+-              .map_bus = pci_ecam_map_bus,
+-              .read = pci_generic_config_read,
+-              .write = pci_generic_config_write,
+-      }
+-};
+-
+-static const struct of_device_id mc_pcie_of_match[] = {
+-      {
+-              .compatible = "microchip,pcie-host-1.0",
+-              .data = &mc_ecam_ops,
+-      },
+-      {},
+-};
+-
+-MODULE_DEVICE_TABLE(of, mc_pcie_of_match);
+-
+-static struct platform_driver mc_pcie_driver = {
+-      .probe = mc_host_probe,
+-      .driver = {
+-              .name = "microchip-pcie",
+-              .of_match_table = mc_pcie_of_match,
+-              .suppress_bind_attrs = true,
+-      },
+-};
+-
+-builtin_platform_driver(mc_pcie_driver);
+-MODULE_LICENSE("GPL");
+-MODULE_DESCRIPTION("Microchip PCIe host controller driver");
+-MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -0,0 +1,1216 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Microchip AXI PCIe Bridge host controller driver
++ *
++ * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved.
++ *
++ * Author: Daire McNamara <daire.mcnamara@microchip.com>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/module.h>
++#include <linux/msi.h>
++#include <linux/of_address.h>
++#include <linux/of_pci.h>
++#include <linux/pci-ecam.h>
++#include <linux/platform_device.h>
++
++#include "../../pci.h"
++
++/* Number of MSI IRQs */
++#define MC_MAX_NUM_MSI_IRQS                   32
++
++/* PCIe Bridge Phy and Controller Phy offsets */
++#define MC_PCIE1_BRIDGE_ADDR                  0x00008000u
++#define MC_PCIE1_CTRL_ADDR                    0x0000a000u
++
++#define MC_PCIE_BRIDGE_ADDR                   (MC_PCIE1_BRIDGE_ADDR)
++#define MC_PCIE_CTRL_ADDR                     (MC_PCIE1_CTRL_ADDR)
++
++/* PCIe Bridge Phy Regs */
++#define PCIE_PCI_IRQ_DW0                      0xa8
++#define  MSIX_CAP_MASK                                BIT(31)
++#define  NUM_MSI_MSGS_MASK                    GENMASK(6, 4)
++#define  NUM_MSI_MSGS_SHIFT                   4
++
++#define IMASK_LOCAL                           0x180
++#define  DMA_END_ENGINE_0_MASK                        0x00000000u
++#define  DMA_END_ENGINE_0_SHIFT                       0
++#define  DMA_END_ENGINE_1_MASK                        0x00000000u
++#define  DMA_END_ENGINE_1_SHIFT                       1
++#define  DMA_ERROR_ENGINE_0_MASK              0x00000100u
++#define  DMA_ERROR_ENGINE_0_SHIFT             8
++#define  DMA_ERROR_ENGINE_1_MASK              0x00000200u
++#define  DMA_ERROR_ENGINE_1_SHIFT             9
++#define  A_ATR_EVT_POST_ERR_MASK              0x00010000u
++#define  A_ATR_EVT_POST_ERR_SHIFT             16
++#define  A_ATR_EVT_FETCH_ERR_MASK             0x00020000u
++#define  A_ATR_EVT_FETCH_ERR_SHIFT            17
++#define  A_ATR_EVT_DISCARD_ERR_MASK           0x00040000u
++#define  A_ATR_EVT_DISCARD_ERR_SHIFT          18
++#define  A_ATR_EVT_DOORBELL_MASK              0x00000000u
++#define  A_ATR_EVT_DOORBELL_SHIFT             19
++#define  P_ATR_EVT_POST_ERR_MASK              0x00100000u
++#define  P_ATR_EVT_POST_ERR_SHIFT             20
++#define  P_ATR_EVT_FETCH_ERR_MASK             0x00200000u
++#define  P_ATR_EVT_FETCH_ERR_SHIFT            21
++#define  P_ATR_EVT_DISCARD_ERR_MASK           0x00400000u
++#define  P_ATR_EVT_DISCARD_ERR_SHIFT          22
++#define  P_ATR_EVT_DOORBELL_MASK              0x00000000u
++#define  P_ATR_EVT_DOORBELL_SHIFT             23
++#define  PM_MSI_INT_INTA_MASK                 0x01000000u
++#define  PM_MSI_INT_INTA_SHIFT                        24
++#define  PM_MSI_INT_INTB_MASK                 0x02000000u
++#define  PM_MSI_INT_INTB_SHIFT                        25
++#define  PM_MSI_INT_INTC_MASK                 0x04000000u
++#define  PM_MSI_INT_INTC_SHIFT                        26
++#define  PM_MSI_INT_INTD_MASK                 0x08000000u
++#define  PM_MSI_INT_INTD_SHIFT                        27
++#define  PM_MSI_INT_INTX_MASK                 0x0f000000u
++#define  PM_MSI_INT_INTX_SHIFT                        24
++#define  PM_MSI_INT_MSI_MASK                  0x10000000u
++#define  PM_MSI_INT_MSI_SHIFT                 28
++#define  PM_MSI_INT_AER_EVT_MASK              0x20000000u
++#define  PM_MSI_INT_AER_EVT_SHIFT             29
++#define  PM_MSI_INT_EVENTS_MASK                       0x40000000u
++#define  PM_MSI_INT_EVENTS_SHIFT              30
++#define  PM_MSI_INT_SYS_ERR_MASK              0x80000000u
++#define  PM_MSI_INT_SYS_ERR_SHIFT             31
++#define  NUM_LOCAL_EVENTS                     15
++#define ISTATUS_LOCAL                         0x184
++#define IMASK_HOST                            0x188
++#define ISTATUS_HOST                          0x18c
++#define IMSI_ADDR                             0x190
++#define ISTATUS_MSI                           0x194
++
++/* PCIe Master table init defines */
++#define ATR0_PCIE_WIN0_SRCADDR_PARAM          0x600u
++#define  ATR0_PCIE_ATR_SIZE                   0x25
++#define  ATR0_PCIE_ATR_SIZE_SHIFT             1
++#define ATR0_PCIE_WIN0_SRC_ADDR                       0x604u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB          0x608u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW          0x60cu
++#define ATR0_PCIE_WIN0_TRSL_PARAM             0x610u
++
++/* PCIe AXI slave table init defines */
++#define ATR0_AXI4_SLV0_SRCADDR_PARAM          0x800u
++#define  ATR_SIZE_SHIFT                               1
++#define  ATR_IMPL_ENABLE                      1
++#define ATR0_AXI4_SLV0_SRC_ADDR                       0x804u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB          0x808u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW          0x80cu
++#define ATR0_AXI4_SLV0_TRSL_PARAM             0x810u
++#define  PCIE_TX_RX_INTERFACE                 0x00000000u
++#define  PCIE_CONFIG_INTERFACE                        0x00000001u
++
++#define ATR_ENTRY_SIZE                                32
++
++/* PCIe Controller Phy Regs */
++#define SEC_ERROR_EVENT_CNT                   0x20
++#define DED_ERROR_EVENT_CNT                   0x24
++#define SEC_ERROR_INT                         0x28
++#define  SEC_ERROR_INT_TX_RAM_SEC_ERR_INT     GENMASK(3, 0)
++#define  SEC_ERROR_INT_RX_RAM_SEC_ERR_INT     GENMASK(7, 4)
++#define  SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT       GENMASK(11, 8)
++#define  SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT       GENMASK(15, 12)
++#define  SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT    GENMASK(15, 0)
++#define  NUM_SEC_ERROR_INTS                   (4)
++#define SEC_ERROR_INT_MASK                    0x2c
++#define DED_ERROR_INT                         0x30
++#define  DED_ERROR_INT_TX_RAM_DED_ERR_INT     GENMASK(3, 0)
++#define  DED_ERROR_INT_RX_RAM_DED_ERR_INT     GENMASK(7, 4)
++#define  DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT       GENMASK(11, 8)
++#define  DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT       GENMASK(15, 12)
++#define  DED_ERROR_INT_ALL_RAM_DED_ERR_INT    GENMASK(15, 0)
++#define  NUM_DED_ERROR_INTS                   (4)
++#define DED_ERROR_INT_MASK                    0x34
++#define ECC_CONTROL                           0x38
++#define  ECC_CONTROL_TX_RAM_INJ_ERROR_0               BIT(0)
++#define  ECC_CONTROL_TX_RAM_INJ_ERROR_1               BIT(1)
++#define  ECC_CONTROL_TX_RAM_INJ_ERROR_2               BIT(2)
++#define  ECC_CONTROL_TX_RAM_INJ_ERROR_3               BIT(3)
++#define  ECC_CONTROL_RX_RAM_INJ_ERROR_0               BIT(4)
++#define  ECC_CONTROL_RX_RAM_INJ_ERROR_1               BIT(5)
++#define  ECC_CONTROL_RX_RAM_INJ_ERROR_2               BIT(6)
++#define  ECC_CONTROL_RX_RAM_INJ_ERROR_3               BIT(7)
++#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8)
++#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9)
++#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10)
++#define  ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11)
++#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12)
++#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13)
++#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14)
++#define  ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15)
++#define  ECC_CONTROL_TX_RAM_ECC_BYPASS                BIT(24)
++#define  ECC_CONTROL_RX_RAM_ECC_BYPASS                BIT(25)
++#define  ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS  BIT(26)
++#define  ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS  BIT(27)
++#define PCIE_EVENT_INT                                0x14c
++#define  PCIE_EVENT_INT_L2_EXIT_INT           BIT(0)
++#define  PCIE_EVENT_INT_HOTRST_EXIT_INT               BIT(1)
++#define  PCIE_EVENT_INT_DLUP_EXIT_INT         BIT(2)
++#define  PCIE_EVENT_INT_MASK                  GENMASK(2, 0)
++#define  PCIE_EVENT_INT_L2_EXIT_INT_MASK      BIT(16)
++#define  PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK  BIT(17)
++#define  PCIE_EVENT_INT_DLUP_EXIT_INT_MASK    BIT(18)
++#define  PCIE_EVENT_INT_ENB_MASK              GENMASK(18, 16)
++#define  PCIE_EVENT_INT_ENB_SHIFT             16
++#define  NUM_PCIE_EVENTS                      (3)
++
++/* PCIe Config space MSI capability structure */
++#define MC_MSI_CAP_CTRL_OFFSET                        0xe0u
++
++/* Events */
++#define EVENT_PCIE_L2_EXIT                    0
++#define EVENT_PCIE_HOTRST_EXIT                        1
++#define EVENT_PCIE_DLUP_EXIT                  2
++#define EVENT_SEC_TX_RAM_SEC_ERR              3
++#define EVENT_SEC_RX_RAM_SEC_ERR              4
++#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR                5
++#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR                6
++#define EVENT_DED_TX_RAM_DED_ERR              7
++#define EVENT_DED_RX_RAM_DED_ERR              8
++#define EVENT_DED_PCIE2AXI_RAM_DED_ERR                9
++#define EVENT_DED_AXI2PCIE_RAM_DED_ERR                10
++#define EVENT_LOCAL_DMA_END_ENGINE_0          11
++#define EVENT_LOCAL_DMA_END_ENGINE_1          12
++#define EVENT_LOCAL_DMA_ERROR_ENGINE_0                13
++#define EVENT_LOCAL_DMA_ERROR_ENGINE_1                14
++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR                15
++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR               16
++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR     17
++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL                18
++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR                19
++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR               20
++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR     21
++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL                22
++#define EVENT_LOCAL_PM_MSI_INT_INTX           23
++#define EVENT_LOCAL_PM_MSI_INT_MSI            24
++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT                25
++#define EVENT_LOCAL_PM_MSI_INT_EVENTS         26
++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR                27
++#define NUM_EVENTS                            28
++
++#define PCIE_EVENT_CAUSE(x, s)        \
++      [EVENT_PCIE_ ## x] = { __stringify(x), s }
++
++#define SEC_ERROR_CAUSE(x, s) \
++      [EVENT_SEC_ ## x] = { __stringify(x), s }
++
++#define DED_ERROR_CAUSE(x, s) \
++      [EVENT_DED_ ## x] = { __stringify(x), s }
++
++#define LOCAL_EVENT_CAUSE(x, s) \
++      [EVENT_LOCAL_ ## x] = { __stringify(x), s }
++
++#define PCIE_EVENT(x) \
++      .base = MC_PCIE_CTRL_ADDR, \
++      .offset = PCIE_EVENT_INT, \
++      .mask_offset = PCIE_EVENT_INT, \
++      .mask_high = 1, \
++      .mask = PCIE_EVENT_INT_ ## x ## _INT, \
++      .enb_mask = PCIE_EVENT_INT_ENB_MASK
++
++#define SEC_EVENT(x) \
++      .base = MC_PCIE_CTRL_ADDR, \
++      .offset = SEC_ERROR_INT, \
++      .mask_offset = SEC_ERROR_INT_MASK, \
++      .mask = SEC_ERROR_INT_ ## x ## _INT, \
++      .mask_high = 1, \
++      .enb_mask = 0
++
++#define DED_EVENT(x) \
++      .base = MC_PCIE_CTRL_ADDR, \
++      .offset = DED_ERROR_INT, \
++      .mask_offset = DED_ERROR_INT_MASK, \
++      .mask_high = 1, \
++      .mask = DED_ERROR_INT_ ## x ## _INT, \
++      .enb_mask = 0
++
++#define LOCAL_EVENT(x) \
++      .base = MC_PCIE_BRIDGE_ADDR, \
++      .offset = ISTATUS_LOCAL, \
++      .mask_offset = IMASK_LOCAL, \
++      .mask_high = 0, \
++      .mask = x ## _MASK, \
++      .enb_mask = 0
++
++#define PCIE_EVENT_TO_EVENT_MAP(x) \
++      { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x }
++
++#define SEC_ERROR_TO_EVENT_MAP(x) \
++      { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x }
++
++#define DED_ERROR_TO_EVENT_MAP(x) \
++      { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x }
++
++#define LOCAL_STATUS_TO_EVENT_MAP(x) \
++      { x ## _MASK, EVENT_LOCAL_ ## x }
++
++struct event_map {
++      u32 reg_mask;
++      u32 event_bit;
++};
++
++struct mc_msi {
++      struct mutex lock;              /* Protect used bitmap */
++      struct irq_domain *msi_domain;
++      struct irq_domain *dev_domain;
++      u32 num_vectors;
++      u64 vector_phy;
++      DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
++};
++
++struct mc_pcie {
++      void __iomem *axi_base_addr;
++      struct device *dev;
++      struct irq_domain *intx_domain;
++      struct irq_domain *event_domain;
++      raw_spinlock_t lock;
++      struct mc_msi msi;
++};
++
++struct cause {
++      const char *sym;
++      const char *str;
++};
++
++static const struct cause event_cause[NUM_EVENTS] = {
++      PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"),
++      PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"),
++      PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"),
++      SEC_ERROR_CAUSE(TX_RAM_SEC_ERR,  "sec error in tx buffer"),
++      SEC_ERROR_CAUSE(RX_RAM_SEC_ERR,  "sec error in rx buffer"),
++      SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR,  "sec error in pcie2axi buffer"),
++      SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR,  "sec error in axi2pcie buffer"),
++      DED_ERROR_CAUSE(TX_RAM_DED_ERR,  "ded error in tx buffer"),
++      DED_ERROR_CAUSE(RX_RAM_DED_ERR,  "ded error in rx buffer"),
++      DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR,  "ded error in pcie2axi buffer"),
++      DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR,  "ded error in axi2pcie buffer"),
++      LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"),
++      LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"),
++      LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"),
++      LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"),
++      LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"),
++      LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"),
++      LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"),
++      LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"),
++      LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"),
++      LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"),
++      LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),
++};
++
++static struct event_map pcie_event_to_event[] = {
++      PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),
++      PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),
++      PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT),
++};
++
++static struct event_map sec_error_to_event[] = {
++      SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),
++      SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),
++      SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),
++      SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR),
++};
++
++static struct event_map ded_error_to_event[] = {
++      DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),
++      DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),
++      DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),
++      DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR),
++};
++
++static struct event_map local_status_to_event[] = {
++      LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),
++      LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),
++      LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0),
++      LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1),
++      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR),
++      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR),
++      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR),
++      LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL),
++      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR),
++      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR),
++      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR),
++      LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL),
++      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX),
++      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI),
++      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT),
++      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS),
++      LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR),
++};
++
++static struct {
++      u32 base;
++      u32 offset;
++      u32 mask;
++      u32 shift;
++      u32 enb_mask;
++      u32 mask_high;
++      u32 mask_offset;
++} event_descs[] = {
++      { PCIE_EVENT(L2_EXIT) },
++      { PCIE_EVENT(HOTRST_EXIT) },
++      { PCIE_EVENT(DLUP_EXIT) },
++      { SEC_EVENT(TX_RAM_SEC_ERR) },
++      { SEC_EVENT(RX_RAM_SEC_ERR) },
++      { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) },
++      { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) },
++      { DED_EVENT(TX_RAM_DED_ERR) },
++      { DED_EVENT(RX_RAM_DED_ERR) },
++      { DED_EVENT(PCIE2AXI_RAM_DED_ERR) },
++      { DED_EVENT(AXI2PCIE_RAM_DED_ERR) },
++      { LOCAL_EVENT(DMA_END_ENGINE_0) },
++      { LOCAL_EVENT(DMA_END_ENGINE_1) },
++      { LOCAL_EVENT(DMA_ERROR_ENGINE_0) },
++      { LOCAL_EVENT(DMA_ERROR_ENGINE_1) },
++      { LOCAL_EVENT(A_ATR_EVT_POST_ERR) },
++      { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) },
++      { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) },
++      { LOCAL_EVENT(A_ATR_EVT_DOORBELL) },
++      { LOCAL_EVENT(P_ATR_EVT_POST_ERR) },
++      { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) },
++      { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) },
++      { LOCAL_EVENT(P_ATR_EVT_DOORBELL) },
++      { LOCAL_EVENT(PM_MSI_INT_INTX) },
++      { LOCAL_EVENT(PM_MSI_INT_MSI) },
++      { LOCAL_EVENT(PM_MSI_INT_AER_EVT) },
++      { LOCAL_EVENT(PM_MSI_INT_EVENTS) },
++      { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) },
++};
++
++static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" };
++
++static struct mc_pcie *port;
++
++static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam)
++{
++      struct mc_msi *msi = &port->msi;
++      u16 reg;
++      u8 queue_size;
++
++      /* Fixup MSI enable flag */
++      reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
++      reg |= PCI_MSI_FLAGS_ENABLE;
++      writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
++
++      /* Fixup PCI MSI queue flags */
++      queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg);
++      reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size);
++      writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS);
++
++      /* Fixup MSI addr fields */
++      writel_relaxed(lower_32_bits(msi->vector_phy),
++                     ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO);
++      writel_relaxed(upper_32_bits(msi->vector_phy),
++                     ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
++}
++
++static void mc_handle_msi(struct irq_desc *desc)
++{
++      struct mc_pcie *port = irq_desc_get_handler_data(desc);
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++      struct device *dev = port->dev;
++      struct mc_msi *msi = &port->msi;
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      unsigned long status;
++      u32 bit;
++      int ret;
++
++      chained_irq_enter(chip, desc);
++
++      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++      if (status & PM_MSI_INT_MSI_MASK) {
++              writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
++              status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
++              for_each_set_bit(bit, &status, msi->num_vectors) {
++                      ret = generic_handle_domain_irq(msi->dev_domain, bit);
++                      if (ret)
++                              dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
++                                                  bit);
++              }
++      }
++
++      chained_irq_exit(chip, desc);
++}
++
++static void mc_msi_bottom_irq_ack(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      u32 bitpos = data->hwirq;
++
++      writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
++}
++
++static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      phys_addr_t addr = port->msi.vector_phy;
++
++      msg->address_lo = lower_32_bits(addr);
++      msg->address_hi = upper_32_bits(addr);
++      msg->data = data->hwirq;
++
++      dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
++              (int)data->hwirq, msg->address_hi, msg->address_lo);
++}
++
++static int mc_msi_set_affinity(struct irq_data *irq_data,
++                             const struct cpumask *mask, bool force)
++{
++      return -EINVAL;
++}
++
++static struct irq_chip mc_msi_bottom_irq_chip = {
++      .name = "Microchip MSI",
++      .irq_ack = mc_msi_bottom_irq_ack,
++      .irq_compose_msi_msg = mc_compose_msi_msg,
++      .irq_set_affinity = mc_msi_set_affinity,
++};
++
++static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
++                                 unsigned int nr_irqs, void *args)
++{
++      struct mc_pcie *port = domain->host_data;
++      struct mc_msi *msi = &port->msi;
++      unsigned long bit;
++
++      mutex_lock(&msi->lock);
++      bit = find_first_zero_bit(msi->used, msi->num_vectors);
++      if (bit >= msi->num_vectors) {
++              mutex_unlock(&msi->lock);
++              return -ENOSPC;
++      }
++
++      set_bit(bit, msi->used);
++
++      irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip,
++                          domain->host_data, handle_edge_irq, NULL, NULL);
++
++      mutex_unlock(&msi->lock);
++
++      return 0;
++}
++
++static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq,
++                                 unsigned int nr_irqs)
++{
++      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++      struct mc_pcie *port = irq_data_get_irq_chip_data(d);
++      struct mc_msi *msi = &port->msi;
++
++      mutex_lock(&msi->lock);
++
++      if (test_bit(d->hwirq, msi->used))
++              __clear_bit(d->hwirq, msi->used);
++      else
++              dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
++
++      mutex_unlock(&msi->lock);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++      .alloc  = mc_irq_msi_domain_alloc,
++      .free   = mc_irq_msi_domain_free,
++};
++
++static struct irq_chip mc_msi_irq_chip = {
++      .name = "Microchip PCIe MSI",
++      .irq_ack = irq_chip_ack_parent,
++      .irq_mask = pci_msi_mask_irq,
++      .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info mc_msi_domain_info = {
++      .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++                MSI_FLAG_PCI_MSIX),
++      .chip = &mc_msi_irq_chip,
++};
++
++static int mc_allocate_msi_domains(struct mc_pcie *port)
++{
++      struct device *dev = port->dev;
++      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++      struct mc_msi *msi = &port->msi;
++
++      mutex_init(&port->msi.lock);
++
++      msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
++                                              &msi_domain_ops, port);
++      if (!msi->dev_domain) {
++              dev_err(dev, "failed to create IRQ domain\n");
++              return -ENOMEM;
++      }
++
++      msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info,
++                                                  msi->dev_domain);
++      if (!msi->msi_domain) {
++              dev_err(dev, "failed to create MSI domain\n");
++              irq_domain_remove(msi->dev_domain);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void mc_handle_intx(struct irq_desc *desc)
++{
++      struct mc_pcie *port = irq_desc_get_handler_data(desc);
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++      struct device *dev = port->dev;
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      unsigned long status;
++      u32 bit;
++      int ret;
++
++      chained_irq_enter(chip, desc);
++
++      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++      if (status & PM_MSI_INT_INTX_MASK) {
++              status &= PM_MSI_INT_INTX_MASK;
++              status >>= PM_MSI_INT_INTX_SHIFT;
++              for_each_set_bit(bit, &status, PCI_NUM_INTX) {
++                      ret = generic_handle_domain_irq(port->intx_domain, bit);
++                      if (ret)
++                              dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
++                                                  bit);
++              }
++      }
++
++      chained_irq_exit(chip, desc);
++}
++
++static void mc_ack_intx_irq(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++
++      writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
++}
++
++static void mc_mask_intx_irq(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      unsigned long flags;
++      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++      u32 val;
++
++      raw_spin_lock_irqsave(&port->lock, flags);
++      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++      val &= ~mask;
++      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++      raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void mc_unmask_intx_irq(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      unsigned long flags;
++      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++      u32 val;
++
++      raw_spin_lock_irqsave(&port->lock, flags);
++      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++      val |= mask;
++      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++      raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static struct irq_chip mc_intx_irq_chip = {
++      .name = "Microchip PCIe INTx",
++      .irq_ack = mc_ack_intx_irq,
++      .irq_mask = mc_mask_intx_irq,
++      .irq_unmask = mc_unmask_intx_irq,
++};
++
++static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++                          irq_hw_number_t hwirq)
++{
++      irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
++      irq_set_chip_data(irq, domain->host_data);
++
++      return 0;
++}
++
++static const struct irq_domain_ops intx_domain_ops = {
++      .map = mc_pcie_intx_map,
++};
++
++static inline u32 reg_to_event(u32 reg, struct event_map field)
++{
++      return (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
++}
++
++static u32 pcie_events(struct mc_pcie *port)
++{
++      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++      u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT);
++      u32 val = 0;
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++)
++              val |= reg_to_event(reg, pcie_event_to_event[i]);
++
++      return val;
++}
++
++static u32 sec_errors(struct mc_pcie *port)
++{
++      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++      u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT);
++      u32 val = 0;
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++)
++              val |= reg_to_event(reg, sec_error_to_event[i]);
++
++      return val;
++}
++
++static u32 ded_errors(struct mc_pcie *port)
++{
++      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++      u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT);
++      u32 val = 0;
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++)
++              val |= reg_to_event(reg, ded_error_to_event[i]);
++
++      return val;
++}
++
++static u32 local_events(struct mc_pcie *port)
++{
++      void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++      u32 val = 0;
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++)
++              val |= reg_to_event(reg, local_status_to_event[i]);
++
++      return val;
++}
++
++static u32 get_events(struct mc_pcie *port)
++{
++      u32 events = 0;
++
++      events |= pcie_events(port);
++      events |= sec_errors(port);
++      events |= ded_errors(port);
++      events |= local_events(port);
++
++      return events;
++}
++
++static irqreturn_t mc_event_handler(int irq, void *dev_id)
++{
++      struct mc_pcie *port = dev_id;
++      struct device *dev = port->dev;
++      struct irq_data *data;
++
++      data = irq_domain_get_irq_data(port->event_domain, irq);
++
++      if (event_cause[data->hwirq].str)
++              dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str);
++      else
++              dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq);
++
++      return IRQ_HANDLED;
++}
++
++static void mc_handle_event(struct irq_desc *desc)
++{
++      struct mc_pcie *port = irq_desc_get_handler_data(desc);
++      unsigned long events;
++      u32 bit;
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++
++      chained_irq_enter(chip, desc);
++
++      events = get_events(port);
++
++      for_each_set_bit(bit, &events, NUM_EVENTS)
++              generic_handle_domain_irq(port->event_domain, bit);
++
++      chained_irq_exit(chip, desc);
++}
++
++static void mc_ack_event_irq(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      u32 event = data->hwirq;
++      void __iomem *addr;
++      u32 mask;
++
++      addr = port->axi_base_addr + event_descs[event].base +
++              event_descs[event].offset;
++      mask = event_descs[event].mask;
++      mask |= event_descs[event].enb_mask;
++
++      writel_relaxed(mask, addr);
++}
++
++static void mc_mask_event_irq(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      u32 event = data->hwirq;
++      void __iomem *addr;
++      u32 mask;
++      u32 val;
++
++      addr = port->axi_base_addr + event_descs[event].base +
++              event_descs[event].mask_offset;
++      mask = event_descs[event].mask;
++      if (event_descs[event].enb_mask) {
++              mask <<= PCIE_EVENT_INT_ENB_SHIFT;
++              mask &= PCIE_EVENT_INT_ENB_MASK;
++      }
++
++      if (!event_descs[event].mask_high)
++              mask = ~mask;
++
++      raw_spin_lock(&port->lock);
++      val = readl_relaxed(addr);
++      if (event_descs[event].mask_high)
++              val |= mask;
++      else
++              val &= mask;
++
++      writel_relaxed(val, addr);
++      raw_spin_unlock(&port->lock);
++}
++
++static void mc_unmask_event_irq(struct irq_data *data)
++{
++      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      u32 event = data->hwirq;
++      void __iomem *addr;
++      u32 mask;
++      u32 val;
++
++      addr = port->axi_base_addr + event_descs[event].base +
++              event_descs[event].mask_offset;
++      mask = event_descs[event].mask;
++
++      if (event_descs[event].enb_mask)
++              mask <<= PCIE_EVENT_INT_ENB_SHIFT;
++
++      if (event_descs[event].mask_high)
++              mask = ~mask;
++
++      if (event_descs[event].enb_mask)
++              mask &= PCIE_EVENT_INT_ENB_MASK;
++
++      raw_spin_lock(&port->lock);
++      val = readl_relaxed(addr);
++      if (event_descs[event].mask_high)
++              val &= mask;
++      else
++              val |= mask;
++      writel_relaxed(val, addr);
++      raw_spin_unlock(&port->lock);
++}
++
++static struct irq_chip mc_event_irq_chip = {
++      .name = "Microchip PCIe EVENT",
++      .irq_ack = mc_ack_event_irq,
++      .irq_mask = mc_mask_event_irq,
++      .irq_unmask = mc_unmask_event_irq,
++};
++
++static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
++                           irq_hw_number_t hwirq)
++{
++      irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
++      irq_set_chip_data(irq, domain->host_data);
++
++      return 0;
++}
++
++static const struct irq_domain_ops event_domain_ops = {
++      .map = mc_pcie_event_map,
++};
++
++static inline void mc_pcie_deinit_clk(void *data)
++{
++      struct clk *clk = data;
++
++      clk_disable_unprepare(clk);
++}
++
++static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id)
++{
++      struct clk *clk;
++      int ret;
++
++      clk = devm_clk_get_optional(dev, id);
++      if (IS_ERR(clk))
++              return clk;
++      if (!clk)
++              return clk;
++
++      ret = clk_prepare_enable(clk);
++      if (ret)
++              return ERR_PTR(ret);
++
++      devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk);
++
++      return clk;
++}
++
++static int mc_pcie_init_clks(struct device *dev)
++{
++      int i;
++      struct clk *fic;
++
++      /*
++       * PCIe may be clocked via Fabric Interface using between 1 and 4
++       * clocks. Scan DT for clocks and enable them if present
++       */
++      for (i = 0; i < ARRAY_SIZE(poss_clks); i++) {
++              fic = mc_pcie_init_clk(dev, poss_clks[i]);
++              if (IS_ERR(fic))
++                      return PTR_ERR(fic);
++      }
++
++      return 0;
++}
++
++static int mc_pcie_init_irq_domains(struct mc_pcie *port)
++{
++      struct device *dev = port->dev;
++      struct device_node *node = dev->of_node;
++      struct device_node *pcie_intc_node;
++
++      /* Setup INTx */
++      pcie_intc_node = of_get_next_child(node, NULL);
++      if (!pcie_intc_node) {
++              dev_err(dev, "failed to find PCIe Intc node\n");
++              return -EINVAL;
++      }
++
++      port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
++                                                 &event_domain_ops, port);
++      if (!port->event_domain) {
++              dev_err(dev, "failed to get event domain\n");
++              of_node_put(pcie_intc_node);
++              return -ENOMEM;
++      }
++
++      irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
++
++      port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
++                                                &intx_domain_ops, port);
++      if (!port->intx_domain) {
++              dev_err(dev, "failed to get an INTx IRQ domain\n");
++              of_node_put(pcie_intc_node);
++              return -ENOMEM;
++      }
++
++      irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
++
++      of_node_put(pcie_intc_node);
++      raw_spin_lock_init(&port->lock);
++
++      return mc_allocate_msi_domains(port);
++}
++
++static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++                               phys_addr_t axi_addr, phys_addr_t pci_addr,
++                               size_t size)
++{
++      u32 atr_sz = ilog2(size) - 1;
++      u32 val;
++
++      if (index == 0)
++              val = PCIE_CONFIG_INTERFACE;
++      else
++              val = PCIE_TX_RX_INTERFACE;
++
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_TRSL_PARAM);
++
++      val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
++                          ATR_IMPL_ENABLE;
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_SRCADDR_PARAM);
++
++      val = upper_32_bits(axi_addr);
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_SRC_ADDR);
++
++      val = lower_32_bits(pci_addr);
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
++
++      val = upper_32_bits(pci_addr);
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
++
++      val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++      val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
++      writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++      writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
++}
++
++static int mc_pcie_setup_windows(struct platform_device *pdev,
++                               struct mc_pcie *port)
++{
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
++      struct resource_entry *entry;
++      u64 pci_addr;
++      u32 index = 1;
++
++      resource_list_for_each_entry(entry, &bridge->windows) {
++              if (resource_type(entry->res) == IORESOURCE_MEM) {
++                      pci_addr = entry->res->start - entry->offset;
++                      mc_pcie_setup_window(bridge_base_addr, index,
++                                           entry->res->start, pci_addr,
++                                           resource_size(entry->res));
++                      index++;
++              }
++      }
++
++      return 0;
++}
++
++static inline void mc_clear_secs(struct mc_pcie *port)
++{
++      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++
++      writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
++                     SEC_ERROR_INT);
++      writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT);
++}
++
++static inline void mc_clear_deds(struct mc_pcie *port)
++{
++      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++
++      writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
++                     DED_ERROR_INT);
++      writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT);
++}
++
++static void mc_disable_interrupts(struct mc_pcie *port)
++{
++      void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
++      u32 val;
++
++      /* Ensure ECC bypass is enabled */
++      val = ECC_CONTROL_TX_RAM_ECC_BYPASS |
++            ECC_CONTROL_RX_RAM_ECC_BYPASS |
++            ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS |
++            ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS;
++      writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
++
++      /* Disable SEC errors and clear any outstanding */
++      writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
++                     SEC_ERROR_INT_MASK);
++      mc_clear_secs(port);
++
++      /* Disable DED errors and clear any outstanding */
++      writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
++                     DED_ERROR_INT_MASK);
++      mc_clear_deds(port);
++
++      /* Disable local interrupts and clear any outstanding */
++      writel_relaxed(0, bridge_base_addr + IMASK_LOCAL);
++      writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL);
++      writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI);
++
++      /* Disable PCIe events and clear any outstanding */
++      val = PCIE_EVENT_INT_L2_EXIT_INT |
++            PCIE_EVENT_INT_HOTRST_EXIT_INT |
++            PCIE_EVENT_INT_DLUP_EXIT_INT |
++            PCIE_EVENT_INT_L2_EXIT_INT_MASK |
++            PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK |
++            PCIE_EVENT_INT_DLUP_EXIT_INT_MASK;
++      writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
++
++      /* Disable host interrupts and clear any outstanding */
++      writel_relaxed(0, bridge_base_addr + IMASK_HOST);
++      writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
++}
++
++static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port)
++{
++      struct device *dev = &pdev->dev;
++      int irq;
++      int i, intx_irq, msi_irq, event_irq;
++      int ret;
++
++      ret = mc_pcie_init_irq_domains(port);
++      if (ret) {
++              dev_err(dev, "failed creating IRQ domains\n");
++              return ret;
++      }
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0)
++              return -ENODEV;
++
++      for (i = 0; i < NUM_EVENTS; i++) {
++              event_irq = irq_create_mapping(port->event_domain, i);
++              if (!event_irq) {
++                      dev_err(dev, "failed to map hwirq %d\n", i);
++                      return -ENXIO;
++              }
++
++              ret = devm_request_irq(dev, event_irq, mc_event_handler,
++                                     0, event_cause[i].sym, port);
++              if (ret) {
++                      dev_err(dev, "failed to request IRQ %d\n", event_irq);
++                      return ret;
++              }
++      }
++
++      intx_irq = irq_create_mapping(port->event_domain,
++                                    EVENT_LOCAL_PM_MSI_INT_INTX);
++      if (!intx_irq) {
++              dev_err(dev, "failed to map INTx interrupt\n");
++              return -ENXIO;
++      }
++
++      /* Plug the INTx chained handler */
++      irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
++
++      msi_irq = irq_create_mapping(port->event_domain,
++                                   EVENT_LOCAL_PM_MSI_INT_MSI);
++      if (!msi_irq)
++              return -ENXIO;
++
++      /* Plug the MSI chained handler */
++      irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
++
++      /* Plug the main event chained handler */
++      irq_set_chained_handler_and_data(irq, mc_handle_event, port);
++
++      return 0;
++}
++
++static int mc_platform_init(struct pci_config_window *cfg)
++{
++      struct device *dev = cfg->parent;
++      struct platform_device *pdev = to_platform_device(dev);
++      void __iomem *bridge_base_addr =
++              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      int ret;
++
++      /* Configure address translation table 0 for PCIe config space */
++      mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
++                           cfg->res.start,
++                           resource_size(&cfg->res));
++
++      /* Need some fixups in config space */
++      mc_pcie_enable_msi(port, cfg->win);
++
++      /* Configure non-config space outbound ranges */
++      ret = mc_pcie_setup_windows(pdev, port);
++      if (ret)
++              return ret;
++
++      /* Address translation is up; safe to enable interrupts */
++      ret = mc_init_interrupts(pdev, port);
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++static int mc_host_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      void __iomem *bridge_base_addr;
++      int ret;
++      u32 val;
++
++      port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
++      if (!port)
++              return -ENOMEM;
++
++      port->dev = dev;
++
++      port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
++      if (IS_ERR(port->axi_base_addr))
++              return PTR_ERR(port->axi_base_addr);
++
++      mc_disable_interrupts(port);
++
++      bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++
++      /* Allow enabling MSI by disabling MSI-X */
++      val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
++      val &= ~MSIX_CAP_MASK;
++      writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0);
++
++      /* Pick num vectors from bitfile programmed onto FPGA fabric */
++      val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
++      val &= NUM_MSI_MSGS_MASK;
++      val >>= NUM_MSI_MSGS_SHIFT;
++
++      port->msi.num_vectors = 1 << val;
++
++      /* Pick vector address from design */
++      port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
++
++      ret = mc_pcie_init_clks(dev);
++      if (ret) {
++              dev_err(dev, "failed to get clock resources, error %d\n", ret);
++              return -ENODEV;
++      }
++
++      return pci_host_common_probe(pdev);
++}
++
++static const struct pci_ecam_ops mc_ecam_ops = {
++      .init = mc_platform_init,
++      .pci_ops = {
++              .map_bus = pci_ecam_map_bus,
++              .read = pci_generic_config_read,
++              .write = pci_generic_config_write,
++      }
++};
++
++static const struct of_device_id mc_pcie_of_match[] = {
++      {
++              .compatible = "microchip,pcie-host-1.0",
++              .data = &mc_ecam_ops,
++      },
++      {},
++};
++
++MODULE_DEVICE_TABLE(of, mc_pcie_of_match);
++
++static struct platform_driver mc_pcie_driver = {
++      .probe = mc_host_probe,
++      .driver = {
++              .name = "microchip-pcie",
++              .of_match_table = mc_pcie_of_match,
++              .suppress_bind_attrs = true,
++      },
++};
++
++builtin_platform_driver(mc_pcie_driver);
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Microchip PCIe host controller driver");
++MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
diff --git a/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch b/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch
new file mode 100644 (file)
index 0000000..0aacec2
--- /dev/null
@@ -0,0 +1,259 @@
+From eca1b864bb2d4e8d9811506979560a89351c2e37 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:53 +0800
+Subject: [PATCH 016/116] PCI: microchip: Move PLDA IP register macros to
+ pcie-plda.h
+
+Move PLDA PCIe host controller IP registers macros to pcie-plda.h,
+including bridge registers and local IRQ event number.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 108 +++---------------
+ drivers/pci/controller/plda/pcie-plda.h       | 108 ++++++++++++++++++
+ 2 files changed, 124 insertions(+), 92 deletions(-)
+ create mode 100644 drivers/pci/controller/plda/pcie-plda.h
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -19,6 +19,7 @@
+ #include <linux/platform_device.h>
+ #include "../../pci.h"
++#include "pcie-plda.h"
+ /* Number of MSI IRQs */
+ #define MC_MAX_NUM_MSI_IRQS                   32
+@@ -30,84 +31,6 @@
+ #define MC_PCIE_BRIDGE_ADDR                   (MC_PCIE1_BRIDGE_ADDR)
+ #define MC_PCIE_CTRL_ADDR                     (MC_PCIE1_CTRL_ADDR)
+-/* PCIe Bridge Phy Regs */
+-#define PCIE_PCI_IRQ_DW0                      0xa8
+-#define  MSIX_CAP_MASK                                BIT(31)
+-#define  NUM_MSI_MSGS_MASK                    GENMASK(6, 4)
+-#define  NUM_MSI_MSGS_SHIFT                   4
+-
+-#define IMASK_LOCAL                           0x180
+-#define  DMA_END_ENGINE_0_MASK                        0x00000000u
+-#define  DMA_END_ENGINE_0_SHIFT                       0
+-#define  DMA_END_ENGINE_1_MASK                        0x00000000u
+-#define  DMA_END_ENGINE_1_SHIFT                       1
+-#define  DMA_ERROR_ENGINE_0_MASK              0x00000100u
+-#define  DMA_ERROR_ENGINE_0_SHIFT             8
+-#define  DMA_ERROR_ENGINE_1_MASK              0x00000200u
+-#define  DMA_ERROR_ENGINE_1_SHIFT             9
+-#define  A_ATR_EVT_POST_ERR_MASK              0x00010000u
+-#define  A_ATR_EVT_POST_ERR_SHIFT             16
+-#define  A_ATR_EVT_FETCH_ERR_MASK             0x00020000u
+-#define  A_ATR_EVT_FETCH_ERR_SHIFT            17
+-#define  A_ATR_EVT_DISCARD_ERR_MASK           0x00040000u
+-#define  A_ATR_EVT_DISCARD_ERR_SHIFT          18
+-#define  A_ATR_EVT_DOORBELL_MASK              0x00000000u
+-#define  A_ATR_EVT_DOORBELL_SHIFT             19
+-#define  P_ATR_EVT_POST_ERR_MASK              0x00100000u
+-#define  P_ATR_EVT_POST_ERR_SHIFT             20
+-#define  P_ATR_EVT_FETCH_ERR_MASK             0x00200000u
+-#define  P_ATR_EVT_FETCH_ERR_SHIFT            21
+-#define  P_ATR_EVT_DISCARD_ERR_MASK           0x00400000u
+-#define  P_ATR_EVT_DISCARD_ERR_SHIFT          22
+-#define  P_ATR_EVT_DOORBELL_MASK              0x00000000u
+-#define  P_ATR_EVT_DOORBELL_SHIFT             23
+-#define  PM_MSI_INT_INTA_MASK                 0x01000000u
+-#define  PM_MSI_INT_INTA_SHIFT                        24
+-#define  PM_MSI_INT_INTB_MASK                 0x02000000u
+-#define  PM_MSI_INT_INTB_SHIFT                        25
+-#define  PM_MSI_INT_INTC_MASK                 0x04000000u
+-#define  PM_MSI_INT_INTC_SHIFT                        26
+-#define  PM_MSI_INT_INTD_MASK                 0x08000000u
+-#define  PM_MSI_INT_INTD_SHIFT                        27
+-#define  PM_MSI_INT_INTX_MASK                 0x0f000000u
+-#define  PM_MSI_INT_INTX_SHIFT                        24
+-#define  PM_MSI_INT_MSI_MASK                  0x10000000u
+-#define  PM_MSI_INT_MSI_SHIFT                 28
+-#define  PM_MSI_INT_AER_EVT_MASK              0x20000000u
+-#define  PM_MSI_INT_AER_EVT_SHIFT             29
+-#define  PM_MSI_INT_EVENTS_MASK                       0x40000000u
+-#define  PM_MSI_INT_EVENTS_SHIFT              30
+-#define  PM_MSI_INT_SYS_ERR_MASK              0x80000000u
+-#define  PM_MSI_INT_SYS_ERR_SHIFT             31
+-#define  NUM_LOCAL_EVENTS                     15
+-#define ISTATUS_LOCAL                         0x184
+-#define IMASK_HOST                            0x188
+-#define ISTATUS_HOST                          0x18c
+-#define IMSI_ADDR                             0x190
+-#define ISTATUS_MSI                           0x194
+-
+-/* PCIe Master table init defines */
+-#define ATR0_PCIE_WIN0_SRCADDR_PARAM          0x600u
+-#define  ATR0_PCIE_ATR_SIZE                   0x25
+-#define  ATR0_PCIE_ATR_SIZE_SHIFT             1
+-#define ATR0_PCIE_WIN0_SRC_ADDR                       0x604u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB          0x608u
+-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW          0x60cu
+-#define ATR0_PCIE_WIN0_TRSL_PARAM             0x610u
+-
+-/* PCIe AXI slave table init defines */
+-#define ATR0_AXI4_SLV0_SRCADDR_PARAM          0x800u
+-#define  ATR_SIZE_SHIFT                               1
+-#define  ATR_IMPL_ENABLE                      1
+-#define ATR0_AXI4_SLV0_SRC_ADDR                       0x804u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB          0x808u
+-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW          0x80cu
+-#define ATR0_AXI4_SLV0_TRSL_PARAM             0x810u
+-#define  PCIE_TX_RX_INTERFACE                 0x00000000u
+-#define  PCIE_CONFIG_INTERFACE                        0x00000001u
+-
+-#define ATR_ENTRY_SIZE                                32
+-
+ /* PCIe Controller Phy Regs */
+ #define SEC_ERROR_EVENT_CNT                   0x20
+ #define DED_ERROR_EVENT_CNT                   0x24
+@@ -179,20 +102,21 @@
+ #define EVENT_LOCAL_DMA_END_ENGINE_1          12
+ #define EVENT_LOCAL_DMA_ERROR_ENGINE_0                13
+ #define EVENT_LOCAL_DMA_ERROR_ENGINE_1                14
+-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR                15
+-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR               16
+-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR     17
+-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL                18
+-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR                19
+-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR               20
+-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR     21
+-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL                22
+-#define EVENT_LOCAL_PM_MSI_INT_INTX           23
+-#define EVENT_LOCAL_PM_MSI_INT_MSI            24
+-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT                25
+-#define EVENT_LOCAL_PM_MSI_INT_EVENTS         26
+-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR                27
+-#define NUM_EVENTS                            28
++#define NUM_MC_EVENTS                         15
++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR                (NUM_MC_EVENTS + PLDA_AXI_POST_ERR)
++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR               (NUM_MC_EVENTS + PLDA_AXI_FETCH_ERR)
++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR     (NUM_MC_EVENTS + PLDA_AXI_DISCARD_ERR)
++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL                (NUM_MC_EVENTS + PLDA_AXI_DOORBELL)
++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR                (NUM_MC_EVENTS + PLDA_PCIE_POST_ERR)
++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR               (NUM_MC_EVENTS + PLDA_PCIE_FETCH_ERR)
++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR     (NUM_MC_EVENTS + PLDA_PCIE_DISCARD_ERR)
++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL                (NUM_MC_EVENTS + PLDA_PCIE_DOORBELL)
++#define EVENT_LOCAL_PM_MSI_INT_INTX           (NUM_MC_EVENTS + PLDA_INTX)
++#define EVENT_LOCAL_PM_MSI_INT_MSI            (NUM_MC_EVENTS + PLDA_MSI)
++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT                (NUM_MC_EVENTS + PLDA_AER_EVENT)
++#define EVENT_LOCAL_PM_MSI_INT_EVENTS         (NUM_MC_EVENTS + PLDA_MISC_EVENTS)
++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR                (NUM_MC_EVENTS + PLDA_SYS_ERR)
++#define NUM_EVENTS                            (NUM_MC_EVENTS + PLDA_INT_EVENT_NUM)
+ #define PCIE_EVENT_CAUSE(x, s)        \
+       [EVENT_PCIE_ ## x] = { __stringify(x), s }
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -0,0 +1,108 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PLDA PCIe host controller driver
++ */
++
++#ifndef _PCIE_PLDA_H
++#define _PCIE_PLDA_H
++
++/* PCIe Bridge Phy Regs */
++#define PCIE_PCI_IRQ_DW0                      0xa8
++#define  MSIX_CAP_MASK                                BIT(31)
++#define  NUM_MSI_MSGS_MASK                    GENMASK(6, 4)
++#define  NUM_MSI_MSGS_SHIFT                   4
++
++#define IMASK_LOCAL                           0x180
++#define  DMA_END_ENGINE_0_MASK                        0x00000000u
++#define  DMA_END_ENGINE_0_SHIFT                       0
++#define  DMA_END_ENGINE_1_MASK                        0x00000000u
++#define  DMA_END_ENGINE_1_SHIFT                       1
++#define  DMA_ERROR_ENGINE_0_MASK              0x00000100u
++#define  DMA_ERROR_ENGINE_0_SHIFT             8
++#define  DMA_ERROR_ENGINE_1_MASK              0x00000200u
++#define  DMA_ERROR_ENGINE_1_SHIFT             9
++#define  A_ATR_EVT_POST_ERR_MASK              0x00010000u
++#define  A_ATR_EVT_POST_ERR_SHIFT             16
++#define  A_ATR_EVT_FETCH_ERR_MASK             0x00020000u
++#define  A_ATR_EVT_FETCH_ERR_SHIFT            17
++#define  A_ATR_EVT_DISCARD_ERR_MASK           0x00040000u
++#define  A_ATR_EVT_DISCARD_ERR_SHIFT          18
++#define  A_ATR_EVT_DOORBELL_MASK              0x00000000u
++#define  A_ATR_EVT_DOORBELL_SHIFT             19
++#define  P_ATR_EVT_POST_ERR_MASK              0x00100000u
++#define  P_ATR_EVT_POST_ERR_SHIFT             20
++#define  P_ATR_EVT_FETCH_ERR_MASK             0x00200000u
++#define  P_ATR_EVT_FETCH_ERR_SHIFT            21
++#define  P_ATR_EVT_DISCARD_ERR_MASK           0x00400000u
++#define  P_ATR_EVT_DISCARD_ERR_SHIFT          22
++#define  P_ATR_EVT_DOORBELL_MASK              0x00000000u
++#define  P_ATR_EVT_DOORBELL_SHIFT             23
++#define  PM_MSI_INT_INTA_MASK                 0x01000000u
++#define  PM_MSI_INT_INTA_SHIFT                        24
++#define  PM_MSI_INT_INTB_MASK                 0x02000000u
++#define  PM_MSI_INT_INTB_SHIFT                        25
++#define  PM_MSI_INT_INTC_MASK                 0x04000000u
++#define  PM_MSI_INT_INTC_SHIFT                        26
++#define  PM_MSI_INT_INTD_MASK                 0x08000000u
++#define  PM_MSI_INT_INTD_SHIFT                        27
++#define  PM_MSI_INT_INTX_MASK                 0x0f000000u
++#define  PM_MSI_INT_INTX_SHIFT                        24
++#define  PM_MSI_INT_MSI_MASK                  0x10000000u
++#define  PM_MSI_INT_MSI_SHIFT                 28
++#define  PM_MSI_INT_AER_EVT_MASK              0x20000000u
++#define  PM_MSI_INT_AER_EVT_SHIFT             29
++#define  PM_MSI_INT_EVENTS_MASK                       0x40000000u
++#define  PM_MSI_INT_EVENTS_SHIFT              30
++#define  PM_MSI_INT_SYS_ERR_MASK              0x80000000u
++#define  PM_MSI_INT_SYS_ERR_SHIFT             31
++#define  NUM_LOCAL_EVENTS                     15
++#define ISTATUS_LOCAL                         0x184
++#define IMASK_HOST                            0x188
++#define ISTATUS_HOST                          0x18c
++#define IMSI_ADDR                             0x190
++#define ISTATUS_MSI                           0x194
++
++/* PCIe Master table init defines */
++#define ATR0_PCIE_WIN0_SRCADDR_PARAM          0x600u
++#define  ATR0_PCIE_ATR_SIZE                   0x25
++#define  ATR0_PCIE_ATR_SIZE_SHIFT             1
++#define ATR0_PCIE_WIN0_SRC_ADDR                       0x604u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB          0x608u
++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW          0x60cu
++#define ATR0_PCIE_WIN0_TRSL_PARAM             0x610u
++
++/* PCIe AXI slave table init defines */
++#define ATR0_AXI4_SLV0_SRCADDR_PARAM          0x800u
++#define  ATR_SIZE_SHIFT                               1
++#define  ATR_IMPL_ENABLE                      1
++#define ATR0_AXI4_SLV0_SRC_ADDR                       0x804u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB          0x808u
++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW          0x80cu
++#define ATR0_AXI4_SLV0_TRSL_PARAM             0x810u
++#define  PCIE_TX_RX_INTERFACE                 0x00000000u
++#define  PCIE_CONFIG_INTERFACE                        0x00000001u
++
++#define ATR_ENTRY_SIZE                                32
++
++enum plda_int_event {
++      PLDA_AXI_POST_ERR,
++      PLDA_AXI_FETCH_ERR,
++      PLDA_AXI_DISCARD_ERR,
++      PLDA_AXI_DOORBELL,
++      PLDA_PCIE_POST_ERR,
++      PLDA_PCIE_FETCH_ERR,
++      PLDA_PCIE_DISCARD_ERR,
++      PLDA_PCIE_DOORBELL,
++      PLDA_INTX,
++      PLDA_MSI,
++      PLDA_AER_EVENT,
++      PLDA_MISC_EVENTS,
++      PLDA_SYS_ERR,
++      PLDA_INT_EVENT_NUM
++};
++
++#define PLDA_NUM_DMA_EVENTS                   16
++
++#define PLDA_MAX_INT_NUM                      (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
++
++#endif
diff --git a/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch b/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch
new file mode 100644 (file)
index 0000000..bc3f909
--- /dev/null
@@ -0,0 +1,107 @@
+From 6ee4d4568314425079ae88229bb9abbff9b92b8b Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:54 +0800
+Subject: [PATCH 017/116] PCI: microchip: Add bridge_addr field to struct
+ mc_pcie
+
+For bridge address base is common PLDA field, Add this to struct mc_pcie
+first.
+
+INTx and MSI codes interrupts codes will get the bridge base address from
+port->bridge_addr. These codes will be changed to common codes.
+axi_base_addr is Microchip its own data.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 23 ++++++++-----------
+ 1 file changed, 9 insertions(+), 14 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -195,6 +195,7 @@ struct mc_pcie {
+       struct irq_domain *event_domain;
+       raw_spinlock_t lock;
+       struct mc_msi msi;
++      void __iomem *bridge_addr;
+ };
+ struct cause {
+@@ -339,8 +340,7 @@ static void mc_handle_msi(struct irq_des
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct device *dev = port->dev;
+       struct mc_msi *msi = &port->msi;
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long status;
+       u32 bit;
+       int ret;
+@@ -365,8 +365,7 @@ static void mc_handle_msi(struct irq_des
+ static void mc_msi_bottom_irq_ack(struct irq_data *data)
+ {
+       struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       u32 bitpos = data->hwirq;
+       writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+@@ -488,8 +487,7 @@ static void mc_handle_intx(struct irq_de
+       struct mc_pcie *port = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct device *dev = port->dev;
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long status;
+       u32 bit;
+       int ret;
+@@ -514,8 +512,7 @@ static void mc_handle_intx(struct irq_de
+ static void mc_ack_intx_irq(struct irq_data *data)
+ {
+       struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+       writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+@@ -524,8 +521,7 @@ static void mc_ack_intx_irq(struct irq_d
+ static void mc_mask_intx_irq(struct irq_data *data)
+ {
+       struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long flags;
+       u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+       u32 val;
+@@ -540,8 +536,7 @@ static void mc_mask_intx_irq(struct irq_
+ static void mc_unmask_intx_irq(struct irq_data *data)
+ {
+       struct mc_pcie *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long flags;
+       u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+       u32 val;
+@@ -896,8 +891,7 @@ static void mc_pcie_setup_window(void __
+ static int mc_pcie_setup_windows(struct platform_device *pdev,
+                                struct mc_pcie *port)
+ {
+-      void __iomem *bridge_base_addr =
+-              port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      void __iomem *bridge_base_addr = port->bridge_addr;
+       struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+       struct resource_entry *entry;
+       u64 pci_addr;
+@@ -1081,6 +1075,7 @@ static int mc_host_probe(struct platform
+       mc_disable_interrupts(port);
+       bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
++      port->bridge_addr = bridge_base_addr;
+       /* Allow enabling MSI by disabling MSI-X */
+       val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
diff --git a/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch b/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch
new file mode 100644 (file)
index 0000000..a9e7b89
--- /dev/null
@@ -0,0 +1,347 @@
+From 7c1c679bdd0b6b727248edbee77836024c935f91 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:55 +0800
+Subject: [PATCH 018/116] PCI: microchip: Rename two PCIe data structures
+
+Add PLDA PCIe related data structures by rename data structure name from
+mc_* to plda_*.
+
+axi_base_addr is stayed in struct mc_pcie for it's microchip its own data.
+
+The event interrupt codes is still using struct mc_pcie because the event
+interrupt codes can not be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 96 ++++++++++---------
+ 1 file changed, 53 insertions(+), 43 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -22,7 +22,7 @@
+ #include "pcie-plda.h"
+ /* Number of MSI IRQs */
+-#define MC_MAX_NUM_MSI_IRQS                   32
++#define PLDA_MAX_NUM_MSI_IRQS                 32
+ /* PCIe Bridge Phy and Controller Phy offsets */
+ #define MC_PCIE1_BRIDGE_ADDR                  0x00008000u
+@@ -179,25 +179,29 @@ struct event_map {
+       u32 event_bit;
+ };
+-struct mc_msi {
++struct plda_msi {
+       struct mutex lock;              /* Protect used bitmap */
+       struct irq_domain *msi_domain;
+       struct irq_domain *dev_domain;
+       u32 num_vectors;
+       u64 vector_phy;
+-      DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
++      DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS);
+ };
+-struct mc_pcie {
+-      void __iomem *axi_base_addr;
++struct plda_pcie_rp {
+       struct device *dev;
+       struct irq_domain *intx_domain;
+       struct irq_domain *event_domain;
+       raw_spinlock_t lock;
+-      struct mc_msi msi;
++      struct plda_msi msi;
+       void __iomem *bridge_addr;
+ };
++struct mc_pcie {
++      struct plda_pcie_rp plda;
++      void __iomem *axi_base_addr;
++};
++
+ struct cause {
+       const char *sym;
+       const char *str;
+@@ -313,7 +317,7 @@ static struct mc_pcie *port;
+ static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam)
+ {
+-      struct mc_msi *msi = &port->msi;
++      struct plda_msi *msi = &port->plda.msi;
+       u16 reg;
+       u8 queue_size;
+@@ -336,10 +340,10 @@ static void mc_pcie_enable_msi(struct mc
+ static void mc_handle_msi(struct irq_desc *desc)
+ {
+-      struct mc_pcie *port = irq_desc_get_handler_data(desc);
++      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct device *dev = port->dev;
+-      struct mc_msi *msi = &port->msi;
++      struct plda_msi *msi = &port->msi;
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long status;
+       u32 bit;
+@@ -364,7 +368,7 @@ static void mc_handle_msi(struct irq_des
+ static void mc_msi_bottom_irq_ack(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       u32 bitpos = data->hwirq;
+@@ -373,7 +377,7 @@ static void mc_msi_bottom_irq_ack(struct
+ static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       phys_addr_t addr = port->msi.vector_phy;
+       msg->address_lo = lower_32_bits(addr);
+@@ -400,8 +404,8 @@ static struct irq_chip mc_msi_bottom_irq
+ static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                  unsigned int nr_irqs, void *args)
+ {
+-      struct mc_pcie *port = domain->host_data;
+-      struct mc_msi *msi = &port->msi;
++      struct plda_pcie_rp *port = domain->host_data;
++      struct plda_msi *msi = &port->msi;
+       unsigned long bit;
+       mutex_lock(&msi->lock);
+@@ -425,8 +429,8 @@ static void mc_irq_msi_domain_free(struc
+                                  unsigned int nr_irqs)
+ {
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(d);
+-      struct mc_msi *msi = &port->msi;
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
++      struct plda_msi *msi = &port->msi;
+       mutex_lock(&msi->lock);
+@@ -456,11 +460,11 @@ static struct msi_domain_info mc_msi_dom
+       .chip = &mc_msi_irq_chip,
+ };
+-static int mc_allocate_msi_domains(struct mc_pcie *port)
++static int mc_allocate_msi_domains(struct plda_pcie_rp *port)
+ {
+       struct device *dev = port->dev;
+       struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+-      struct mc_msi *msi = &port->msi;
++      struct plda_msi *msi = &port->msi;
+       mutex_init(&port->msi.lock);
+@@ -484,7 +488,7 @@ static int mc_allocate_msi_domains(struc
+ static void mc_handle_intx(struct irq_desc *desc)
+ {
+-      struct mc_pcie *port = irq_desc_get_handler_data(desc);
++      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct device *dev = port->dev;
+       void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -511,7 +515,7 @@ static void mc_handle_intx(struct irq_de
+ static void mc_ack_intx_irq(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+@@ -520,7 +524,7 @@ static void mc_ack_intx_irq(struct irq_d
+ static void mc_mask_intx_irq(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long flags;
+       u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+@@ -535,7 +539,7 @@ static void mc_mask_intx_irq(struct irq_
+ static void mc_unmask_intx_irq(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       unsigned long flags;
+       u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+@@ -625,21 +629,22 @@ static u32 local_events(struct mc_pcie *
+       return val;
+ }
+-static u32 get_events(struct mc_pcie *port)
++static u32 get_events(struct plda_pcie_rp *port)
+ {
++      struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+       u32 events = 0;
+-      events |= pcie_events(port);
+-      events |= sec_errors(port);
+-      events |= ded_errors(port);
+-      events |= local_events(port);
++      events |= pcie_events(mc_port);
++      events |= sec_errors(mc_port);
++      events |= ded_errors(mc_port);
++      events |= local_events(mc_port);
+       return events;
+ }
+ static irqreturn_t mc_event_handler(int irq, void *dev_id)
+ {
+-      struct mc_pcie *port = dev_id;
++      struct plda_pcie_rp *port = dev_id;
+       struct device *dev = port->dev;
+       struct irq_data *data;
+@@ -655,7 +660,7 @@ static irqreturn_t mc_event_handler(int
+ static void mc_handle_event(struct irq_desc *desc)
+ {
+-      struct mc_pcie *port = irq_desc_get_handler_data(desc);
++      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+       unsigned long events;
+       u32 bit;
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -672,12 +677,13 @@ static void mc_handle_event(struct irq_d
+ static void mc_ack_event_irq(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+       u32 event = data->hwirq;
+       void __iomem *addr;
+       u32 mask;
+-      addr = port->axi_base_addr + event_descs[event].base +
++      addr = mc_port->axi_base_addr + event_descs[event].base +
+               event_descs[event].offset;
+       mask = event_descs[event].mask;
+       mask |= event_descs[event].enb_mask;
+@@ -687,13 +693,14 @@ static void mc_ack_event_irq(struct irq_
+ static void mc_mask_event_irq(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+       u32 event = data->hwirq;
+       void __iomem *addr;
+       u32 mask;
+       u32 val;
+-      addr = port->axi_base_addr + event_descs[event].base +
++      addr = mc_port->axi_base_addr + event_descs[event].base +
+               event_descs[event].mask_offset;
+       mask = event_descs[event].mask;
+       if (event_descs[event].enb_mask) {
+@@ -717,13 +724,14 @@ static void mc_mask_event_irq(struct irq
+ static void mc_unmask_event_irq(struct irq_data *data)
+ {
+-      struct mc_pcie *port = irq_data_get_irq_chip_data(data);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+       u32 event = data->hwirq;
+       void __iomem *addr;
+       u32 mask;
+       u32 val;
+-      addr = port->axi_base_addr + event_descs[event].base +
++      addr = mc_port->axi_base_addr + event_descs[event].base +
+               event_descs[event].mask_offset;
+       mask = event_descs[event].mask;
+@@ -811,7 +819,7 @@ static int mc_pcie_init_clks(struct devi
+       return 0;
+ }
+-static int mc_pcie_init_irq_domains(struct mc_pcie *port)
++static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port)
+ {
+       struct device *dev = port->dev;
+       struct device_node *node = dev->of_node;
+@@ -889,7 +897,7 @@ static void mc_pcie_setup_window(void __
+ }
+ static int mc_pcie_setup_windows(struct platform_device *pdev,
+-                               struct mc_pcie *port)
++                               struct plda_pcie_rp *port)
+ {
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+@@ -970,7 +978,7 @@ static void mc_disable_interrupts(struct
+       writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port)
++static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
+ {
+       struct device *dev = &pdev->dev;
+       int irq;
+@@ -1043,12 +1051,12 @@ static int mc_platform_init(struct pci_c
+       mc_pcie_enable_msi(port, cfg->win);
+       /* Configure non-config space outbound ranges */
+-      ret = mc_pcie_setup_windows(pdev, port);
++      ret = mc_pcie_setup_windows(pdev, &port->plda);
+       if (ret)
+               return ret;
+       /* Address translation is up; safe to enable interrupts */
+-      ret = mc_init_interrupts(pdev, port);
++      ret = mc_init_interrupts(pdev, &port->plda);
+       if (ret)
+               return ret;
+@@ -1059,6 +1067,7 @@ static int mc_host_probe(struct platform
+ {
+       struct device *dev = &pdev->dev;
+       void __iomem *bridge_base_addr;
++      struct plda_pcie_rp *plda;
+       int ret;
+       u32 val;
+@@ -1066,7 +1075,8 @@ static int mc_host_probe(struct platform
+       if (!port)
+               return -ENOMEM;
+-      port->dev = dev;
++      plda = &port->plda;
++      plda->dev = dev;
+       port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
+       if (IS_ERR(port->axi_base_addr))
+@@ -1075,7 +1085,7 @@ static int mc_host_probe(struct platform
+       mc_disable_interrupts(port);
+       bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+-      port->bridge_addr = bridge_base_addr;
++      plda->bridge_addr = bridge_base_addr;
+       /* Allow enabling MSI by disabling MSI-X */
+       val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+@@ -1087,10 +1097,10 @@ static int mc_host_probe(struct platform
+       val &= NUM_MSI_MSGS_MASK;
+       val >>= NUM_MSI_MSGS_SHIFT;
+-      port->msi.num_vectors = 1 << val;
++      plda->msi.num_vectors = 1 << val;
+       /* Pick vector address from design */
+-      port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
++      plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
+       ret = mc_pcie_init_clks(dev);
+       if (ret) {
diff --git a/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch b/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch
new file mode 100644 (file)
index 0000000..0823502
--- /dev/null
@@ -0,0 +1,87 @@
+From a53cf9b237dd53c9538b51a8df592888935c8411 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:56 +0800
+Subject: [PATCH 019/116] PCI: microchip: Move PCIe host data structures to
+ plda-pcie.h
+
+Move the common data structures definition to head file for these two data
+structures can be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 20 ------------------
+ drivers/pci/controller/plda/pcie-plda.h       | 21 +++++++++++++++++++
+ 2 files changed, 21 insertions(+), 20 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -21,9 +21,6 @@
+ #include "../../pci.h"
+ #include "pcie-plda.h"
+-/* Number of MSI IRQs */
+-#define PLDA_MAX_NUM_MSI_IRQS                 32
+-
+ /* PCIe Bridge Phy and Controller Phy offsets */
+ #define MC_PCIE1_BRIDGE_ADDR                  0x00008000u
+ #define MC_PCIE1_CTRL_ADDR                    0x0000a000u
+@@ -179,23 +176,6 @@ struct event_map {
+       u32 event_bit;
+ };
+-struct plda_msi {
+-      struct mutex lock;              /* Protect used bitmap */
+-      struct irq_domain *msi_domain;
+-      struct irq_domain *dev_domain;
+-      u32 num_vectors;
+-      u64 vector_phy;
+-      DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS);
+-};
+-
+-struct plda_pcie_rp {
+-      struct device *dev;
+-      struct irq_domain *intx_domain;
+-      struct irq_domain *event_domain;
+-      raw_spinlock_t lock;
+-      struct plda_msi msi;
+-      void __iomem *bridge_addr;
+-};
+ struct mc_pcie {
+       struct plda_pcie_rp plda;
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -6,6 +6,9 @@
+ #ifndef _PCIE_PLDA_H
+ #define _PCIE_PLDA_H
++/* Number of MSI IRQs */
++#define PLDA_MAX_NUM_MSI_IRQS                 32
++
+ /* PCIe Bridge Phy Regs */
+ #define PCIE_PCI_IRQ_DW0                      0xa8
+ #define  MSIX_CAP_MASK                                BIT(31)
+@@ -105,4 +108,22 @@ enum plda_int_event {
+ #define PLDA_MAX_INT_NUM                      (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
++struct plda_msi {
++      struct mutex lock;              /* Protect used bitmap */
++      struct irq_domain *msi_domain;
++      struct irq_domain *dev_domain;
++      u32 num_vectors;
++      u64 vector_phy;
++      DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS);
++};
++
++struct plda_pcie_rp {
++      struct device *dev;
++      struct irq_domain *intx_domain;
++      struct irq_domain *event_domain;
++      raw_spinlock_t lock;
++      struct plda_msi msi;
++      void __iomem *bridge_addr;
++};
++
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch b/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch
new file mode 100644 (file)
index 0000000..2bed600
--- /dev/null
@@ -0,0 +1,76 @@
+From d0e56d1ef7398bbf76be6e48d77943cbf644688e Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:57 +0800
+Subject: [PATCH 020/116] PCI: microchip: Rename two setup functions
+
+Rename two setup functions to plda prefix. Prepare to re-use these two
+setup function.
+
+For two setup functions names are similar, rename mc_pcie_setup_windows()
+to plda_pcie_setup_iomems().
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 24 +++++++++----------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -838,9 +838,9 @@ static int mc_pcie_init_irq_domains(stru
+       return mc_allocate_msi_domains(port);
+ }
+-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+-                               phys_addr_t axi_addr, phys_addr_t pci_addr,
+-                               size_t size)
++static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++                                 phys_addr_t axi_addr, phys_addr_t pci_addr,
++                                 size_t size)
+ {
+       u32 atr_sz = ilog2(size) - 1;
+       u32 val;
+@@ -876,8 +876,8 @@ static void mc_pcie_setup_window(void __
+       writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+ }
+-static int mc_pcie_setup_windows(struct platform_device *pdev,
+-                               struct plda_pcie_rp *port)
++static int plda_pcie_setup_iomems(struct platform_device *pdev,
++                                struct plda_pcie_rp *port)
+ {
+       void __iomem *bridge_base_addr = port->bridge_addr;
+       struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+@@ -888,9 +888,9 @@ static int mc_pcie_setup_windows(struct
+       resource_list_for_each_entry(entry, &bridge->windows) {
+               if (resource_type(entry->res) == IORESOURCE_MEM) {
+                       pci_addr = entry->res->start - entry->offset;
+-                      mc_pcie_setup_window(bridge_base_addr, index,
+-                                           entry->res->start, pci_addr,
+-                                           resource_size(entry->res));
++                      plda_pcie_setup_window(bridge_base_addr, index,
++                                             entry->res->start, pci_addr,
++                                             resource_size(entry->res));
+                       index++;
+               }
+       }
+@@ -1023,15 +1023,15 @@ static int mc_platform_init(struct pci_c
+       int ret;
+       /* Configure address translation table 0 for PCIe config space */
+-      mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
+-                           cfg->res.start,
+-                           resource_size(&cfg->res));
++      plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
++                             cfg->res.start,
++                             resource_size(&cfg->res));
+       /* Need some fixups in config space */
+       mc_pcie_enable_msi(port, cfg->win);
+       /* Configure non-config space outbound ranges */
+-      ret = mc_pcie_setup_windows(pdev, &port->plda);
++      ret = plda_pcie_setup_iomems(pdev, &port->plda);
+       if (ret)
+               return ret;
diff --git a/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch b/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch
new file mode 100644 (file)
index 0000000..de6868b
--- /dev/null
@@ -0,0 +1,49 @@
+From 2fd7c88ef318fd39023ce1eb73f37a29fbd25d74 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:58 +0800
+Subject: [PATCH 021/116] PCI: microchip: Change the argument of
+ plda_pcie_setup_iomems()
+
+If other vendor do not select PCI_HOST_COMMON, the driver data is not
+struct pci_host_bridge.
+
+Move calling platform_get_drvdata() to mc_platform_init().
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -876,11 +876,10 @@ static void plda_pcie_setup_window(void
+       writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+ }
+-static int plda_pcie_setup_iomems(struct platform_device *pdev,
++static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
+                                 struct plda_pcie_rp *port)
+ {
+       void __iomem *bridge_base_addr = port->bridge_addr;
+-      struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+       struct resource_entry *entry;
+       u64 pci_addr;
+       u32 index = 1;
+@@ -1018,6 +1017,7 @@ static int mc_platform_init(struct pci_c
+ {
+       struct device *dev = cfg->parent;
+       struct platform_device *pdev = to_platform_device(dev);
++      struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
+       void __iomem *bridge_base_addr =
+               port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+       int ret;
+@@ -1031,7 +1031,7 @@ static int mc_platform_init(struct pci_c
+       mc_pcie_enable_msi(port, cfg->win);
+       /* Configure non-config space outbound ranges */
+-      ret = plda_pcie_setup_iomems(pdev, &port->plda);
++      ret = plda_pcie_setup_iomems(bridge, &port->plda);
+       if (ret)
+               return ret;
diff --git a/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch b/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch
new file mode 100644 (file)
index 0000000..62f458b
--- /dev/null
@@ -0,0 +1,200 @@
+From 201ce99897ff5fff2612cb633406e90c1b2acbcf Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:05:59 +0800
+Subject: [PATCH 022/116] PCI: microchip: Move setup functions to
+ pcie-plda-host.c
+
+Move setup functions to common pcie-plda-host.c. So these two functions
+can be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/Kconfig           |  4 +
+ drivers/pci/controller/plda/Makefile          |  1 +
+ .../pci/controller/plda/pcie-microchip-host.c | 59 ---------------
+ drivers/pci/controller/plda/pcie-plda-host.c  | 74 +++++++++++++++++++
+ drivers/pci/controller/plda/pcie-plda.h       |  5 ++
+ 5 files changed, 84 insertions(+), 59 deletions(-)
+ create mode 100644 drivers/pci/controller/plda/pcie-plda-host.c
+
+--- a/drivers/pci/controller/plda/Kconfig
++++ b/drivers/pci/controller/plda/Kconfig
+@@ -3,10 +3,14 @@
+ menu "PLDA-based PCIe controllers"
+       depends on PCI
++config PCIE_PLDA_HOST
++      bool
++
+ config PCIE_MICROCHIP_HOST
+       tristate "Microchip AXI PCIe controller"
+       depends on PCI_MSI && OF
+       select PCI_HOST_COMMON
++      select PCIE_PLDA_HOST
+       help
+         Say Y here if you want kernel to support the Microchip AXI PCIe
+         Host Bridge driver.
+--- a/drivers/pci/controller/plda/Makefile
++++ b/drivers/pci/controller/plda/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o
+ obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -838,65 +838,6 @@ static int mc_pcie_init_irq_domains(stru
+       return mc_allocate_msi_domains(port);
+ }
+-static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+-                                 phys_addr_t axi_addr, phys_addr_t pci_addr,
+-                                 size_t size)
+-{
+-      u32 atr_sz = ilog2(size) - 1;
+-      u32 val;
+-
+-      if (index == 0)
+-              val = PCIE_CONFIG_INTERFACE;
+-      else
+-              val = PCIE_TX_RX_INTERFACE;
+-
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_TRSL_PARAM);
+-
+-      val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
+-                          ATR_IMPL_ENABLE;
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_SRCADDR_PARAM);
+-
+-      val = upper_32_bits(axi_addr);
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_SRC_ADDR);
+-
+-      val = lower_32_bits(pci_addr);
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
+-
+-      val = upper_32_bits(pci_addr);
+-      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
+-             ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
+-
+-      val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+-      val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
+-      writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+-      writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+-}
+-
+-static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
+-                                struct plda_pcie_rp *port)
+-{
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      struct resource_entry *entry;
+-      u64 pci_addr;
+-      u32 index = 1;
+-
+-      resource_list_for_each_entry(entry, &bridge->windows) {
+-              if (resource_type(entry->res) == IORESOURCE_MEM) {
+-                      pci_addr = entry->res->start - entry->offset;
+-                      plda_pcie_setup_window(bridge_base_addr, index,
+-                                             entry->res->start, pci_addr,
+-                                             resource_size(entry->res));
+-                      index++;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+ static inline void mc_clear_secs(struct mc_pcie *port)
+ {
+       void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -0,0 +1,74 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PLDA PCIe XpressRich host controller driver
++ *
++ * Copyright (C) 2023 Microchip Co. Ltd
++ *
++ * Author: Daire McNamara <daire.mcnamara@microchip.com>
++ */
++
++#include <linux/pci_regs.h>
++#include <linux/pci-ecam.h>
++
++#include "pcie-plda.h"
++
++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++                          phys_addr_t axi_addr, phys_addr_t pci_addr,
++                          size_t size)
++{
++      u32 atr_sz = ilog2(size) - 1;
++      u32 val;
++
++      if (index == 0)
++              val = PCIE_CONFIG_INTERFACE;
++      else
++              val = PCIE_TX_RX_INTERFACE;
++
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_TRSL_PARAM);
++
++      val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
++                          ATR_IMPL_ENABLE;
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_SRCADDR_PARAM);
++
++      val = upper_32_bits(axi_addr);
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_SRC_ADDR);
++
++      val = lower_32_bits(pci_addr);
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
++
++      val = upper_32_bits(pci_addr);
++      writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
++             ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
++
++      val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++      val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
++      writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
++      writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
++}
++EXPORT_SYMBOL_GPL(plda_pcie_setup_window);
++
++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
++                         struct plda_pcie_rp *port)
++{
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      struct resource_entry *entry;
++      u64 pci_addr;
++      u32 index = 1;
++
++      resource_list_for_each_entry(entry, &bridge->windows) {
++              if (resource_type(entry->res) == IORESOURCE_MEM) {
++                      pci_addr = entry->res->start - entry->offset;
++                      plda_pcie_setup_window(bridge_base_addr, index,
++                                             entry->res->start, pci_addr,
++                                             resource_size(entry->res));
++                      index++;
++              }
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -126,4 +126,9 @@ struct plda_pcie_rp {
+       void __iomem *bridge_addr;
+ };
++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
++                          phys_addr_t axi_addr, phys_addr_t pci_addr,
++                          size_t size);
++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
++                         struct plda_pcie_rp *port);
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch b/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch
new file mode 100644 (file)
index 0000000..09c1633
--- /dev/null
@@ -0,0 +1,336 @@
+From 2a48bc45dcf8cbe736b594d013cfa9d682214c43 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:00 +0800
+Subject: [PATCH 023/116] PCI: microchip: Rename interrupt related functions
+
+Rename mc_* to plda_* for IRQ functions and related IRQ domain ops data
+instances.
+
+MSI, INTx interrupt code and IRQ init code are all can be re-used.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 109 +++++++++---------
+ 1 file changed, 57 insertions(+), 52 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -318,7 +318,7 @@ static void mc_pcie_enable_msi(struct mc
+                      ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
+ }
+-static void mc_handle_msi(struct irq_desc *desc)
++static void plda_handle_msi(struct irq_desc *desc)
+ {
+       struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -346,7 +346,7 @@ static void mc_handle_msi(struct irq_des
+       chained_irq_exit(chip, desc);
+ }
+-static void mc_msi_bottom_irq_ack(struct irq_data *data)
++static void plda_msi_bottom_irq_ack(struct irq_data *data)
+ {
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -355,7 +355,7 @@ static void mc_msi_bottom_irq_ack(struct
+       writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+ }
+-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ {
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       phys_addr_t addr = port->msi.vector_phy;
+@@ -368,21 +368,23 @@ static void mc_compose_msi_msg(struct ir
+               (int)data->hwirq, msg->address_hi, msg->address_lo);
+ }
+-static int mc_msi_set_affinity(struct irq_data *irq_data,
+-                             const struct cpumask *mask, bool force)
++static int plda_msi_set_affinity(struct irq_data *irq_data,
++                               const struct cpumask *mask, bool force)
+ {
+       return -EINVAL;
+ }
+-static struct irq_chip mc_msi_bottom_irq_chip = {
+-      .name = "Microchip MSI",
+-      .irq_ack = mc_msi_bottom_irq_ack,
+-      .irq_compose_msi_msg = mc_compose_msi_msg,
+-      .irq_set_affinity = mc_msi_set_affinity,
++static struct irq_chip plda_msi_bottom_irq_chip = {
++      .name = "PLDA MSI",
++      .irq_ack = plda_msi_bottom_irq_ack,
++      .irq_compose_msi_msg = plda_compose_msi_msg,
++      .irq_set_affinity = plda_msi_set_affinity,
+ };
+-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+-                                 unsigned int nr_irqs, void *args)
++static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
++                                   unsigned int virq,
++                                   unsigned int nr_irqs,
++                                   void *args)
+ {
+       struct plda_pcie_rp *port = domain->host_data;
+       struct plda_msi *msi = &port->msi;
+@@ -397,7 +399,7 @@ static int mc_irq_msi_domain_alloc(struc
+       set_bit(bit, msi->used);
+-      irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip,
++      irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
+                           domain->host_data, handle_edge_irq, NULL, NULL);
+       mutex_unlock(&msi->lock);
+@@ -405,8 +407,9 @@ static int mc_irq_msi_domain_alloc(struc
+       return 0;
+ }
+-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+-                                 unsigned int nr_irqs)
++static void plda_irq_msi_domain_free(struct irq_domain *domain,
++                                   unsigned int virq,
++                                   unsigned int nr_irqs)
+ {
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
+@@ -423,24 +426,24 @@ static void mc_irq_msi_domain_free(struc
+ }
+ static const struct irq_domain_ops msi_domain_ops = {
+-      .alloc  = mc_irq_msi_domain_alloc,
+-      .free   = mc_irq_msi_domain_free,
++      .alloc  = plda_irq_msi_domain_alloc,
++      .free   = plda_irq_msi_domain_free,
+ };
+-static struct irq_chip mc_msi_irq_chip = {
+-      .name = "Microchip PCIe MSI",
++static struct irq_chip plda_msi_irq_chip = {
++      .name = "PLDA PCIe MSI",
+       .irq_ack = irq_chip_ack_parent,
+       .irq_mask = pci_msi_mask_irq,
+       .irq_unmask = pci_msi_unmask_irq,
+ };
+-static struct msi_domain_info mc_msi_domain_info = {
++static struct msi_domain_info plda_msi_domain_info = {
+       .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+-      .chip = &mc_msi_irq_chip,
++      .chip = &plda_msi_irq_chip,
+ };
+-static int mc_allocate_msi_domains(struct plda_pcie_rp *port)
++static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
+ {
+       struct device *dev = port->dev;
+       struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+@@ -455,7 +458,8 @@ static int mc_allocate_msi_domains(struc
+               return -ENOMEM;
+       }
+-      msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info,
++      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++                                                  &plda_msi_domain_info,
+                                                   msi->dev_domain);
+       if (!msi->msi_domain) {
+               dev_err(dev, "failed to create MSI domain\n");
+@@ -466,7 +470,7 @@ static int mc_allocate_msi_domains(struc
+       return 0;
+ }
+-static void mc_handle_intx(struct irq_desc *desc)
++static void plda_handle_intx(struct irq_desc *desc)
+ {
+       struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -493,7 +497,7 @@ static void mc_handle_intx(struct irq_de
+       chained_irq_exit(chip, desc);
+ }
+-static void mc_ack_intx_irq(struct irq_data *data)
++static void plda_ack_intx_irq(struct irq_data *data)
+ {
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -502,7 +506,7 @@ static void mc_ack_intx_irq(struct irq_d
+       writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+ }
+-static void mc_mask_intx_irq(struct irq_data *data)
++static void plda_mask_intx_irq(struct irq_data *data)
+ {
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -517,7 +521,7 @@ static void mc_mask_intx_irq(struct irq_
+       raw_spin_unlock_irqrestore(&port->lock, flags);
+ }
+-static void mc_unmask_intx_irq(struct irq_data *data)
++static void plda_unmask_intx_irq(struct irq_data *data)
+ {
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+       void __iomem *bridge_base_addr = port->bridge_addr;
+@@ -532,24 +536,24 @@ static void mc_unmask_intx_irq(struct ir
+       raw_spin_unlock_irqrestore(&port->lock, flags);
+ }
+-static struct irq_chip mc_intx_irq_chip = {
+-      .name = "Microchip PCIe INTx",
+-      .irq_ack = mc_ack_intx_irq,
+-      .irq_mask = mc_mask_intx_irq,
+-      .irq_unmask = mc_unmask_intx_irq,
++static struct irq_chip plda_intx_irq_chip = {
++      .name = "PLDA PCIe INTx",
++      .irq_ack = plda_ack_intx_irq,
++      .irq_mask = plda_mask_intx_irq,
++      .irq_unmask = plda_unmask_intx_irq,
+ };
+-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+-                          irq_hw_number_t hwirq)
++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++                            irq_hw_number_t hwirq)
+ {
+-      irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq);
++      irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
+       irq_set_chip_data(irq, domain->host_data);
+       return 0;
+ }
+ static const struct irq_domain_ops intx_domain_ops = {
+-      .map = mc_pcie_intx_map,
++      .map = plda_pcie_intx_map,
+ };
+ static inline u32 reg_to_event(u32 reg, struct event_map field)
+@@ -609,7 +613,7 @@ static u32 local_events(struct mc_pcie *
+       return val;
+ }
+-static u32 get_events(struct plda_pcie_rp *port)
++static u32 mc_get_events(struct plda_pcie_rp *port)
+ {
+       struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda);
+       u32 events = 0;
+@@ -638,7 +642,7 @@ static irqreturn_t mc_event_handler(int
+       return IRQ_HANDLED;
+ }
+-static void mc_handle_event(struct irq_desc *desc)
++static void plda_handle_event(struct irq_desc *desc)
+ {
+       struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+       unsigned long events;
+@@ -647,7 +651,7 @@ static void mc_handle_event(struct irq_d
+       chained_irq_enter(chip, desc);
+-      events = get_events(port);
++      events = mc_get_events(port);
+       for_each_set_bit(bit, &events, NUM_EVENTS)
+               generic_handle_domain_irq(port->event_domain, bit);
+@@ -741,8 +745,8 @@ static struct irq_chip mc_event_irq_chip
+       .irq_unmask = mc_unmask_event_irq,
+ };
+-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+-                           irq_hw_number_t hwirq)
++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
++                             irq_hw_number_t hwirq)
+ {
+       irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
+       irq_set_chip_data(irq, domain->host_data);
+@@ -750,8 +754,8 @@ static int mc_pcie_event_map(struct irq_
+       return 0;
+ }
+-static const struct irq_domain_ops event_domain_ops = {
+-      .map = mc_pcie_event_map,
++static const struct irq_domain_ops plda_event_domain_ops = {
++      .map = plda_pcie_event_map,
+ };
+ static inline void mc_pcie_deinit_clk(void *data)
+@@ -799,7 +803,7 @@ static int mc_pcie_init_clks(struct devi
+       return 0;
+ }
+-static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port)
++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+ {
+       struct device *dev = port->dev;
+       struct device_node *node = dev->of_node;
+@@ -813,7 +817,8 @@ static int mc_pcie_init_irq_domains(stru
+       }
+       port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
+-                                                 &event_domain_ops, port);
++                                                 &plda_event_domain_ops,
++                                                 port);
+       if (!port->event_domain) {
+               dev_err(dev, "failed to get event domain\n");
+               of_node_put(pcie_intc_node);
+@@ -835,7 +840,7 @@ static int mc_pcie_init_irq_domains(stru
+       of_node_put(pcie_intc_node);
+       raw_spin_lock_init(&port->lock);
+-      return mc_allocate_msi_domains(port);
++      return plda_allocate_msi_domains(port);
+ }
+ static inline void mc_clear_secs(struct mc_pcie *port)
+@@ -898,14 +903,14 @@ static void mc_disable_interrupts(struct
+       writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+-static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
++static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
+ {
+       struct device *dev = &pdev->dev;
+       int irq;
+       int i, intx_irq, msi_irq, event_irq;
+       int ret;
+-      ret = mc_pcie_init_irq_domains(port);
++      ret = plda_pcie_init_irq_domains(port);
+       if (ret) {
+               dev_err(dev, "failed creating IRQ domains\n");
+               return ret;
+@@ -938,7 +943,7 @@ static int mc_init_interrupts(struct pla
+       }
+       /* Plug the INTx chained handler */
+-      irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port);
++      irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
+       msi_irq = irq_create_mapping(port->event_domain,
+                                    EVENT_LOCAL_PM_MSI_INT_MSI);
+@@ -946,10 +951,10 @@ static int mc_init_interrupts(struct pla
+               return -ENXIO;
+       /* Plug the MSI chained handler */
+-      irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port);
++      irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
+       /* Plug the main event chained handler */
+-      irq_set_chained_handler_and_data(irq, mc_handle_event, port);
++      irq_set_chained_handler_and_data(irq, plda_handle_event, port);
+       return 0;
+ }
+@@ -977,7 +982,7 @@ static int mc_platform_init(struct pci_c
+               return ret;
+       /* Address translation is up; safe to enable interrupts */
+-      ret = mc_init_interrupts(pdev, &port->plda);
++      ret = plda_init_interrupts(pdev, &port->plda);
+       if (ret)
+               return ret;
diff --git a/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch b/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch
new file mode 100644 (file)
index 0000000..4071feb
--- /dev/null
@@ -0,0 +1,65 @@
+From ab04dadb45a4150c9fd55b97fdd7397f4739a629 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:01 +0800
+Subject: [PATCH 024/116] PCI: microchip: Add num_events field to struct
+ plda_pcie_rp
+
+The number of events is different across platforms. In order to share
+interrupt processing code, add a variable that defines the number of
+events so that it can be set per-platform instead of hardcoding it.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 8 +++++---
+ drivers/pci/controller/plda/pcie-plda.h           | 1 +
+ 2 files changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -653,7 +653,7 @@ static void plda_handle_event(struct irq
+       events = mc_get_events(port);
+-      for_each_set_bit(bit, &events, NUM_EVENTS)
++      for_each_set_bit(bit, &events, port->num_events)
+               generic_handle_domain_irq(port->event_domain, bit);
+       chained_irq_exit(chip, desc);
+@@ -816,7 +816,8 @@ static int plda_pcie_init_irq_domains(st
+               return -EINVAL;
+       }
+-      port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS,
++      port->event_domain = irq_domain_add_linear(pcie_intc_node,
++                                                 port->num_events,
+                                                  &plda_event_domain_ops,
+                                                  port);
+       if (!port->event_domain) {
+@@ -920,7 +921,7 @@ static int plda_init_interrupts(struct p
+       if (irq < 0)
+               return -ENODEV;
+-      for (i = 0; i < NUM_EVENTS; i++) {
++      for (i = 0; i < port->num_events; i++) {
+               event_irq = irq_create_mapping(port->event_domain, i);
+               if (!event_irq) {
+                       dev_err(dev, "failed to map hwirq %d\n", i);
+@@ -1012,6 +1013,7 @@ static int mc_host_probe(struct platform
+       bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+       plda->bridge_addr = bridge_base_addr;
++      plda->num_events = NUM_EVENTS;
+       /* Allow enabling MSI by disabling MSI-X */
+       val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -124,6 +124,7 @@ struct plda_pcie_rp {
+       raw_spinlock_t lock;
+       struct plda_msi msi;
+       void __iomem *bridge_addr;
++      int num_events;
+ };
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
diff --git a/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch b/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch
new file mode 100644 (file)
index 0000000..2b299e0
--- /dev/null
@@ -0,0 +1,114 @@
+From 9f202f211cc79eefecbb03715c884e54eb95a62c Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:02 +0800
+Subject: [PATCH 025/116] PCI: microchip: Add request_event_irq() callback
+ function
+
+As PLDA dts binding doc(Documentation/devicetree/bindings/pci/
+plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt
+controller. Microchip Polarfire PCIe add some PCIe interrupts base on
+PLDA IP interrupt controller.
+
+Microchip Polarfire PCIe additional intrerrupts:
+EVENT_PCIE_L2_EXIT
+EVENT_PCIE_HOTRST_EXIT
+EVENT_PCIE_DLUP_EXIT
+EVENT_SEC_TX_RAM_SEC_ERR
+EVENT_SEC_RX_RAM_SEC_ERR
+....
+
+Both codes of register interrupts and mc_event_handler() contain
+additional interrupts symbol names, these can not be re-used. So add a
+new plda_event_handler() functions, which implements PLDA interrupt
+defalt handler. Add request_event_irq() callback function to
+compat Microchip Polorfire PCIe additional interrupts.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 31 ++++++++++++++++---
+ drivers/pci/controller/plda/pcie-plda.h       |  5 +++
+ 2 files changed, 32 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -642,6 +642,11 @@ static irqreturn_t mc_event_handler(int
+       return IRQ_HANDLED;
+ }
++static irqreturn_t plda_event_handler(int irq, void *dev_id)
++{
++      return IRQ_HANDLED;
++}
++
+ static void plda_handle_event(struct irq_desc *desc)
+ {
+       struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+@@ -803,6 +808,17 @@ static int mc_pcie_init_clks(struct devi
+       return 0;
+ }
++static int mc_request_event_irq(struct plda_pcie_rp *plda, int event_irq,
++                              int event)
++{
++      return devm_request_irq(plda->dev, event_irq, mc_event_handler,
++                              0, event_cause[event].sym, plda);
++}
++
++static const struct plda_event mc_event = {
++      .request_event_irq = mc_request_event_irq,
++};
++
+ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+ {
+       struct device *dev = port->dev;
+@@ -904,7 +920,9 @@ static void mc_disable_interrupts(struct
+       writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+-static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port)
++static int plda_init_interrupts(struct platform_device *pdev,
++                              struct plda_pcie_rp *port,
++                              const struct plda_event *event)
+ {
+       struct device *dev = &pdev->dev;
+       int irq;
+@@ -928,8 +946,13 @@ static int plda_init_interrupts(struct p
+                       return -ENXIO;
+               }
+-              ret = devm_request_irq(dev, event_irq, mc_event_handler,
+-                                     0, event_cause[i].sym, port);
++              if (event->request_event_irq)
++                      ret = event->request_event_irq(port, event_irq, i);
++              else
++                      ret = devm_request_irq(dev, event_irq,
++                                             plda_event_handler,
++                                             0, NULL, port);
++
+               if (ret) {
+                       dev_err(dev, "failed to request IRQ %d\n", event_irq);
+                       return ret;
+@@ -983,7 +1006,7 @@ static int mc_platform_init(struct pci_c
+               return ret;
+       /* Address translation is up; safe to enable interrupts */
+-      ret = plda_init_interrupts(pdev, &port->plda);
++      ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+       if (ret)
+               return ret;
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -127,6 +127,11 @@ struct plda_pcie_rp {
+       int num_events;
+ };
++struct plda_event {
++      int (*request_event_irq)(struct plda_pcie_rp *pcie,
++                               int event_irq, int event);
++};
++
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+                           phys_addr_t axi_addr, phys_addr_t pci_addr,
+                           size_t size);
diff --git a/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch b/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch
new file mode 100644 (file)
index 0000000..85b8143
--- /dev/null
@@ -0,0 +1,56 @@
+From 3cdc20d9cc029ba9444be111bf4e55fd5331ccbe Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:03 +0800
+Subject: [PATCH 026/116] PCI: microchip: Add INTx and MSI event num to struct
+ plda_event
+
+The INTx and MSI interrupt event num is different in Microchip and
+StarFive platform.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 6 ++++--
+ drivers/pci/controller/plda/pcie-plda.h           | 2 ++
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -817,6 +817,8 @@ static int mc_request_event_irq(struct p
+ static const struct plda_event mc_event = {
+       .request_event_irq = mc_request_event_irq,
++      .intx_event        = EVENT_LOCAL_PM_MSI_INT_INTX,
++      .msi_event         = EVENT_LOCAL_PM_MSI_INT_MSI,
+ };
+ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+@@ -960,7 +962,7 @@ static int plda_init_interrupts(struct p
+       }
+       intx_irq = irq_create_mapping(port->event_domain,
+-                                    EVENT_LOCAL_PM_MSI_INT_INTX);
++                                    event->intx_event);
+       if (!intx_irq) {
+               dev_err(dev, "failed to map INTx interrupt\n");
+               return -ENXIO;
+@@ -970,7 +972,7 @@ static int plda_init_interrupts(struct p
+       irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
+       msi_irq = irq_create_mapping(port->event_domain,
+-                                   EVENT_LOCAL_PM_MSI_INT_MSI);
++                                   event->msi_event);
+       if (!msi_irq)
+               return -ENXIO;
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -130,6 +130,8 @@ struct plda_pcie_rp {
+ struct plda_event {
+       int (*request_event_irq)(struct plda_pcie_rp *pcie,
+                                int event_irq, int event);
++      int intx_event;
++      int msi_event;
+ };
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
diff --git a/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch b/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch
new file mode 100644 (file)
index 0000000..a0cb478
--- /dev/null
@@ -0,0 +1,167 @@
+From b4a38ef87661f21fe2fb3e085ae98f25f78aaf99 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:04 +0800
+Subject: [PATCH 027/116] PCI: microchip: Add get_events() callback and add
+ PLDA get_event()
+
+As PLDA dts binding doc(Documentation/devicetree/bindings/pci/
+plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt
+controller.
+
+PolarFire implements its own PCIe interrupts, additional to the regular
+PCIe interrupts, due to lack of an MSI controller, so the interrupt to
+event number mapping is different to the PLDA regular interrupts,
+necessitating a custom get_events() implementation.
+
+Microchip Polarfire PCIe additional intrerrupts:
+EVENT_PCIE_L2_EXIT
+EVENT_PCIE_HOTRST_EXIT
+EVENT_PCIE_DLUP_EXIT
+EVENT_SEC_TX_RAM_SEC_ERR
+EVENT_SEC_RX_RAM_SEC_ERR
+....
+
+plda_get_events() adds interrupt register to PLDA local event num mapping
+codes. All The PLDA interrupts can be seen in new added graph.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 35 ++++++++++++++++++-
+ drivers/pci/controller/plda/pcie-plda.h       | 32 +++++++++++++++++
+ 2 files changed, 66 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -626,6 +626,26 @@ static u32 mc_get_events(struct plda_pci
+       return events;
+ }
++static u32 plda_get_events(struct plda_pcie_rp *port)
++{
++      u32 events, val, origin;
++
++      origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);
++
++      /* MSI event and sys events */
++      val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
++      events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);
++
++      /* INTx events */
++      if (origin & PM_MSI_INT_INTX_MASK)
++              events |= BIT(PM_MSI_INT_INTX_SHIFT);
++
++      /* remains are same with register */
++      events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);
++
++      return events;
++}
++
+ static irqreturn_t mc_event_handler(int irq, void *dev_id)
+ {
+       struct plda_pcie_rp *port = dev_id;
+@@ -656,7 +676,7 @@ static void plda_handle_event(struct irq
+       chained_irq_enter(chip, desc);
+-      events = mc_get_events(port);
++      events = port->event_ops->get_events(port);
+       for_each_set_bit(bit, &events, port->num_events)
+               generic_handle_domain_irq(port->event_domain, bit);
+@@ -750,6 +770,10 @@ static struct irq_chip mc_event_irq_chip
+       .irq_unmask = mc_unmask_event_irq,
+ };
++static const struct plda_event_ops plda_event_ops = {
++      .get_events = plda_get_events,
++};
++
+ static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+                              irq_hw_number_t hwirq)
+ {
+@@ -815,6 +839,10 @@ static int mc_request_event_irq(struct p
+                               0, event_cause[event].sym, plda);
+ }
++static const struct plda_event_ops mc_event_ops = {
++      .get_events = mc_get_events,
++};
++
+ static const struct plda_event mc_event = {
+       .request_event_irq = mc_request_event_irq,
+       .intx_event        = EVENT_LOCAL_PM_MSI_INT_INTX,
+@@ -931,6 +959,9 @@ static int plda_init_interrupts(struct p
+       int i, intx_irq, msi_irq, event_irq;
+       int ret;
++      if (!port->event_ops)
++              port->event_ops = &plda_event_ops;
++
+       ret = plda_pcie_init_irq_domains(port);
+       if (ret) {
+               dev_err(dev, "failed creating IRQ domains\n");
+@@ -1007,6 +1038,8 @@ static int mc_platform_init(struct pci_c
+       if (ret)
+               return ret;
++      port->plda.event_ops = &mc_event_ops;
++
+       /* Address translation is up; safe to enable interrupts */
+       ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+       if (ret)
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -58,6 +58,7 @@
+ #define  PM_MSI_INT_EVENTS_SHIFT              30
+ #define  PM_MSI_INT_SYS_ERR_MASK              0x80000000u
+ #define  PM_MSI_INT_SYS_ERR_SHIFT             31
++#define  SYS_AND_MSI_MASK                     GENMASK(31, 28)
+ #define  NUM_LOCAL_EVENTS                     15
+ #define ISTATUS_LOCAL                         0x184
+ #define IMASK_HOST                            0x188
+@@ -108,6 +109,36 @@ enum plda_int_event {
+ #define PLDA_MAX_INT_NUM                      (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
++/*
++ * PLDA interrupt register
++ *
++ * 31         27     23              15           7          0
++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+
++ * |12|11|10|9| intx |7|6|5|4|3|2|1|0| DMA error | DMA end   |
++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+
++ * bit 0-7  DMA interrupt end : reserved for vendor implement
++ * bit 8-15 DMA error : reserved for vendor implement
++ * 0:  AXI post error (PLDA_AXI_POST_ERR)
++ * 1:  AXI fetch error (PLDA_AXI_FETCH_ERR)
++ * 2:  AXI discard error (PLDA_AXI_DISCARD_ERR)
++ * 3:  AXI doorbell (PLDA_PCIE_DOORBELL)
++ * 4:  PCIe post error (PLDA_PCIE_POST_ERR)
++ * 5:  PCIe fetch error (PLDA_PCIE_FETCH_ERR)
++ * 6:  PCIe discard error (PLDA_PCIE_DISCARD_ERR)
++ * 7:  PCIe doorbell (PLDA_PCIE_DOORBELL)
++ * 8:  4 INTx interruts (PLDA_INTX)
++ * 9:  MSI interrupt (PLDA_MSI)
++ * 10: AER event (PLDA_AER_EVENT)
++ * 11: PM/LTR/Hotplug (PLDA_MISC_EVENTS)
++ * 12: System error (PLDA_SYS_ERR)
++ */
++
++struct plda_pcie_rp;
++
++struct plda_event_ops {
++      u32 (*get_events)(struct plda_pcie_rp *pcie);
++};
++
+ struct plda_msi {
+       struct mutex lock;              /* Protect used bitmap */
+       struct irq_domain *msi_domain;
+@@ -123,6 +154,7 @@ struct plda_pcie_rp {
+       struct irq_domain *event_domain;
+       raw_spinlock_t lock;
+       struct plda_msi msi;
++      const struct plda_event_ops *event_ops;
+       void __iomem *bridge_addr;
+       int num_events;
+ };
diff --git a/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch b/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch
new file mode 100644 (file)
index 0000000..9ed8119
--- /dev/null
@@ -0,0 +1,144 @@
+From 229ea8e7b674eb5c9bc4f70d43df1bd02a79862a Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:05 +0800
+Subject: [PATCH 028/116] PCI: microchip: Add event irqchip field to host port
+ and add PLDA irqchip
+
+As PLDA dts binding doc(Documentation/devicetree/bindings/pci/
+plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt
+controller.
+
+Microchip PolarFire PCIE event IRQs includes PLDA interrupts and
+Polarfire their own interrupts. The interrupt irqchip ops includes
+ack/mask/unmask interrupt ops, which will write correct registers.
+Microchip Polarfire PCIe additional interrupts require to write Polarfire
+SoC self-defined registers. So Microchip PCIe event irqchip ops can not
+be re-used.
+
+To support PLDA its own event IRQ process, implements PLDA irqchip ops and
+add event irqchip field to struct pcie_plda_rp.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 66 ++++++++++++++++++-
+ drivers/pci/controller/plda/pcie-plda.h       |  5 +-
+ 2 files changed, 69 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -770,6 +770,64 @@ static struct irq_chip mc_event_irq_chip
+       .irq_unmask = mc_unmask_event_irq,
+ };
++static u32 plda_hwirq_to_mask(int hwirq)
++{
++      u32 mask;
++
++      /* hwirq 23 - 0 are the same with register */
++      if (hwirq < EVENT_PM_MSI_INT_INTX)
++              mask = BIT(hwirq);
++      else if (hwirq == EVENT_PM_MSI_INT_INTX)
++              mask = PM_MSI_INT_INTX_MASK;
++      else
++              mask = BIT(hwirq + PCI_NUM_INTX - 1);
++
++      return mask;
++}
++
++static void plda_ack_event_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++
++      writel_relaxed(plda_hwirq_to_mask(data->hwirq),
++                     port->bridge_addr + ISTATUS_LOCAL);
++}
++
++static void plda_mask_event_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      u32 mask, val;
++
++      mask = plda_hwirq_to_mask(data->hwirq);
++
++      raw_spin_lock(&port->lock);
++      val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++      val &= ~mask;
++      writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++      raw_spin_unlock(&port->lock);
++}
++
++static void plda_unmask_event_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      u32 mask, val;
++
++      mask = plda_hwirq_to_mask(data->hwirq);
++
++      raw_spin_lock(&port->lock);
++      val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++      val |= mask;
++      writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++      raw_spin_unlock(&port->lock);
++}
++
++static struct irq_chip plda_event_irq_chip = {
++      .name = "PLDA PCIe EVENT",
++      .irq_ack = plda_ack_event_irq,
++      .irq_mask = plda_mask_event_irq,
++      .irq_unmask = plda_unmask_event_irq,
++};
++
+ static const struct plda_event_ops plda_event_ops = {
+       .get_events = plda_get_events,
+ };
+@@ -777,7 +835,9 @@ static const struct plda_event_ops plda_
+ static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+                              irq_hw_number_t hwirq)
+ {
+-      irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq);
++      struct plda_pcie_rp *port = (void *)domain->host_data;
++
++      irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
+       irq_set_chip_data(irq, domain->host_data);
+       return 0;
+@@ -962,6 +1022,9 @@ static int plda_init_interrupts(struct p
+       if (!port->event_ops)
+               port->event_ops = &plda_event_ops;
++      if (!port->event_irq_chip)
++              port->event_irq_chip = &plda_event_irq_chip;
++
+       ret = plda_pcie_init_irq_domains(port);
+       if (ret) {
+               dev_err(dev, "failed creating IRQ domains\n");
+@@ -1039,6 +1102,7 @@ static int mc_platform_init(struct pci_c
+               return ret;
+       port->plda.event_ops = &mc_event_ops;
++      port->plda.event_irq_chip = &mc_event_irq_chip;
+       /* Address translation is up; safe to enable interrupts */
+       ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -107,7 +107,9 @@ enum plda_int_event {
+ #define PLDA_NUM_DMA_EVENTS                   16
+-#define PLDA_MAX_INT_NUM                      (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
++#define EVENT_PM_MSI_INT_INTX                 (PLDA_NUM_DMA_EVENTS + PLDA_INTX)
++#define EVENT_PM_MSI_INT_MSI                  (PLDA_NUM_DMA_EVENTS + PLDA_MSI)
++#define PLDA_MAX_EVENT_NUM                    (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM)
+ /*
+  * PLDA interrupt register
+@@ -155,6 +157,7 @@ struct plda_pcie_rp {
+       raw_spinlock_t lock;
+       struct plda_msi msi;
+       const struct plda_event_ops *event_ops;
++      const struct irq_chip *event_irq_chip;
+       void __iomem *bridge_addr;
+       int num_events;
+ };
diff --git a/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch b/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch
new file mode 100644 (file)
index 0000000..840e334
--- /dev/null
@@ -0,0 +1,1028 @@
+From 6be452d8e61594790ae57b282a612ec0df473e61 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:06 +0800
+Subject: [PATCH 029/116] PCI: microchip: Move IRQ functions to
+ pcie-plda-host.c
+
+Move IRQ related functions to pcie-plda-host.c for re-use these codes.
+Now Refactoring codes complete.
+
+Including MSI, INTx, event interrupts and IRQ init functions.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../pci/controller/plda/pcie-microchip-host.c | 467 -----------------
+ drivers/pci/controller/plda/pcie-plda-host.c  | 472 ++++++++++++++++++
+ drivers/pci/controller/plda/pcie-plda.h       |   3 +
+ 3 files changed, 475 insertions(+), 467 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -318,244 +318,6 @@ static void mc_pcie_enable_msi(struct mc
+                      ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI);
+ }
+-static void plda_handle_msi(struct irq_desc *desc)
+-{
+-      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+-      struct irq_chip *chip = irq_desc_get_chip(desc);
+-      struct device *dev = port->dev;
+-      struct plda_msi *msi = &port->msi;
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      unsigned long status;
+-      u32 bit;
+-      int ret;
+-
+-      chained_irq_enter(chip, desc);
+-
+-      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+-      if (status & PM_MSI_INT_MSI_MASK) {
+-              writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
+-              status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
+-              for_each_set_bit(bit, &status, msi->num_vectors) {
+-                      ret = generic_handle_domain_irq(msi->dev_domain, bit);
+-                      if (ret)
+-                              dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
+-                                                  bit);
+-              }
+-      }
+-
+-      chained_irq_exit(chip, desc);
+-}
+-
+-static void plda_msi_bottom_irq_ack(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      u32 bitpos = data->hwirq;
+-
+-      writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
+-}
+-
+-static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      phys_addr_t addr = port->msi.vector_phy;
+-
+-      msg->address_lo = lower_32_bits(addr);
+-      msg->address_hi = upper_32_bits(addr);
+-      msg->data = data->hwirq;
+-
+-      dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
+-              (int)data->hwirq, msg->address_hi, msg->address_lo);
+-}
+-
+-static int plda_msi_set_affinity(struct irq_data *irq_data,
+-                               const struct cpumask *mask, bool force)
+-{
+-      return -EINVAL;
+-}
+-
+-static struct irq_chip plda_msi_bottom_irq_chip = {
+-      .name = "PLDA MSI",
+-      .irq_ack = plda_msi_bottom_irq_ack,
+-      .irq_compose_msi_msg = plda_compose_msi_msg,
+-      .irq_set_affinity = plda_msi_set_affinity,
+-};
+-
+-static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
+-                                   unsigned int virq,
+-                                   unsigned int nr_irqs,
+-                                   void *args)
+-{
+-      struct plda_pcie_rp *port = domain->host_data;
+-      struct plda_msi *msi = &port->msi;
+-      unsigned long bit;
+-
+-      mutex_lock(&msi->lock);
+-      bit = find_first_zero_bit(msi->used, msi->num_vectors);
+-      if (bit >= msi->num_vectors) {
+-              mutex_unlock(&msi->lock);
+-              return -ENOSPC;
+-      }
+-
+-      set_bit(bit, msi->used);
+-
+-      irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
+-                          domain->host_data, handle_edge_irq, NULL, NULL);
+-
+-      mutex_unlock(&msi->lock);
+-
+-      return 0;
+-}
+-
+-static void plda_irq_msi_domain_free(struct irq_domain *domain,
+-                                   unsigned int virq,
+-                                   unsigned int nr_irqs)
+-{
+-      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
+-      struct plda_msi *msi = &port->msi;
+-
+-      mutex_lock(&msi->lock);
+-
+-      if (test_bit(d->hwirq, msi->used))
+-              __clear_bit(d->hwirq, msi->used);
+-      else
+-              dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
+-
+-      mutex_unlock(&msi->lock);
+-}
+-
+-static const struct irq_domain_ops msi_domain_ops = {
+-      .alloc  = plda_irq_msi_domain_alloc,
+-      .free   = plda_irq_msi_domain_free,
+-};
+-
+-static struct irq_chip plda_msi_irq_chip = {
+-      .name = "PLDA PCIe MSI",
+-      .irq_ack = irq_chip_ack_parent,
+-      .irq_mask = pci_msi_mask_irq,
+-      .irq_unmask = pci_msi_unmask_irq,
+-};
+-
+-static struct msi_domain_info plda_msi_domain_info = {
+-      .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+-                MSI_FLAG_PCI_MSIX),
+-      .chip = &plda_msi_irq_chip,
+-};
+-
+-static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
+-{
+-      struct device *dev = port->dev;
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+-      struct plda_msi *msi = &port->msi;
+-
+-      mutex_init(&port->msi.lock);
+-
+-      msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
+-                                              &msi_domain_ops, port);
+-      if (!msi->dev_domain) {
+-              dev_err(dev, "failed to create IRQ domain\n");
+-              return -ENOMEM;
+-      }
+-
+-      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
+-                                                  &plda_msi_domain_info,
+-                                                  msi->dev_domain);
+-      if (!msi->msi_domain) {
+-              dev_err(dev, "failed to create MSI domain\n");
+-              irq_domain_remove(msi->dev_domain);
+-              return -ENOMEM;
+-      }
+-
+-      return 0;
+-}
+-
+-static void plda_handle_intx(struct irq_desc *desc)
+-{
+-      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+-      struct irq_chip *chip = irq_desc_get_chip(desc);
+-      struct device *dev = port->dev;
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      unsigned long status;
+-      u32 bit;
+-      int ret;
+-
+-      chained_irq_enter(chip, desc);
+-
+-      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+-      if (status & PM_MSI_INT_INTX_MASK) {
+-              status &= PM_MSI_INT_INTX_MASK;
+-              status >>= PM_MSI_INT_INTX_SHIFT;
+-              for_each_set_bit(bit, &status, PCI_NUM_INTX) {
+-                      ret = generic_handle_domain_irq(port->intx_domain, bit);
+-                      if (ret)
+-                              dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
+-                                                  bit);
+-              }
+-      }
+-
+-      chained_irq_exit(chip, desc);
+-}
+-
+-static void plda_ack_intx_irq(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-
+-      writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
+-}
+-
+-static void plda_mask_intx_irq(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      unsigned long flags;
+-      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-      u32 val;
+-
+-      raw_spin_lock_irqsave(&port->lock, flags);
+-      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+-      val &= ~mask;
+-      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+-      raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static void plda_unmask_intx_irq(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      void __iomem *bridge_base_addr = port->bridge_addr;
+-      unsigned long flags;
+-      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
+-      u32 val;
+-
+-      raw_spin_lock_irqsave(&port->lock, flags);
+-      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
+-      val |= mask;
+-      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
+-      raw_spin_unlock_irqrestore(&port->lock, flags);
+-}
+-
+-static struct irq_chip plda_intx_irq_chip = {
+-      .name = "PLDA PCIe INTx",
+-      .irq_ack = plda_ack_intx_irq,
+-      .irq_mask = plda_mask_intx_irq,
+-      .irq_unmask = plda_unmask_intx_irq,
+-};
+-
+-static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+-                            irq_hw_number_t hwirq)
+-{
+-      irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
+-      irq_set_chip_data(irq, domain->host_data);
+-
+-      return 0;
+-}
+-
+-static const struct irq_domain_ops intx_domain_ops = {
+-      .map = plda_pcie_intx_map,
+-};
+-
+ static inline u32 reg_to_event(u32 reg, struct event_map field)
+ {
+       return (reg & field.reg_mask) ? BIT(field.event_bit) : 0;
+@@ -626,26 +388,6 @@ static u32 mc_get_events(struct plda_pci
+       return events;
+ }
+-static u32 plda_get_events(struct plda_pcie_rp *port)
+-{
+-      u32 events, val, origin;
+-
+-      origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);
+-
+-      /* MSI event and sys events */
+-      val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
+-      events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);
+-
+-      /* INTx events */
+-      if (origin & PM_MSI_INT_INTX_MASK)
+-              events |= BIT(PM_MSI_INT_INTX_SHIFT);
+-
+-      /* remains are same with register */
+-      events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);
+-
+-      return events;
+-}
+-
+ static irqreturn_t mc_event_handler(int irq, void *dev_id)
+ {
+       struct plda_pcie_rp *port = dev_id;
+@@ -662,28 +404,6 @@ static irqreturn_t mc_event_handler(int
+       return IRQ_HANDLED;
+ }
+-static irqreturn_t plda_event_handler(int irq, void *dev_id)
+-{
+-      return IRQ_HANDLED;
+-}
+-
+-static void plda_handle_event(struct irq_desc *desc)
+-{
+-      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+-      unsigned long events;
+-      u32 bit;
+-      struct irq_chip *chip = irq_desc_get_chip(desc);
+-
+-      chained_irq_enter(chip, desc);
+-
+-      events = port->event_ops->get_events(port);
+-
+-      for_each_set_bit(bit, &events, port->num_events)
+-              generic_handle_domain_irq(port->event_domain, bit);
+-
+-      chained_irq_exit(chip, desc);
+-}
+-
+ static void mc_ack_event_irq(struct irq_data *data)
+ {
+       struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+@@ -770,83 +490,6 @@ static struct irq_chip mc_event_irq_chip
+       .irq_unmask = mc_unmask_event_irq,
+ };
+-static u32 plda_hwirq_to_mask(int hwirq)
+-{
+-      u32 mask;
+-
+-      /* hwirq 23 - 0 are the same with register */
+-      if (hwirq < EVENT_PM_MSI_INT_INTX)
+-              mask = BIT(hwirq);
+-      else if (hwirq == EVENT_PM_MSI_INT_INTX)
+-              mask = PM_MSI_INT_INTX_MASK;
+-      else
+-              mask = BIT(hwirq + PCI_NUM_INTX - 1);
+-
+-      return mask;
+-}
+-
+-static void plda_ack_event_irq(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-
+-      writel_relaxed(plda_hwirq_to_mask(data->hwirq),
+-                     port->bridge_addr + ISTATUS_LOCAL);
+-}
+-
+-static void plda_mask_event_irq(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      u32 mask, val;
+-
+-      mask = plda_hwirq_to_mask(data->hwirq);
+-
+-      raw_spin_lock(&port->lock);
+-      val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
+-      val &= ~mask;
+-      writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
+-      raw_spin_unlock(&port->lock);
+-}
+-
+-static void plda_unmask_event_irq(struct irq_data *data)
+-{
+-      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
+-      u32 mask, val;
+-
+-      mask = plda_hwirq_to_mask(data->hwirq);
+-
+-      raw_spin_lock(&port->lock);
+-      val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
+-      val |= mask;
+-      writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
+-      raw_spin_unlock(&port->lock);
+-}
+-
+-static struct irq_chip plda_event_irq_chip = {
+-      .name = "PLDA PCIe EVENT",
+-      .irq_ack = plda_ack_event_irq,
+-      .irq_mask = plda_mask_event_irq,
+-      .irq_unmask = plda_unmask_event_irq,
+-};
+-
+-static const struct plda_event_ops plda_event_ops = {
+-      .get_events = plda_get_events,
+-};
+-
+-static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
+-                             irq_hw_number_t hwirq)
+-{
+-      struct plda_pcie_rp *port = (void *)domain->host_data;
+-
+-      irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
+-      irq_set_chip_data(irq, domain->host_data);
+-
+-      return 0;
+-}
+-
+-static const struct irq_domain_ops plda_event_domain_ops = {
+-      .map = plda_pcie_event_map,
+-};
+-
+ static inline void mc_pcie_deinit_clk(void *data)
+ {
+       struct clk *clk = data;
+@@ -909,47 +552,6 @@ static const struct plda_event mc_event
+       .msi_event         = EVENT_LOCAL_PM_MSI_INT_MSI,
+ };
+-static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
+-{
+-      struct device *dev = port->dev;
+-      struct device_node *node = dev->of_node;
+-      struct device_node *pcie_intc_node;
+-
+-      /* Setup INTx */
+-      pcie_intc_node = of_get_next_child(node, NULL);
+-      if (!pcie_intc_node) {
+-              dev_err(dev, "failed to find PCIe Intc node\n");
+-              return -EINVAL;
+-      }
+-
+-      port->event_domain = irq_domain_add_linear(pcie_intc_node,
+-                                                 port->num_events,
+-                                                 &plda_event_domain_ops,
+-                                                 port);
+-      if (!port->event_domain) {
+-              dev_err(dev, "failed to get event domain\n");
+-              of_node_put(pcie_intc_node);
+-              return -ENOMEM;
+-      }
+-
+-      irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
+-
+-      port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+-                                                &intx_domain_ops, port);
+-      if (!port->intx_domain) {
+-              dev_err(dev, "failed to get an INTx IRQ domain\n");
+-              of_node_put(pcie_intc_node);
+-              return -ENOMEM;
+-      }
+-
+-      irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
+-
+-      of_node_put(pcie_intc_node);
+-      raw_spin_lock_init(&port->lock);
+-
+-      return plda_allocate_msi_domains(port);
+-}
+-
+ static inline void mc_clear_secs(struct mc_pcie *port)
+ {
+       void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
+@@ -1010,75 +612,6 @@ static void mc_disable_interrupts(struct
+       writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ }
+-static int plda_init_interrupts(struct platform_device *pdev,
+-                              struct plda_pcie_rp *port,
+-                              const struct plda_event *event)
+-{
+-      struct device *dev = &pdev->dev;
+-      int irq;
+-      int i, intx_irq, msi_irq, event_irq;
+-      int ret;
+-
+-      if (!port->event_ops)
+-              port->event_ops = &plda_event_ops;
+-
+-      if (!port->event_irq_chip)
+-              port->event_irq_chip = &plda_event_irq_chip;
+-
+-      ret = plda_pcie_init_irq_domains(port);
+-      if (ret) {
+-              dev_err(dev, "failed creating IRQ domains\n");
+-              return ret;
+-      }
+-
+-      irq = platform_get_irq(pdev, 0);
+-      if (irq < 0)
+-              return -ENODEV;
+-
+-      for (i = 0; i < port->num_events; i++) {
+-              event_irq = irq_create_mapping(port->event_domain, i);
+-              if (!event_irq) {
+-                      dev_err(dev, "failed to map hwirq %d\n", i);
+-                      return -ENXIO;
+-              }
+-
+-              if (event->request_event_irq)
+-                      ret = event->request_event_irq(port, event_irq, i);
+-              else
+-                      ret = devm_request_irq(dev, event_irq,
+-                                             plda_event_handler,
+-                                             0, NULL, port);
+-
+-              if (ret) {
+-                      dev_err(dev, "failed to request IRQ %d\n", event_irq);
+-                      return ret;
+-              }
+-      }
+-
+-      intx_irq = irq_create_mapping(port->event_domain,
+-                                    event->intx_event);
+-      if (!intx_irq) {
+-              dev_err(dev, "failed to map INTx interrupt\n");
+-              return -ENXIO;
+-      }
+-
+-      /* Plug the INTx chained handler */
+-      irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
+-
+-      msi_irq = irq_create_mapping(port->event_domain,
+-                                   event->msi_event);
+-      if (!msi_irq)
+-              return -ENXIO;
+-
+-      /* Plug the MSI chained handler */
+-      irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
+-
+-      /* Plug the main event chained handler */
+-      irq_set_chained_handler_and_data(irq, plda_handle_event, port);
+-
+-      return 0;
+-}
+-
+ static int mc_platform_init(struct pci_config_window *cfg)
+ {
+       struct device *dev = cfg->parent;
+--- a/drivers/pci/controller/plda/pcie-plda-host.c
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -7,11 +7,483 @@
+  * Author: Daire McNamara <daire.mcnamara@microchip.com>
+  */
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/msi.h>
+ #include <linux/pci_regs.h>
+ #include <linux/pci-ecam.h>
+ #include "pcie-plda.h"
++static void plda_handle_msi(struct irq_desc *desc)
++{
++      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++      struct device *dev = port->dev;
++      struct plda_msi *msi = &port->msi;
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      unsigned long status;
++      u32 bit;
++      int ret;
++
++      chained_irq_enter(chip, desc);
++
++      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++      if (status & PM_MSI_INT_MSI_MASK) {
++              writel_relaxed(status & PM_MSI_INT_MSI_MASK,
++                             bridge_base_addr + ISTATUS_LOCAL);
++              status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
++              for_each_set_bit(bit, &status, msi->num_vectors) {
++                      ret = generic_handle_domain_irq(msi->dev_domain, bit);
++                      if (ret)
++                              dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
++                                                  bit);
++              }
++      }
++
++      chained_irq_exit(chip, desc);
++}
++
++static void plda_msi_bottom_irq_ack(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      u32 bitpos = data->hwirq;
++
++      writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
++}
++
++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      phys_addr_t addr = port->msi.vector_phy;
++
++      msg->address_lo = lower_32_bits(addr);
++      msg->address_hi = upper_32_bits(addr);
++      msg->data = data->hwirq;
++
++      dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n",
++              (int)data->hwirq, msg->address_hi, msg->address_lo);
++}
++
++static int plda_msi_set_affinity(struct irq_data *irq_data,
++                               const struct cpumask *mask, bool force)
++{
++      return -EINVAL;
++}
++
++static struct irq_chip plda_msi_bottom_irq_chip = {
++      .name = "PLDA MSI",
++      .irq_ack = plda_msi_bottom_irq_ack,
++      .irq_compose_msi_msg = plda_compose_msi_msg,
++      .irq_set_affinity = plda_msi_set_affinity,
++};
++
++static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
++                                   unsigned int virq,
++                                   unsigned int nr_irqs,
++                                   void *args)
++{
++      struct plda_pcie_rp *port = domain->host_data;
++      struct plda_msi *msi = &port->msi;
++      unsigned long bit;
++
++      mutex_lock(&msi->lock);
++      bit = find_first_zero_bit(msi->used, msi->num_vectors);
++      if (bit >= msi->num_vectors) {
++              mutex_unlock(&msi->lock);
++              return -ENOSPC;
++      }
++
++      set_bit(bit, msi->used);
++
++      irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
++                          domain->host_data, handle_edge_irq, NULL, NULL);
++
++      mutex_unlock(&msi->lock);
++
++      return 0;
++}
++
++static void plda_irq_msi_domain_free(struct irq_domain *domain,
++                                   unsigned int virq,
++                                   unsigned int nr_irqs)
++{
++      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
++      struct plda_msi *msi = &port->msi;
++
++      mutex_lock(&msi->lock);
++
++      if (test_bit(d->hwirq, msi->used))
++              __clear_bit(d->hwirq, msi->used);
++      else
++              dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);
++
++      mutex_unlock(&msi->lock);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++      .alloc  = plda_irq_msi_domain_alloc,
++      .free   = plda_irq_msi_domain_free,
++};
++
++static struct irq_chip plda_msi_irq_chip = {
++      .name = "PLDA PCIe MSI",
++      .irq_ack = irq_chip_ack_parent,
++      .irq_mask = pci_msi_mask_irq,
++      .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info plda_msi_domain_info = {
++      .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++                MSI_FLAG_PCI_MSIX),
++      .chip = &plda_msi_irq_chip,
++};
++
++static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
++{
++      struct device *dev = port->dev;
++      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++      struct plda_msi *msi = &port->msi;
++
++      mutex_init(&port->msi.lock);
++
++      msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors,
++                                              &msi_domain_ops, port);
++      if (!msi->dev_domain) {
++              dev_err(dev, "failed to create IRQ domain\n");
++              return -ENOMEM;
++      }
++
++      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++                                                  &plda_msi_domain_info,
++                                                  msi->dev_domain);
++      if (!msi->msi_domain) {
++              dev_err(dev, "failed to create MSI domain\n");
++              irq_domain_remove(msi->dev_domain);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void plda_handle_intx(struct irq_desc *desc)
++{
++      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++      struct device *dev = port->dev;
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      unsigned long status;
++      u32 bit;
++      int ret;
++
++      chained_irq_enter(chip, desc);
++
++      status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
++      if (status & PM_MSI_INT_INTX_MASK) {
++              status &= PM_MSI_INT_INTX_MASK;
++              status >>= PM_MSI_INT_INTX_SHIFT;
++              for_each_set_bit(bit, &status, PCI_NUM_INTX) {
++                      ret = generic_handle_domain_irq(port->intx_domain, bit);
++                      if (ret)
++                              dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
++                                                  bit);
++              }
++      }
++
++      chained_irq_exit(chip, desc);
++}
++
++static void plda_ack_intx_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++
++      writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
++}
++
++static void plda_mask_intx_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      unsigned long flags;
++      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++      u32 val;
++
++      raw_spin_lock_irqsave(&port->lock, flags);
++      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++      val &= ~mask;
++      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++      raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void plda_unmask_intx_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      void __iomem *bridge_base_addr = port->bridge_addr;
++      unsigned long flags;
++      u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
++      u32 val;
++
++      raw_spin_lock_irqsave(&port->lock, flags);
++      val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
++      val |= mask;
++      writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
++      raw_spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static struct irq_chip plda_intx_irq_chip = {
++      .name = "PLDA PCIe INTx",
++      .irq_ack = plda_ack_intx_irq,
++      .irq_mask = plda_mask_intx_irq,
++      .irq_unmask = plda_unmask_intx_irq,
++};
++
++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++                            irq_hw_number_t hwirq)
++{
++      irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
++      irq_set_chip_data(irq, domain->host_data);
++
++      return 0;
++}
++
++static const struct irq_domain_ops intx_domain_ops = {
++      .map = plda_pcie_intx_map,
++};
++
++static u32 plda_get_events(struct plda_pcie_rp *port)
++{
++      u32 events, val, origin;
++
++      origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);
++
++      /* MSI event and sys events */
++      val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
++      events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);
++
++      /* INTx events */
++      if (origin & PM_MSI_INT_INTX_MASK)
++              events |= BIT(PM_MSI_INT_INTX_SHIFT);
++
++      /* remains are same with register */
++      events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);
++
++      return events;
++}
++
++static irqreturn_t plda_event_handler(int irq, void *dev_id)
++{
++      return IRQ_HANDLED;
++}
++
++static void plda_handle_event(struct irq_desc *desc)
++{
++      struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
++      unsigned long events;
++      u32 bit;
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++
++      chained_irq_enter(chip, desc);
++
++      events = port->event_ops->get_events(port);
++
++      for_each_set_bit(bit, &events, port->num_events)
++              generic_handle_domain_irq(port->event_domain, bit);
++
++      chained_irq_exit(chip, desc);
++}
++
++static u32 plda_hwirq_to_mask(int hwirq)
++{
++      u32 mask;
++
++      /* hwirq 23 - 0 are the same with register */
++      if (hwirq < EVENT_PM_MSI_INT_INTX)
++              mask = BIT(hwirq);
++      else if (hwirq == EVENT_PM_MSI_INT_INTX)
++              mask = PM_MSI_INT_INTX_MASK;
++      else
++              mask = BIT(hwirq + PCI_NUM_INTX - 1);
++
++      return mask;
++}
++
++static void plda_ack_event_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++
++      writel_relaxed(plda_hwirq_to_mask(data->hwirq),
++                     port->bridge_addr + ISTATUS_LOCAL);
++}
++
++static void plda_mask_event_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      u32 mask, val;
++
++      mask = plda_hwirq_to_mask(data->hwirq);
++
++      raw_spin_lock(&port->lock);
++      val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++      val &= ~mask;
++      writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++      raw_spin_unlock(&port->lock);
++}
++
++static void plda_unmask_event_irq(struct irq_data *data)
++{
++      struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
++      u32 mask, val;
++
++      mask = plda_hwirq_to_mask(data->hwirq);
++
++      raw_spin_lock(&port->lock);
++      val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
++      val |= mask;
++      writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
++      raw_spin_unlock(&port->lock);
++}
++
++static struct irq_chip plda_event_irq_chip = {
++      .name = "PLDA PCIe EVENT",
++      .irq_ack = plda_ack_event_irq,
++      .irq_mask = plda_mask_event_irq,
++      .irq_unmask = plda_unmask_event_irq,
++};
++
++static const struct plda_event_ops plda_event_ops = {
++      .get_events = plda_get_events,
++};
++
++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
++                             irq_hw_number_t hwirq)
++{
++      struct plda_pcie_rp *port = (void *)domain->host_data;
++
++      irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
++      irq_set_chip_data(irq, domain->host_data);
++
++      return 0;
++}
++
++static const struct irq_domain_ops plda_event_domain_ops = {
++      .map = plda_pcie_event_map,
++};
++
++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
++{
++      struct device *dev = port->dev;
++      struct device_node *node = dev->of_node;
++      struct device_node *pcie_intc_node;
++
++      /* Setup INTx */
++      pcie_intc_node = of_get_next_child(node, NULL);
++      if (!pcie_intc_node) {
++              dev_err(dev, "failed to find PCIe Intc node\n");
++              return -EINVAL;
++      }
++
++      port->event_domain = irq_domain_add_linear(pcie_intc_node,
++                                                 port->num_events,
++                                                 &plda_event_domain_ops,
++                                                 port);
++      if (!port->event_domain) {
++              dev_err(dev, "failed to get event domain\n");
++              of_node_put(pcie_intc_node);
++              return -ENOMEM;
++      }
++
++      irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);
++
++      port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
++                                                &intx_domain_ops, port);
++      if (!port->intx_domain) {
++              dev_err(dev, "failed to get an INTx IRQ domain\n");
++              of_node_put(pcie_intc_node);
++              return -ENOMEM;
++      }
++
++      irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
++
++      of_node_put(pcie_intc_node);
++      raw_spin_lock_init(&port->lock);
++
++      return plda_allocate_msi_domains(port);
++}
++
++int plda_init_interrupts(struct platform_device *pdev,
++                       struct plda_pcie_rp *port,
++                       const struct plda_event *event)
++{
++      struct device *dev = &pdev->dev;
++      int irq;
++      int i, intx_irq, msi_irq, event_irq;
++      int ret;
++
++      if (!port->event_ops)
++              port->event_ops = &plda_event_ops;
++
++      if (!port->event_irq_chip)
++              port->event_irq_chip = &plda_event_irq_chip;
++
++      ret = plda_pcie_init_irq_domains(port);
++      if (ret) {
++              dev_err(dev, "failed creating IRQ domains\n");
++              return ret;
++      }
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0)
++              return -ENODEV;
++
++      for (i = 0; i < port->num_events; i++) {
++              event_irq = irq_create_mapping(port->event_domain, i);
++              if (!event_irq) {
++                      dev_err(dev, "failed to map hwirq %d\n", i);
++                      return -ENXIO;
++              }
++
++              if (event->request_event_irq)
++                      ret = event->request_event_irq(port, event_irq, i);
++              else
++                      ret = devm_request_irq(dev, event_irq,
++                                             plda_event_handler,
++                                             0, NULL, port);
++
++              if (ret) {
++                      dev_err(dev, "failed to request IRQ %d\n", event_irq);
++                      return ret;
++              }
++      }
++
++      intx_irq = irq_create_mapping(port->event_domain,
++                                    event->intx_event);
++      if (!intx_irq) {
++              dev_err(dev, "failed to map INTx interrupt\n");
++              return -ENXIO;
++      }
++
++      /* Plug the INTx chained handler */
++      irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
++
++      msi_irq = irq_create_mapping(port->event_domain,
++                                   event->msi_event);
++      if (!msi_irq)
++              return -ENXIO;
++
++      /* Plug the MSI chained handler */
++      irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
++
++      /* Plug the main event chained handler */
++      irq_set_chained_handler_and_data(irq, plda_handle_event, port);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(plda_init_interrupts);
++
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+                           phys_addr_t axi_addr, phys_addr_t pci_addr,
+                           size_t size)
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -169,6 +169,9 @@ struct plda_event {
+       int msi_event;
+ };
++int plda_init_interrupts(struct platform_device *pdev,
++                       struct plda_pcie_rp *port,
++                       const struct plda_event *event);
+ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
+                           phys_addr_t axi_addr, phys_addr_t pci_addr,
+                           size_t size);
diff --git a/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch b/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch
new file mode 100644 (file)
index 0000000..dcd2310
--- /dev/null
@@ -0,0 +1,67 @@
+From 142fc300fd7511a217783dcfa342031d8ad70188 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:07 +0800
+Subject: [PATCH 030/116] pci: plda: Add event bitmap field to struct
+ plda_pcie_rp
+
+For PLDA DMA interrupts are not all implemented. The non-implemented
+interrupts should be masked. So add a bitmap field to mask the non-
+implemented interrupts.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+---
+ drivers/pci/controller/plda/pcie-microchip-host.c | 1 +
+ drivers/pci/controller/plda/pcie-plda-host.c      | 6 ++++--
+ drivers/pci/controller/plda/pcie-plda.h           | 1 +
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-microchip-host.c
++++ b/drivers/pci/controller/plda/pcie-microchip-host.c
+@@ -636,6 +636,7 @@ static int mc_platform_init(struct pci_c
+       port->plda.event_ops = &mc_event_ops;
+       port->plda.event_irq_chip = &mc_event_irq_chip;
++      port->plda.events_bitmap = GENMASK(NUM_EVENTS - 1, 0);
+       /* Address translation is up; safe to enable interrupts */
+       ret = plda_init_interrupts(pdev, &port->plda, &mc_event);
+--- a/drivers/pci/controller/plda/pcie-plda-host.c
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -290,6 +290,7 @@ static void plda_handle_event(struct irq
+       events = port->event_ops->get_events(port);
++      events &= port->events_bitmap;
+       for_each_set_bit(bit, &events, port->num_events)
+               generic_handle_domain_irq(port->event_domain, bit);
+@@ -420,8 +421,9 @@ int plda_init_interrupts(struct platform
+ {
+       struct device *dev = &pdev->dev;
+       int irq;
+-      int i, intx_irq, msi_irq, event_irq;
++      int intx_irq, msi_irq, event_irq;
+       int ret;
++      u32 i;
+       if (!port->event_ops)
+               port->event_ops = &plda_event_ops;
+@@ -439,7 +441,7 @@ int plda_init_interrupts(struct platform
+       if (irq < 0)
+               return -ENODEV;
+-      for (i = 0; i < port->num_events; i++) {
++      for_each_set_bit(i, &port->events_bitmap, port->num_events) {
+               event_irq = irq_create_mapping(port->event_domain, i);
+               if (!event_irq) {
+                       dev_err(dev, "failed to map hwirq %d\n", i);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -159,6 +159,7 @@ struct plda_pcie_rp {
+       const struct plda_event_ops *event_ops;
+       const struct irq_chip *event_irq_chip;
+       void __iomem *bridge_addr;
++      unsigned long events_bitmap;
+       int num_events;
+ };
diff --git a/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch b/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch
new file mode 100644 (file)
index 0000000..eb22849
--- /dev/null
@@ -0,0 +1,256 @@
+From 3b9991438094dc472dacb4555603bdc379653411 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:08 +0800
+Subject: [PATCH 031/116] PCI: plda: Add host init/deinit and map bus functions
+
+Add PLDA host plda_pcie_host_init()/plda_pcie_host_deinit() and map bus
+function. So vendor can use it to init PLDA PCIe host core.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Mason Huo <mason.huo@starfivetech.com>
+---
+ drivers/pci/controller/plda/pcie-plda-host.c | 131 +++++++++++++++++--
+ drivers/pci/controller/plda/pcie-plda.h      |  22 ++++
+ 2 files changed, 139 insertions(+), 14 deletions(-)
+
+--- a/drivers/pci/controller/plda/pcie-plda-host.c
++++ b/drivers/pci/controller/plda/pcie-plda-host.c
+@@ -3,6 +3,7 @@
+  * PLDA PCIe XpressRich host controller driver
+  *
+  * Copyright (C) 2023 Microchip Co. Ltd
++ *                  StarFive Co. Ltd
+  *
+  * Author: Daire McNamara <daire.mcnamara@microchip.com>
+  */
+@@ -15,6 +16,15 @@
+ #include "pcie-plda.h"
++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
++                              int where)
++{
++      struct plda_pcie_rp *pcie = bus->sysdata;
++
++      return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
++}
++EXPORT_SYMBOL_GPL(plda_pcie_map_bus);
++
+ static void plda_handle_msi(struct irq_desc *desc)
+ {
+       struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
+@@ -420,9 +430,7 @@ int plda_init_interrupts(struct platform
+                        const struct plda_event *event)
+ {
+       struct device *dev = &pdev->dev;
+-      int irq;
+-      int intx_irq, msi_irq, event_irq;
+-      int ret;
++      int event_irq, ret;
+       u32 i;
+       if (!port->event_ops)
+@@ -437,8 +445,8 @@ int plda_init_interrupts(struct platform
+               return ret;
+       }
+-      irq = platform_get_irq(pdev, 0);
+-      if (irq < 0)
++      port->irq = platform_get_irq(pdev, 0);
++      if (port->irq < 0)
+               return -ENODEV;
+       for_each_set_bit(i, &port->events_bitmap, port->num_events) {
+@@ -461,26 +469,26 @@ int plda_init_interrupts(struct platform
+               }
+       }
+-      intx_irq = irq_create_mapping(port->event_domain,
+-                                    event->intx_event);
+-      if (!intx_irq) {
++      port->intx_irq = irq_create_mapping(port->event_domain,
++                                          event->intx_event);
++      if (!port->intx_irq) {
+               dev_err(dev, "failed to map INTx interrupt\n");
+               return -ENXIO;
+       }
+       /* Plug the INTx chained handler */
+-      irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port);
++      irq_set_chained_handler_and_data(port->intx_irq, plda_handle_intx, port);
+-      msi_irq = irq_create_mapping(port->event_domain,
+-                                   event->msi_event);
+-      if (!msi_irq)
++      port->msi_irq = irq_create_mapping(port->event_domain,
++                                         event->msi_event);
++      if (!port->msi_irq)
+               return -ENXIO;
+       /* Plug the MSI chained handler */
+-      irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port);
++      irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port);
+       /* Plug the main event chained handler */
+-      irq_set_chained_handler_and_data(irq, plda_handle_event, port);
++      irq_set_chained_handler_and_data(port->irq, plda_handle_event, port);
+       return 0;
+ }
+@@ -546,3 +554,98 @@ int plda_pcie_setup_iomems(struct pci_ho
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);
++
++static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie)
++{
++      irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
++      irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
++      irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);
++
++      irq_domain_remove(pcie->msi.msi_domain);
++      irq_domain_remove(pcie->msi.dev_domain);
++
++      irq_domain_remove(pcie->intx_domain);
++      irq_domain_remove(pcie->event_domain);
++}
++
++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops,
++                      const struct plda_event *plda_event)
++{
++      struct device *dev = port->dev;
++      struct pci_host_bridge *bridge;
++      struct platform_device *pdev = to_platform_device(dev);
++      struct resource *cfg_res;
++      int ret;
++
++      pdev = to_platform_device(dev);
++
++      port->bridge_addr =
++              devm_platform_ioremap_resource_byname(pdev, "apb");
++
++      if (IS_ERR(port->bridge_addr))
++              return dev_err_probe(dev, PTR_ERR(port->bridge_addr),
++                                   "failed to map reg memory\n");
++
++      cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
++      if (!cfg_res)
++              return dev_err_probe(dev, -ENODEV,
++                                   "failed to get config memory\n");
++
++      port->config_base = devm_ioremap_resource(dev, cfg_res);
++      if (IS_ERR(port->config_base))
++              return dev_err_probe(dev, PTR_ERR(port->config_base),
++                                   "failed to map config memory\n");
++
++      bridge = devm_pci_alloc_host_bridge(dev, 0);
++      if (!bridge)
++              return dev_err_probe(dev, -ENOMEM,
++                                   "failed to alloc bridge\n");
++
++      if (port->host_ops && port->host_ops->host_init) {
++              ret = port->host_ops->host_init(port);
++              if (ret)
++                      return ret;
++      }
++
++      port->bridge = bridge;
++      plda_pcie_setup_window(port->bridge_addr, 0, cfg_res->start, 0,
++                             resource_size(cfg_res));
++      plda_pcie_setup_iomems(bridge, port);
++      plda_set_default_msi(&port->msi);
++      ret = plda_init_interrupts(pdev, port, plda_event);
++      if (ret)
++              goto err_host;
++
++      /* Set default bus ops */
++      bridge->ops = ops;
++      bridge->sysdata = port;
++
++      ret = pci_host_probe(bridge);
++      if (ret < 0) {
++              dev_err_probe(dev, ret, "failed to probe pci host\n");
++              goto err_probe;
++      }
++
++      return ret;
++
++err_probe:
++      plda_pcie_irq_domain_deinit(port);
++err_host:
++      if (port->host_ops && port->host_ops->host_deinit)
++              port->host_ops->host_deinit(port);
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(plda_pcie_host_init);
++
++void plda_pcie_host_deinit(struct plda_pcie_rp *port)
++{
++      pci_stop_root_bus(port->bridge->bus);
++      pci_remove_root_bus(port->bridge->bus);
++
++      plda_pcie_irq_domain_deinit(port);
++
++      if (port->host_ops && port->host_ops->host_deinit)
++              port->host_ops->host_deinit(port);
++}
++EXPORT_SYMBOL_GPL(plda_pcie_host_deinit);
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -141,6 +141,11 @@ struct plda_event_ops {
+       u32 (*get_events)(struct plda_pcie_rp *pcie);
+ };
++struct plda_pcie_host_ops {
++      int (*host_init)(struct plda_pcie_rp *pcie);
++      void (*host_deinit)(struct plda_pcie_rp *pcie);
++};
++
+ struct plda_msi {
+       struct mutex lock;              /* Protect used bitmap */
+       struct irq_domain *msi_domain;
+@@ -152,14 +157,20 @@ struct plda_msi {
+ struct plda_pcie_rp {
+       struct device *dev;
++      struct pci_host_bridge *bridge;
+       struct irq_domain *intx_domain;
+       struct irq_domain *event_domain;
+       raw_spinlock_t lock;
+       struct plda_msi msi;
+       const struct plda_event_ops *event_ops;
+       const struct irq_chip *event_irq_chip;
++      const struct plda_pcie_host_ops *host_ops;
+       void __iomem *bridge_addr;
++      void __iomem *config_base;
+       unsigned long events_bitmap;
++      int irq;
++      int msi_irq;
++      int intx_irq;
+       int num_events;
+ };
+@@ -170,6 +181,8 @@ struct plda_event {
+       int msi_event;
+ };
++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
++                              int where);
+ int plda_init_interrupts(struct platform_device *pdev,
+                        struct plda_pcie_rp *port,
+                        const struct plda_event *event);
+@@ -178,4 +191,13 @@ void plda_pcie_setup_window(void __iomem
+                           size_t size);
+ int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
+                          struct plda_pcie_rp *port);
++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops,
++                      const struct plda_event *plda_event);
++void plda_pcie_host_deinit(struct plda_pcie_rp *pcie);
++
++static inline void plda_set_default_msi(struct plda_msi *msi)
++{
++      msi->vector_phy = IMSI_ADDR;
++      msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS;
++}
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch b/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch
new file mode 100644 (file)
index 0000000..9e33f7d
--- /dev/null
@@ -0,0 +1,140 @@
+From bc3f8207d9f0af3cb96a7eae232074a644a175f6 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:09 +0800
+Subject: [PATCH 032/116] dt-bindings: PCI: Add StarFive JH7110 PCIe controller
+
+Add StarFive JH7110 SoC PCIe controller dt-bindings. JH7110 using PLDA
+XpressRICH PCIe host controller IP.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ .../bindings/pci/starfive,jh7110-pcie.yaml    | 120 ++++++++++++++++++
+ 1 file changed, 120 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
+@@ -0,0 +1,120 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pci/starfive,jh7110-pcie.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7110 PCIe host controller
++
++maintainers:
++  - Kevin Xie <kevin.xie@starfivetech.com>
++
++allOf:
++  - $ref: plda,xpressrich3-axi-common.yaml#
++
++properties:
++  compatible:
++    const: starfive,jh7110-pcie
++
++  clocks:
++    items:
++      - description: NOC bus clock
++      - description: Transport layer clock
++      - description: AXI MST0 clock
++      - description: APB clock
++
++  clock-names:
++    items:
++      - const: noc
++      - const: tl
++      - const: axi_mst0
++      - const: apb
++
++  resets:
++    items:
++      - description: AXI MST0 reset
++      - description: AXI SLAVE0 reset
++      - description: AXI SLAVE reset
++      - description: PCIE BRIDGE reset
++      - description: PCIE CORE reset
++      - description: PCIE APB reset
++
++  reset-names:
++    items:
++      - const: mst0
++      - const: slv0
++      - const: slv
++      - const: brg
++      - const: core
++      - const: apb
++
++  starfive,stg-syscon:
++    $ref: /schemas/types.yaml#/definitions/phandle-array
++    description:
++      The phandle to System Register Controller syscon node.
++
++  perst-gpios:
++    description: GPIO controlled connection to PERST# signal
++    maxItems: 1
++
++  phys:
++    description:
++      Specified PHY is attached to PCIe controller.
++    maxItems: 1
++
++required:
++  - clocks
++  - resets
++  - starfive,stg-syscon
++
++unevaluatedProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/gpio/gpio.h>
++    soc {
++        #address-cells = <2>;
++        #size-cells = <2>;
++
++        pcie@940000000 {
++            compatible = "starfive,jh7110-pcie";
++            reg = <0x9 0x40000000 0x0 0x10000000>,
++                  <0x0 0x2b000000 0x0 0x1000000>;
++            reg-names = "cfg", "apb";
++            #address-cells = <3>;
++            #size-cells = <2>;
++            #interrupt-cells = <1>;
++            device_type = "pci";
++            ranges = <0x82000000  0x0 0x30000000  0x0 0x30000000 0x0 0x08000000>,
++                     <0xc3000000  0x9 0x00000000  0x9 0x00000000 0x0 0x40000000>;
++            starfive,stg-syscon = <&stg_syscon>;
++            bus-range = <0x0 0xff>;
++            interrupt-parent = <&plic>;
++            interrupts = <56>;
++            interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++            interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>,
++                            <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>,
++                            <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>,
++                            <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>;
++            msi-controller;
++            clocks = <&syscrg 86>,
++                     <&stgcrg 10>,
++                     <&stgcrg 8>,
++                     <&stgcrg 9>;
++            clock-names = "noc", "tl", "axi_mst0", "apb";
++            resets = <&stgcrg 11>,
++                     <&stgcrg 12>,
++                     <&stgcrg 13>,
++                     <&stgcrg 14>,
++                     <&stgcrg 15>,
++                     <&stgcrg 16>;
++            perst-gpios = <&gpios 26 GPIO_ACTIVE_LOW>;
++            phys = <&pciephy0>;
++
++            pcie_intc0: interrupt-controller {
++                #address-cells = <0>;
++                #interrupt-cells = <1>;
++                interrupt-controller;
++            };
++        };
++    };
diff --git a/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch b/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch
new file mode 100644 (file)
index 0000000..cc50dfe
--- /dev/null
@@ -0,0 +1,55 @@
+From abb20b7b8f5e3a7f36dbd6264e6d346275434154 Mon Sep 17 00:00:00 2001
+From: Kevin Xie <kevin.xie@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:10 +0800
+Subject: [PATCH 033/116] PCI: Add PCIE_RESET_CONFIG_DEVICE_WAIT_MS waiting
+ time value
+
+Add the PCIE_RESET_CONFIG_DEVICE_WAIT_MS macro to define the minimum
+waiting time between exit from a conventional reset and sending the
+first configuration request to the device.
+
+As described in PCI base specification r6.0, section 6.6.1 <Conventional
+Reset>, there are two different use cases of the value:
+
+   - "With a Downstream Port that does not support Link speeds greater
+     than 5.0 GT/s, software must wait a minimum of 100 ms following exit
+     from a Conventional Reset before sending a Configuration Request to
+     the device immediately below that Port."
+
+   - "With a Downstream Port that supports Link speeds greater than
+     5.0 GT/s, software must wait a minimum of 100 ms after Link training
+     completes before sending a Configuration Request to the device
+     immediately below that Port."
+
+Signed-off-by: Kevin Xie <kevin.xie@starfivetech.com>
+Reviewed-by: Mason Huo <mason.huo@starfivetech.com>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/pci.h | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -19,6 +19,22 @@
+  */
+ #define PCIE_PME_TO_L2_TIMEOUT_US     10000
++/*
++ * As described in PCI base specification r6.0, section 6.6.1 <Conventional
++ * Reset>, there are two different use cases of the value:
++ *
++ * - "With a Downstream Port that does not support Link speeds greater
++ *    than 5.0 GT/s, software must wait a minimum of 100 ms following exit
++ *    from a Conventional Reset before sending a Configuration Request to
++ *    the device immediately below that Port."
++ *
++ * - "With a Downstream Port that supports Link speeds greater than
++ *    5.0 GT/s, software must wait a minimum of 100 ms after Link training
++ *    completes before sending a Configuration Request to the device
++ *    immediately below that Port."
++ */
++#define PCIE_RESET_CONFIG_DEVICE_WAIT_MS      100
++
+ extern const unsigned char pcie_link_speed[];
+ extern bool pci_early_dump;
diff --git a/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch b/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch
new file mode 100644 (file)
index 0000000..45a549f
--- /dev/null
@@ -0,0 +1,623 @@
+From 323aedef34315b758dc30ba23e2cabca259bb4b2 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 8 Jan 2024 19:06:11 +0800
+Subject: [PATCH 034/116] PCI: starfive: Add JH7110 PCIe controller
+
+Add StarFive JH7110 SoC PCIe controller platform driver codes, JH7110
+with PLDA host PCIe core.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Co-developed-by: Kevin Xie <kevin.xie@starfivetech.com>
+Reviewed-by: Mason Huo <mason.huo@starfivetech.com>
+---
+ drivers/pci/controller/plda/Kconfig         |  12 +
+ drivers/pci/controller/plda/Makefile        |   1 +
+ drivers/pci/controller/plda/pcie-plda.h     |  71 ++-
+ drivers/pci/controller/plda/pcie-starfive.c | 473 ++++++++++++++++++++
+ 4 files changed, 556 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/pci/controller/plda/pcie-starfive.c
+
+--- a/drivers/pci/controller/plda/Kconfig
++++ b/drivers/pci/controller/plda/Kconfig
+@@ -15,4 +15,16 @@ config PCIE_MICROCHIP_HOST
+         Say Y here if you want kernel to support the Microchip AXI PCIe
+         Host Bridge driver.
++config PCIE_STARFIVE_HOST
++      tristate "StarFive PCIe host controller"
++      depends on PCI_MSI && OF
++      depends on ARCH_STARFIVE || COMPILE_TEST
++      select PCIE_PLDA_HOST
++      help
++        Say Y here if you want to support the StarFive PCIe controller in
++        host mode. StarFive PCIe controller uses PLDA PCIe core.
++
++        If you choose to build this driver as module it will be dynamically
++        linked and module will be called pcie-starfive.ko.
++
+ endmenu
+--- a/drivers/pci/controller/plda/Makefile
++++ b/drivers/pci/controller/plda/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o
+ obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
++obj-$(CONFIG_PCIE_STARFIVE_HOST) += pcie-starfive.o
+--- a/drivers/pci/controller/plda/pcie-plda.h
++++ b/drivers/pci/controller/plda/pcie-plda.h
+@@ -10,10 +10,20 @@
+ #define PLDA_MAX_NUM_MSI_IRQS                 32
+ /* PCIe Bridge Phy Regs */
++#define GEN_SETTINGS                          0x80
++#define  RP_ENABLE                            1
++#define PCIE_PCI_IDS_DW1                      0x9c
++#define  IDS_CLASS_CODE_SHIFT                 16
++#define  REVISION_ID_MASK                     GENMASK(7, 0)
++#define  CLASS_CODE_ID_MASK                   GENMASK(31, 8)
+ #define PCIE_PCI_IRQ_DW0                      0xa8
+ #define  MSIX_CAP_MASK                                BIT(31)
+ #define  NUM_MSI_MSGS_MASK                    GENMASK(6, 4)
+ #define  NUM_MSI_MSGS_SHIFT                   4
++#define PCI_MISC                              0xb4
++#define  PHY_FUNCTION_DIS                     BIT(15)
++#define PCIE_WINROM                           0xfc
++#define  PREF_MEM_WIN_64_SUPPORT              BIT(3)
+ #define IMASK_LOCAL                           0x180
+ #define  DMA_END_ENGINE_0_MASK                        0x00000000u
+@@ -65,6 +75,8 @@
+ #define ISTATUS_HOST                          0x18c
+ #define IMSI_ADDR                             0x190
+ #define ISTATUS_MSI                           0x194
++#define PMSG_SUPPORT_RX                               0x3f0
++#define  PMSG_LTR_SUPPORT                     BIT(2)
+ /* PCIe Master table init defines */
+ #define ATR0_PCIE_WIN0_SRCADDR_PARAM          0x600u
+@@ -86,6 +98,8 @@
+ #define  PCIE_TX_RX_INTERFACE                 0x00000000u
+ #define  PCIE_CONFIG_INTERFACE                        0x00000001u
++#define CONFIG_SPACE_ADDR_OFFSET              0x1000u
++
+ #define ATR_ENTRY_SIZE                                32
+ enum plda_int_event {
+@@ -200,4 +214,59 @@ static inline void plda_set_default_msi(
+       msi->vector_phy = IMSI_ADDR;
+       msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS;
+ }
+-#endif
++
++static inline void plda_pcie_enable_root_port(struct plda_pcie_rp *plda)
++{
++      u32 value;
++
++      value = readl_relaxed(plda->bridge_addr + GEN_SETTINGS);
++      value |= RP_ENABLE;
++      writel_relaxed(value, plda->bridge_addr + GEN_SETTINGS);
++}
++
++static inline void plda_pcie_set_standard_class(struct plda_pcie_rp *plda)
++{
++      u32 value;
++
++      /* set class code and reserve revision id */
++      value = readl_relaxed(plda->bridge_addr + PCIE_PCI_IDS_DW1);
++      value &= REVISION_ID_MASK;
++      value |= (PCI_CLASS_BRIDGE_PCI << IDS_CLASS_CODE_SHIFT);
++      writel_relaxed(value, plda->bridge_addr + PCIE_PCI_IDS_DW1);
++}
++
++static inline void plda_pcie_set_pref_win_64bit(struct plda_pcie_rp *plda)
++{
++      u32 value;
++
++      value = readl_relaxed(plda->bridge_addr + PCIE_WINROM);
++      value |= PREF_MEM_WIN_64_SUPPORT;
++      writel_relaxed(value, plda->bridge_addr + PCIE_WINROM);
++}
++
++static inline void plda_pcie_disable_ltr(struct plda_pcie_rp *plda)
++{
++      u32 value;
++
++      value = readl_relaxed(plda->bridge_addr + PMSG_SUPPORT_RX);
++      value &= ~PMSG_LTR_SUPPORT;
++      writel_relaxed(value, plda->bridge_addr + PMSG_SUPPORT_RX);
++}
++
++static inline void plda_pcie_disable_func(struct plda_pcie_rp *plda)
++{
++      u32 value;
++
++      value = readl_relaxed(plda->bridge_addr + PCI_MISC);
++      value |= PHY_FUNCTION_DIS;
++      writel_relaxed(value, plda->bridge_addr + PCI_MISC);
++}
++
++static inline void plda_pcie_write_rc_bar(struct plda_pcie_rp *plda, u64 val)
++{
++      void __iomem *addr = plda->bridge_addr + CONFIG_SPACE_ADDR_OFFSET;
++
++      writel_relaxed(lower_32_bits(val), addr + PCI_BASE_ADDRESS_0);
++      writel_relaxed(upper_32_bits(val), addr + PCI_BASE_ADDRESS_1);
++}
++#endif /* _PCIE_PLDA_H */
+--- /dev/null
++++ b/drivers/pci/controller/plda/pcie-starfive.c
+@@ -0,0 +1,473 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * PCIe host controller driver for StarFive JH7110 Soc.
++ *
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/interrupt.h>
++#include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include "../../pci.h"
++
++#include "pcie-plda.h"
++
++#define PCIE_FUNC_NUM                 4
++
++/* system control */
++#define STG_SYSCON_PCIE0_BASE                 0x48
++#define STG_SYSCON_PCIE1_BASE                 0x1f8
++
++#define STG_SYSCON_AR_OFFSET                  0x78
++#define STG_SYSCON_AXI4_SLVL_AR_MASK          GENMASK(22, 8)
++#define STG_SYSCON_AXI4_SLVL_PHY_AR(x)                FIELD_PREP(GENMASK(20, 17), x)
++#define STG_SYSCON_AW_OFFSET                  0x7c
++#define STG_SYSCON_AXI4_SLVL_AW_MASK          GENMASK(14, 0)
++#define STG_SYSCON_AXI4_SLVL_PHY_AW(x)                FIELD_PREP(GENMASK(12, 9), x)
++#define STG_SYSCON_CLKREQ                     BIT(22)
++#define STG_SYSCON_CKREF_SRC_MASK             GENMASK(19, 18)
++#define STG_SYSCON_RP_NEP_OFFSET              0xe8
++#define STG_SYSCON_K_RP_NEP                   BIT(8)
++#define STG_SYSCON_LNKSTA_OFFSET              0x170
++#define DATA_LINK_ACTIVE                      BIT(5)
++
++/* Parameters for the waiting for link up routine */
++#define LINK_WAIT_MAX_RETRIES 10
++#define LINK_WAIT_USLEEP_MIN  90000
++#define LINK_WAIT_USLEEP_MAX  100000
++
++struct starfive_jh7110_pcie {
++      struct plda_pcie_rp plda;
++      struct reset_control *resets;
++      struct clk_bulk_data *clks;
++      struct regmap *reg_syscon;
++      struct gpio_desc *power_gpio;
++      struct gpio_desc *reset_gpio;
++      struct phy *phy;
++
++      unsigned int stg_pcie_base;
++      int num_clks;
++};
++
++/*
++ * The BAR0/1 of bridge should be hidden during enumeration to
++ * avoid the sizing and resource allocation by PCIe core.
++ */
++static bool starfive_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn,
++                                    int offset)
++{
++      if (pci_is_root_bus(bus) && !devfn &&
++          (offset == PCI_BASE_ADDRESS_0 || offset == PCI_BASE_ADDRESS_1))
++              return true;
++
++      return false;
++}
++
++static int starfive_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
++                                    int where, int size, u32 value)
++{
++      if (starfive_pcie_hide_rc_bar(bus, devfn, where))
++              return PCIBIOS_SUCCESSFUL;
++
++      return pci_generic_config_write(bus, devfn, where, size, value);
++}
++
++static int starfive_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
++                                   int where, int size, u32 *value)
++{
++      if (starfive_pcie_hide_rc_bar(bus, devfn, where)) {
++              *value = 0;
++              return PCIBIOS_SUCCESSFUL;
++      }
++
++      return pci_generic_config_read(bus, devfn, where, size, value);
++}
++
++static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie,
++                                struct device *dev)
++{
++      int domain_nr;
++
++      pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks);
++      if (pcie->num_clks < 0)
++              return dev_err_probe(dev, pcie->num_clks,
++                                   "failed to get pcie clocks\n");
++
++      pcie->resets = devm_reset_control_array_get_exclusive(dev);
++      if (IS_ERR(pcie->resets))
++              return dev_err_probe(dev, PTR_ERR(pcie->resets),
++                                   "failed to get pcie resets");
++
++      pcie->reg_syscon =
++              syscon_regmap_lookup_by_phandle(dev->of_node,
++                                              "starfive,stg-syscon");
++
++      if (IS_ERR(pcie->reg_syscon))
++              return dev_err_probe(dev, PTR_ERR(pcie->reg_syscon),
++                                   "failed to parse starfive,stg-syscon\n");
++
++      pcie->phy = devm_phy_optional_get(dev, NULL);
++      if (IS_ERR(pcie->phy))
++              return dev_err_probe(dev, PTR_ERR(pcie->phy),
++                                   "failed to get pcie phy\n");
++
++      domain_nr = of_get_pci_domain_nr(dev->of_node);
++
++      if (domain_nr < 0 || domain_nr > 1)
++              return dev_err_probe(dev, -ENODEV,
++                                   "failed to get valid pcie domain\n");
++
++      if (domain_nr == 0)
++              pcie->stg_pcie_base = STG_SYSCON_PCIE0_BASE;
++      else
++              pcie->stg_pcie_base = STG_SYSCON_PCIE1_BASE;
++
++      pcie->reset_gpio = devm_gpiod_get_optional(dev, "perst",
++                                                 GPIOD_OUT_HIGH);
++      if (IS_ERR(pcie->reset_gpio))
++              return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio),
++                                   "failed to get perst-gpio\n");
++
++      pcie->power_gpio = devm_gpiod_get_optional(dev, "enable",
++                                                 GPIOD_OUT_LOW);
++      if (IS_ERR(pcie->power_gpio))
++              return dev_err_probe(dev, PTR_ERR(pcie->power_gpio),
++                                   "failed to get power-gpio\n");
++
++      return 0;
++}
++
++static struct pci_ops starfive_pcie_ops = {
++      .map_bus        = plda_pcie_map_bus,
++      .read           = starfive_pcie_config_read,
++      .write          = starfive_pcie_config_write,
++};
++
++static int starfive_pcie_clk_rst_init(struct starfive_jh7110_pcie *pcie)
++{
++      struct device *dev = pcie->plda.dev;
++      int ret;
++
++      ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
++      if (ret)
++              return dev_err_probe(dev, ret, "failed to enable clocks\n");
++
++      ret = reset_control_deassert(pcie->resets);
++      if (ret) {
++              clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
++              dev_err_probe(dev, ret, "failed to deassert resets\n");
++      }
++
++      return ret;
++}
++
++static void starfive_pcie_clk_rst_deinit(struct starfive_jh7110_pcie *pcie)
++{
++      reset_control_assert(pcie->resets);
++      clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
++}
++
++static bool starfive_pcie_link_up(struct plda_pcie_rp *plda)
++{
++      struct starfive_jh7110_pcie *pcie =
++              container_of(plda, struct starfive_jh7110_pcie, plda);
++      int ret;
++      u32 stg_reg_val;
++
++      ret = regmap_read(pcie->reg_syscon,
++                        pcie->stg_pcie_base + STG_SYSCON_LNKSTA_OFFSET,
++                        &stg_reg_val);
++      if (ret) {
++              dev_err(pcie->plda.dev, "failed to read link status\n");
++              return false;
++      }
++
++      return !!(stg_reg_val & DATA_LINK_ACTIVE);
++}
++
++static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie)
++{
++      int retries;
++
++      /* Check if the link is up or not */
++      for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
++              if (starfive_pcie_link_up(&pcie->plda)) {
++                      dev_info(pcie->plda.dev, "port link up\n");
++                      return 0;
++              }
++              usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
++      }
++
++      return -ETIMEDOUT;
++}
++
++static int starfive_pcie_enable_phy(struct device *dev,
++                                  struct starfive_jh7110_pcie *pcie)
++{
++      int ret;
++
++      if (!pcie->phy)
++              return 0;
++
++      ret = phy_init(pcie->phy);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "failed to initialize pcie phy\n");
++
++      ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE);
++      if (ret) {
++              dev_err_probe(dev, ret, "failed to set pcie mode\n");
++              goto err_phy_on;
++      }
++
++      ret = phy_power_on(pcie->phy);
++      if (ret) {
++              dev_err_probe(dev, ret, "failed to power on pcie phy\n");
++              goto err_phy_on;
++      }
++
++      return 0;
++
++err_phy_on:
++      phy_exit(pcie->phy);
++      return ret;
++}
++
++static void starfive_pcie_disable_phy(struct starfive_jh7110_pcie *pcie)
++{
++      phy_power_off(pcie->phy);
++      phy_exit(pcie->phy);
++}
++
++static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda)
++{
++      struct starfive_jh7110_pcie *pcie =
++              container_of(plda, struct starfive_jh7110_pcie, plda);
++
++      starfive_pcie_clk_rst_deinit(pcie);
++      if (pcie->power_gpio)
++              gpiod_set_value_cansleep(pcie->power_gpio, 0);
++      starfive_pcie_disable_phy(pcie);
++}
++
++static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
++{
++      struct starfive_jh7110_pcie *pcie =
++              container_of(plda, struct starfive_jh7110_pcie, plda);
++      struct device *dev = plda->dev;
++      int ret;
++      int i;
++
++      ret = starfive_pcie_enable_phy(dev, pcie);
++      if (ret)
++              return ret;
++
++      regmap_update_bits(pcie->reg_syscon,
++                         pcie->stg_pcie_base + STG_SYSCON_RP_NEP_OFFSET,
++                         STG_SYSCON_K_RP_NEP, STG_SYSCON_K_RP_NEP);
++
++      regmap_update_bits(pcie->reg_syscon,
++                         pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++                         STG_SYSCON_CKREF_SRC_MASK,
++                         FIELD_PREP(STG_SYSCON_CKREF_SRC_MASK, 2));
++
++      regmap_update_bits(pcie->reg_syscon,
++                         pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++                         STG_SYSCON_CLKREQ, STG_SYSCON_CLKREQ);
++
++      ret = starfive_pcie_clk_rst_init(pcie);
++      if (ret)
++              return ret;
++
++      if (pcie->power_gpio)
++              gpiod_set_value_cansleep(pcie->power_gpio, 1);
++
++      if (pcie->reset_gpio)
++              gpiod_set_value_cansleep(pcie->reset_gpio, 1);
++
++      /* Disable physical functions except #0 */
++      for (i = 1; i < PCIE_FUNC_NUM; i++) {
++              regmap_update_bits(pcie->reg_syscon,
++                                 pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET,
++                                 STG_SYSCON_AXI4_SLVL_AR_MASK,
++                                 STG_SYSCON_AXI4_SLVL_PHY_AR(i));
++
++              regmap_update_bits(pcie->reg_syscon,
++                                 pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++                                 STG_SYSCON_AXI4_SLVL_AW_MASK,
++                                 STG_SYSCON_AXI4_SLVL_PHY_AW(i));
++
++              plda_pcie_disable_func(plda);
++      }
++
++      regmap_update_bits(pcie->reg_syscon,
++                         pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET,
++                         STG_SYSCON_AXI4_SLVL_AR_MASK, 0);
++      regmap_update_bits(pcie->reg_syscon,
++                         pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET,
++                         STG_SYSCON_AXI4_SLVL_AW_MASK, 0);
++
++      plda_pcie_enable_root_port(plda);
++      plda_pcie_write_rc_bar(plda, 0);
++
++      /* PCIe PCI Standard Configuration Identification Settings. */
++      plda_pcie_set_standard_class(plda);
++
++      /*
++       * The LTR message forwarding of PCIe Message Reception was set by core
++       * as default, but the forward id & addr are also need to be reset.
++       * If we do not disable LTR message forwarding here, or set a legal
++       * forwarding address, the kernel will get stuck after the driver probe.
++       * To workaround, disable the LTR message forwarding support on
++       * PCIe Message Reception.
++       */
++      plda_pcie_disable_ltr(plda);
++
++      /* Prefetchable memory window 64-bit addressing support */
++      plda_pcie_set_pref_win_64bit(plda);
++
++      /*
++       * Ensure that PERST has been asserted for at least 100 ms,
++       * the sleep value is T_PVPERL from PCIe CEM spec r2.0 (Table 2-4)
++       */
++      msleep(100);
++      if (pcie->reset_gpio)
++              gpiod_set_value_cansleep(pcie->reset_gpio, 0);
++
++      /*
++       * With a Downstream Port (<=5GT/s), software must wait a minimum
++       * of 100ms following exit from a conventional reset before
++       * sending a configuration request to the device.
++       */
++      msleep(PCIE_RESET_CONFIG_DEVICE_WAIT_MS);
++
++      if (starfive_pcie_host_wait_for_link(pcie))
++              dev_info(dev, "port link down\n");
++
++      return 0;
++}
++
++static const struct plda_pcie_host_ops sf_host_ops = {
++      .host_init = starfive_pcie_host_init,
++      .host_deinit = starfive_pcie_host_deinit,
++};
++
++static const struct plda_event stf_pcie_event = {
++      .intx_event = EVENT_PM_MSI_INT_INTX,
++      .msi_event  = EVENT_PM_MSI_INT_MSI
++};
++
++static int starfive_pcie_probe(struct platform_device *pdev)
++{
++      struct starfive_jh7110_pcie *pcie;
++      struct device *dev = &pdev->dev;
++      struct plda_pcie_rp *plda;
++      int ret;
++
++      pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      plda = &pcie->plda;
++      plda->dev = dev;
++
++      ret = starfive_pcie_parse_dt(pcie, dev);
++      if (ret)
++              return ret;
++
++      plda->host_ops = &sf_host_ops;
++      plda->num_events = PLDA_MAX_EVENT_NUM;
++      /* mask doorbell event */
++      plda->events_bitmap = GENMASK(PLDA_INT_EVENT_NUM - 1, 0)
++                           & ~BIT(PLDA_AXI_DOORBELL)
++                           & ~BIT(PLDA_PCIE_DOORBELL);
++      plda->events_bitmap <<= PLDA_NUM_DMA_EVENTS;
++      ret = plda_pcie_host_init(&pcie->plda, &starfive_pcie_ops,
++                                &stf_pcie_event);
++      if (ret)
++              return ret;
++
++      pm_runtime_enable(&pdev->dev);
++      pm_runtime_get_sync(&pdev->dev);
++      platform_set_drvdata(pdev, pcie);
++
++      return 0;
++}
++
++static void starfive_pcie_remove(struct platform_device *pdev)
++{
++      struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev);
++
++      pm_runtime_put(&pdev->dev);
++      pm_runtime_disable(&pdev->dev);
++      plda_pcie_host_deinit(&pcie->plda);
++      platform_set_drvdata(pdev, NULL);
++}
++
++static int starfive_pcie_suspend_noirq(struct device *dev)
++{
++      struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev);
++
++      clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
++      starfive_pcie_disable_phy(pcie);
++
++      return 0;
++}
++
++static int starfive_pcie_resume_noirq(struct device *dev)
++{
++      struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev);
++      int ret;
++
++      ret = starfive_pcie_enable_phy(dev, pcie);
++      if (ret)
++              return ret;
++
++      ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
++      if (ret) {
++              dev_err(dev, "failed to enable clocks\n");
++              starfive_pcie_disable_phy(pcie);
++              return ret;
++      }
++
++      return 0;
++}
++
++static const struct dev_pm_ops starfive_pcie_pm_ops = {
++      NOIRQ_SYSTEM_SLEEP_PM_OPS(starfive_pcie_suspend_noirq,
++                                starfive_pcie_resume_noirq)
++};
++
++static const struct of_device_id starfive_pcie_of_match[] = {
++      { .compatible = "starfive,jh7110-pcie", },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, starfive_pcie_of_match);
++
++static struct platform_driver starfive_pcie_driver = {
++      .driver = {
++              .name = "pcie-starfive",
++              .of_match_table = of_match_ptr(starfive_pcie_of_match),
++              .pm = pm_sleep_ptr(&starfive_pcie_pm_ops),
++      },
++      .probe = starfive_pcie_probe,
++      .remove_new = starfive_pcie_remove,
++};
++module_platform_driver(starfive_pcie_driver);
++
++MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch b/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch
new file mode 100644 (file)
index 0000000..cf14242
--- /dev/null
@@ -0,0 +1,97 @@
+From a306724fd4f32808d1e27efbd87019d56f60db20 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 14 Aug 2023 16:06:16 +0800
+Subject: [PATCH 035/116] ASoC: dt-bindings: Add StarFive JH7110 PWM-DAC
+ controller
+
+Add bindings for the PWM-DAC controller on the JH7110
+RISC-V SoC by StarFive Ltd.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+Link: https://lore.kernel.org/r/20230814080618.10036-2-hal.feng@starfivetech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ .../sound/starfive,jh7110-pwmdac.yaml         | 76 +++++++++++++++++++
+ 1 file changed, 76 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
+@@ -0,0 +1,76 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/sound/starfive,jh7110-pwmdac.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7110 PWM-DAC Controller
++
++description:
++  The PWM-DAC Controller uses PWM square wave generators plus RC filters to
++  form a DAC for audio play in StarFive JH7110 SoC. This audio play controller
++  supports 16 bit audio format, up to 48K sampling frequency, up to left and
++  right dual channels.
++
++maintainers:
++  - Hal Feng <hal.feng@starfivetech.com>
++
++allOf:
++  - $ref: dai-common.yaml#
++
++properties:
++  compatible:
++    const: starfive,jh7110-pwmdac
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: PWMDAC APB
++      - description: PWMDAC CORE
++
++  clock-names:
++    items:
++      - const: apb
++      - const: core
++
++  resets:
++    maxItems: 1
++    description: PWMDAC APB
++
++  dmas:
++    maxItems: 1
++    description: TX DMA Channel
++
++  dma-names:
++    const: tx
++
++  "#sound-dai-cells":
++    const: 0
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - clock-names
++  - resets
++  - dmas
++  - dma-names
++  - "#sound-dai-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    pwmdac@100b0000 {
++        compatible = "starfive,jh7110-pwmdac";
++        reg = <0x100b0000 0x1000>;
++        clocks = <&syscrg 157>,
++                 <&syscrg 158>;
++        clock-names = "apb", "core";
++        resets = <&syscrg 96>;
++        dmas = <&dma 22>;
++        dma-names = "tx";
++        #sound-dai-cells = <0>;
++    };
diff --git a/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch b/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch
new file mode 100644 (file)
index 0000000..acc8593
--- /dev/null
@@ -0,0 +1,574 @@
+From a79d2ec524012e35e32a2c4ae2401d0aa763697d Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 14 Aug 2023 16:06:17 +0800
+Subject: [PATCH 036/116] ASoC: starfive: Add JH7110 PWM-DAC driver
+
+Add PWM-DAC driver support for the StarFive JH7110 SoC.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+Link: https://lore.kernel.org/r/20230814080618.10036-3-hal.feng@starfivetech.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/starfive/Kconfig         |   9 +
+ sound/soc/starfive/Makefile        |   1 +
+ sound/soc/starfive/jh7110_pwmdac.c | 529 +++++++++++++++++++++++++++++
+ 3 files changed, 539 insertions(+)
+ create mode 100644 sound/soc/starfive/jh7110_pwmdac.c
+
+--- a/sound/soc/starfive/Kconfig
++++ b/sound/soc/starfive/Kconfig
+@@ -7,6 +7,15 @@ config SND_SOC_STARFIVE
+         the Starfive SoCs' Audio interfaces. You will also need to
+         select the audio interfaces to support below.
++config SND_SOC_JH7110_PWMDAC
++      tristate "JH7110 PWM-DAC device driver"
++      depends on HAVE_CLK && SND_SOC_STARFIVE
++      select SND_SOC_GENERIC_DMAENGINE_PCM
++      select SND_SOC_SPDIF
++      help
++       Say Y or M if you want to add support for StarFive JH7110
++       PWM-DAC driver.
++
+ config SND_SOC_JH7110_TDM
+       tristate "JH7110 TDM device driver"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+--- a/sound/soc/starfive/Makefile
++++ b/sound/soc/starfive/Makefile
+@@ -1,2 +1,3 @@
+ # StarFive Platform Support
++obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
+ obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_pwmdac.c
+@@ -0,0 +1,529 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ * Authors: Jenny Zhang
++ *        Curry Zhang
++ *        Xingyu Wu <xingyu.wu@starfivetech.com>
++ *        Hal Feng <hal.feng@starfivetech.com>
++ */
++
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#define JH7110_PWMDAC_WDATA                           0x00
++#define JH7110_PWMDAC_CTRL                            0x04
++      #define JH7110_PWMDAC_ENABLE                    BIT(0)
++      #define JH7110_PWMDAC_SHIFT                     BIT(1)
++      #define JH7110_PWMDAC_DUTY_CYCLE_SHIFT          2
++      #define JH7110_PWMDAC_DUTY_CYCLE_MASK           GENMASK(3, 2)
++      #define JH7110_PWMDAC_CNT_N_SHIFT               4
++      #define JH7110_PWMDAC_CNT_N_MASK                GENMASK(12, 4)
++      #define JH7110_PWMDAC_DATA_CHANGE               BIT(13)
++      #define JH7110_PWMDAC_DATA_MODE                 BIT(14)
++      #define JH7110_PWMDAC_DATA_SHIFT_SHIFT          15
++      #define JH7110_PWMDAC_DATA_SHIFT_MASK           GENMASK(17, 15)
++
++enum JH7110_PWMDAC_SHIFT_VAL {
++      PWMDAC_SHIFT_8 = 0,
++      PWMDAC_SHIFT_10,
++};
++
++enum JH7110_PWMDAC_DUTY_CYCLE_VAL {
++      PWMDAC_CYCLE_LEFT = 0,
++      PWMDAC_CYCLE_RIGHT,
++      PWMDAC_CYCLE_CENTER,
++};
++
++enum JH7110_PWMDAC_CNT_N_VAL {
++      PWMDAC_SAMPLE_CNT_1 = 1,
++      PWMDAC_SAMPLE_CNT_2,
++      PWMDAC_SAMPLE_CNT_3,
++      PWMDAC_SAMPLE_CNT_512 = 512, /* max */
++};
++
++enum JH7110_PWMDAC_DATA_CHANGE_VAL {
++      NO_CHANGE = 0,
++      CHANGE,
++};
++
++enum JH7110_PWMDAC_DATA_MODE_VAL {
++      UNSIGNED_DATA = 0,
++      INVERTER_DATA_MSB,
++};
++
++enum JH7110_PWMDAC_DATA_SHIFT_VAL {
++      PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_1,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_2,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_3,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_4,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_5,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_6,
++      PWMDAC_DATA_LEFT_SHIFT_BIT_7,
++};
++
++struct jh7110_pwmdac_cfg {
++      enum JH7110_PWMDAC_SHIFT_VAL shift;
++      enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle;
++      u16 cnt_n;
++      enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change;
++      enum JH7110_PWMDAC_DATA_MODE_VAL data_mode;
++      enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift;
++};
++
++struct jh7110_pwmdac_dev {
++      void __iomem *base;
++      resource_size_t mapbase;
++      struct jh7110_pwmdac_cfg cfg;
++
++      struct clk_bulk_data clks[2];
++      struct reset_control *rst_apb;
++      struct device *dev;
++      struct snd_dmaengine_dai_dma_data play_dma_data;
++      u32 saved_ctrl;
++};
++
++static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val)
++{
++      writel(val, io_base + reg);
++}
++
++static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg)
++{
++      return readl(io_base + reg);
++}
++
++static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      if (enable)
++              value |= JH7110_PWMDAC_ENABLE;
++      else
++              value &= ~JH7110_PWMDAC_ENABLE;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      if (dev->cfg.shift == PWMDAC_SHIFT_8)
++              value &= ~JH7110_PWMDAC_SHIFT;
++      else if (dev->cfg.shift == PWMDAC_SHIFT_10)
++              value |= JH7110_PWMDAC_SHIFT;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK;
++      value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      value &= ~JH7110_PWMDAC_CNT_N_MASK;
++      value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      if (dev->cfg.data_change == NO_CHANGE)
++              value &= ~JH7110_PWMDAC_DATA_CHANGE;
++      else if (dev->cfg.data_change == CHANGE)
++              value |= JH7110_PWMDAC_DATA_CHANGE;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      if (dev->cfg.data_mode == UNSIGNED_DATA)
++              value &= ~JH7110_PWMDAC_DATA_MODE;
++      else if (dev->cfg.data_mode == INVERTER_DATA_MSB)
++              value |= JH7110_PWMDAC_DATA_MODE;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev)
++{
++      u32 value;
++
++      value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL);
++      value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK;
++      value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT;
++
++      jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value);
++}
++
++static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev)
++{
++      jh7110_pwmdac_set_shift(dev);
++      jh7110_pwmdac_set_duty_cycle(dev);
++      jh7110_pwmdac_set_cnt_n(dev);
++      jh7110_pwmdac_set_enable(dev, true);
++
++      jh7110_pwmdac_set_data_change(dev);
++      jh7110_pwmdac_set_data_mode(dev);
++      jh7110_pwmdac_set_data_shift(dev);
++}
++
++static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev)
++{
++      jh7110_pwmdac_set_enable(dev, false);
++}
++
++static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream,
++                               struct snd_soc_dai *dai)
++{
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++      struct snd_soc_dai_link *dai_link = rtd->dai_link;
++
++      dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
++
++      return 0;
++}
++
++static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream,
++                                 struct snd_pcm_hw_params *params,
++                                 struct snd_soc_dai *dai)
++{
++      struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
++      unsigned long core_clk_rate;
++      int ret;
++
++      switch (params_rate(params)) {
++      case 8000:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3;
++              core_clk_rate = 6144000;
++              break;
++      case 11025:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2;
++              core_clk_rate = 5644800;
++              break;
++      case 16000:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3;
++              core_clk_rate = 12288000;
++              break;
++      case 22050:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++              core_clk_rate = 5644800;
++              break;
++      case 32000:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++              core_clk_rate = 8192000;
++              break;
++      case 44100:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++              core_clk_rate = 11289600;
++              break;
++      case 48000:
++              dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++              core_clk_rate = 12288000;
++              break;
++      default:
++              dev_err(dai->dev, "%d rate not supported\n",
++                      params_rate(params));
++              return -EINVAL;
++      }
++
++      switch (params_channels(params)) {
++      case 1:
++              dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
++              break;
++      case 2:
++              dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++              break;
++      default:
++              dev_err(dai->dev, "%d channels not supported\n",
++                      params_channels(params));
++              return -EINVAL;
++      }
++
++      /*
++       * The clock rate always rounds down when using clk_set_rate()
++       * so increase the rate a bit
++       */
++      core_clk_rate += 64;
++      jh7110_pwmdac_set(dev);
++
++      ret = clk_set_rate(dev->clks[1].clk, core_clk_rate);
++      if (ret)
++              return dev_err_probe(dai->dev, ret,
++                                   "failed to set rate %lu for core clock\n",
++                                   core_clk_rate);
++
++      return 0;
++}
++
++static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd,
++                               struct snd_soc_dai *dai)
++{
++      struct jh7110_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);
++      int ret = 0;
++
++      switch (cmd) {
++      case SNDRV_PCM_TRIGGER_START:
++      case SNDRV_PCM_TRIGGER_RESUME:
++      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++              jh7110_pwmdac_set(dev);
++              break;
++
++      case SNDRV_PCM_TRIGGER_STOP:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++              jh7110_pwmdac_stop(dev);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable)
++{
++      int ret;
++
++      if (enable) {
++              ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks);
++              if (ret)
++                      return dev_err_probe(dev->dev, ret,
++                                           "failed to enable pwmdac clocks\n");
++
++              ret = reset_control_deassert(dev->rst_apb);
++              if (ret) {
++                      dev_err(dev->dev, "failed to deassert pwmdac apb reset\n");
++                      goto err_rst_apb;
++              }
++      } else {
++              clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks);
++      }
++
++      return 0;
++
++err_rst_apb:
++      clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks);
++
++      return ret;
++}
++
++static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai)
++{
++      struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
++
++      snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
++      snd_soc_dai_set_drvdata(dai, dev);
++
++      return 0;
++}
++
++static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = {
++      .startup        = jh7110_pwmdac_startup,
++      .hw_params      = jh7110_pwmdac_hw_params,
++      .trigger        = jh7110_pwmdac_trigger,
++};
++
++static const struct snd_soc_component_driver jh7110_pwmdac_component = {
++      .name           = "jh7110-pwmdac",
++};
++
++static struct snd_soc_dai_driver jh7110_pwmdac_dai = {
++      .name           = "jh7110-pwmdac",
++      .id             = 0,
++      .probe          = jh7110_pwmdac_dai_probe,
++      .playback = {
++              .channels_min = 1,
++              .channels_max = 2,
++              .rates = SNDRV_PCM_RATE_8000_48000,
++              .formats = SNDRV_PCM_FMTBIT_S16_LE,
++      },
++      .ops = &jh7110_pwmdac_dai_ops,
++};
++
++static int jh7110_pwmdac_runtime_suspend(struct device *dev)
++{
++      struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++
++      return jh7110_pwmdac_crg_enable(pwmdac, false);
++}
++
++static int jh7110_pwmdac_runtime_resume(struct device *dev)
++{
++      struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++
++      return jh7110_pwmdac_crg_enable(pwmdac, true);
++}
++
++static int jh7110_pwmdac_system_suspend(struct device *dev)
++{
++      struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++
++      /* save the CTRL register value */
++      pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base,
++                                                  JH7110_PWMDAC_CTRL);
++      return pm_runtime_force_suspend(dev);
++}
++
++static int jh7110_pwmdac_system_resume(struct device *dev)
++{
++      struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev);
++      int ret;
++
++      ret = pm_runtime_force_resume(dev);
++      if (ret)
++              return ret;
++
++      /* restore the CTRL register value */
++      jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL,
++                              pwmdac->saved_ctrl);
++      return 0;
++}
++
++static const struct dev_pm_ops jh7110_pwmdac_pm_ops = {
++      RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend,
++                     jh7110_pwmdac_runtime_resume, NULL)
++      SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend,
++                          jh7110_pwmdac_system_resume)
++};
++
++static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev)
++{
++      dev->cfg.shift = PWMDAC_SHIFT_8;
++      dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER;
++      dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1;
++      dev->cfg.data_change = NO_CHANGE;
++      dev->cfg.data_mode = INVERTER_DATA_MSB;
++      dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0;
++
++      dev->play_dma_data.addr = dev->mapbase + JH7110_PWMDAC_WDATA;
++      dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++      dev->play_dma_data.fifo_size = 1;
++      dev->play_dma_data.maxburst = 16;
++}
++
++static int jh7110_pwmdac_probe(struct platform_device *pdev)
++{
++      struct jh7110_pwmdac_dev *dev;
++      struct resource *res;
++      int ret;
++
++      dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
++      if (!dev)
++              return -ENOMEM;
++
++      dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
++      if (IS_ERR(dev->base))
++              return PTR_ERR(dev->base);
++
++      dev->mapbase = res->start;
++
++      dev->clks[0].id = "apb";
++      dev->clks[1].id = "core";
++
++      ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks);
++      if (ret)
++              return dev_err_probe(&pdev->dev, ret,
++                                   "failed to get pwmdac clocks\n");
++
++      dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL);
++      if (IS_ERR(dev->rst_apb))
++              return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb),
++                                   "failed to get pwmdac apb reset\n");
++
++      jh7110_pwmdac_init_params(dev);
++
++      dev->dev = &pdev->dev;
++      dev_set_drvdata(&pdev->dev, dev);
++      ret = devm_snd_soc_register_component(&pdev->dev,
++                                            &jh7110_pwmdac_component,
++                                            &jh7110_pwmdac_dai, 1);
++      if (ret)
++              return dev_err_probe(&pdev->dev, ret, "failed to register dai\n");
++
++      ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
++      if (ret)
++              return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n");
++
++      pm_runtime_enable(dev->dev);
++      if (!pm_runtime_enabled(&pdev->dev)) {
++              ret = jh7110_pwmdac_runtime_resume(&pdev->dev);
++              if (ret)
++                      goto err_pm_disable;
++      }
++
++      return 0;
++
++err_pm_disable:
++      pm_runtime_disable(&pdev->dev);
++
++      return ret;
++}
++
++static int jh7110_pwmdac_remove(struct platform_device *pdev)
++{
++      pm_runtime_disable(&pdev->dev);
++      return 0;
++}
++
++static const struct of_device_id jh7110_pwmdac_of_match[] = {
++      { .compatible = "starfive,jh7110-pwmdac" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match);
++
++static struct platform_driver jh7110_pwmdac_driver = {
++      .driver         = {
++              .name   = "jh7110-pwmdac",
++              .of_match_table = jh7110_pwmdac_of_match,
++              .pm = pm_ptr(&jh7110_pwmdac_pm_ops),
++      },
++      .probe          = jh7110_pwmdac_probe,
++      .remove         = jh7110_pwmdac_remove,
++};
++module_platform_driver(jh7110_pwmdac_driver);
++
++MODULE_AUTHOR("Jenny Zhang");
++MODULE_AUTHOR("Curry Zhang");
++MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
++MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
++MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch b/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch
new file mode 100644 (file)
index 0000000..4833067
--- /dev/null
@@ -0,0 +1,32 @@
+From c6b693f990e1f89ab5af0a139da31401b8cda74f Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@kernel.org>
+Date: Mon, 11 Sep 2023 23:48:39 +0100
+Subject: [PATCH 037/116] ASoC: Update jh7110 PWM DAC for ops move
+
+For some reason the JH7110 PWM DAC driver made it through build testing
+in spite of not being updated for the move of probe() to the ops struct.
+Make the required update.
+
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/starfive/jh7110_pwmdac.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/sound/soc/starfive/jh7110_pwmdac.c
++++ b/sound/soc/starfive/jh7110_pwmdac.c
+@@ -357,6 +357,7 @@ static int jh7110_pwmdac_dai_probe(struc
+ }
+ static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = {
++      .probe          = jh7110_pwmdac_dai_probe,
+       .startup        = jh7110_pwmdac_startup,
+       .hw_params      = jh7110_pwmdac_hw_params,
+       .trigger        = jh7110_pwmdac_trigger,
+@@ -369,7 +370,6 @@ static const struct snd_soc_component_dr
+ static struct snd_soc_dai_driver jh7110_pwmdac_dai = {
+       .name           = "jh7110-pwmdac",
+       .id             = 0,
+-      .probe          = jh7110_pwmdac_dai_probe,
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 2,
diff --git a/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch b/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch
new file mode 100644 (file)
index 0000000..9b5f553
--- /dev/null
@@ -0,0 +1,52 @@
+From 312c3c407c363f0ec7417d2d389cbe936c503729 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Sat, 14 Oct 2023 00:19:49 +0200
+Subject: [PATCH 038/116] ASoC: starfive/jh7110-pwmdac: Convert to platform
+ remove callback returning void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is ignored (apart
+from emitting a warning) and this typically results in resource leaks.
+
+To improve here there is a quest to make the remove callback return
+void. In the first step of this quest all drivers are converted to
+.remove_new(), which already returns void. Eventually after all drivers
+are converted, .remove_new() will be renamed to .remove().
+
+Trivially convert this driver from always returning zero in the remove
+callback to the void returning variant.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Link: https://lore.kernel.org/r/20231013221945.1489203-12-u.kleine-koenig@pengutronix.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/starfive/jh7110_pwmdac.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/starfive/jh7110_pwmdac.c
++++ b/sound/soc/starfive/jh7110_pwmdac.c
+@@ -498,10 +498,9 @@ err_pm_disable:
+       return ret;
+ }
+-static int jh7110_pwmdac_remove(struct platform_device *pdev)
++static void jh7110_pwmdac_remove(struct platform_device *pdev)
+ {
+       pm_runtime_disable(&pdev->dev);
+-      return 0;
+ }
+ static const struct of_device_id jh7110_pwmdac_of_match[] = {
+@@ -517,7 +516,7 @@ static struct platform_driver jh7110_pwm
+               .pm = pm_ptr(&jh7110_pwmdac_pm_ops),
+       },
+       .probe          = jh7110_pwmdac_probe,
+-      .remove         = jh7110_pwmdac_remove,
++      .remove_new     = jh7110_pwmdac_remove,
+ };
+ module_platform_driver(jh7110_pwmdac_driver);
diff --git a/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch b/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch
new file mode 100644 (file)
index 0000000..bcc9af2
--- /dev/null
@@ -0,0 +1,35 @@
+From 8d84bac6d7471ba2e29b33d19a2ef887822e9cce Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:25 +0100
+Subject: [PATCH 039/116] dt-bindings: power: Add power-domain header for
+ JH7110
+
+Add power-domain header for JH7110 SoC, it can use to operate dphy
+power.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-grumbly-rewrite-34c85539f2ed@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ include/dt-bindings/power/starfive,jh7110-pmu.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/include/dt-bindings/power/starfive,jh7110-pmu.h
++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+ /*
+- * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
+  * Author: Walker Chen <walker.chen@starfivetech.com>
+  */
+ #ifndef __DT_BINDINGS_POWER_JH7110_POWER_H__
+@@ -14,4 +14,7 @@
+ #define JH7110_PD_ISP         5
+ #define JH7110_PD_VENC                6
++#define JH7110_PD_DPHY_TX     0
++#define JH7110_PD_DPHY_RX     1
++
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch b/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch
new file mode 100644 (file)
index 0000000..335193f
--- /dev/null
@@ -0,0 +1,31 @@
+From 0ac8e8b0e65d242f455401df0cc6c6d4772216e6 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:26 +0100
+Subject: [PATCH 040/116] pmdomain: starfive: Replace SOC_STARFIVE with
+ ARCH_STARFIVE
+
+Using ARCH_FOO symbol is preferred than SOC_FOO.
+
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-legibly-treachery-567cffcb5604@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/soc/starfive/Kconfig | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/soc/starfive/Kconfig
++++ b/drivers/soc/starfive/Kconfig
+@@ -3,8 +3,8 @@
+ config JH71XX_PMU
+       bool "Support PMU for StarFive JH71XX Soc"
+       depends on PM
+-      depends on SOC_STARFIVE || COMPILE_TEST
+-      default SOC_STARFIVE
++      depends on ARCH_STARFIVE || COMPILE_TEST
++      default ARCH_STARFIVE
+       select PM_GENERIC_DOMAINS
+       help
+         Say 'y' here to enable support power domain support.
diff --git a/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch b/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch
new file mode 100644 (file)
index 0000000..7425dd6
--- /dev/null
@@ -0,0 +1,179 @@
+From a1ba60e35ca7f1b85243054556ecde2e259619e1 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:27 +0100
+Subject: [PATCH 041/116] pmdomain: starfive: Extract JH7110 pmu private
+ operations
+
+Move JH7110 private operation into private data of compatible. Convenient
+to add AON PMU which would not have interrupts property.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-slideshow-luckiness-38ff17de84c6@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 89 ++++++++++++++++++--------
+ 1 file changed, 62 insertions(+), 27 deletions(-)
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -51,9 +51,17 @@ struct jh71xx_domain_info {
+       u8 bit;
+ };
++struct jh71xx_pmu;
++struct jh71xx_pmu_dev;
++
+ struct jh71xx_pmu_match_data {
+       const struct jh71xx_domain_info *domain_info;
+       int num_domains;
++      unsigned int pmu_status;
++      int (*pmu_parse_irq)(struct platform_device *pdev,
++                           struct jh71xx_pmu *pmu);
++      int (*pmu_set_state)(struct jh71xx_pmu_dev *pmd,
++                           u32 mask, bool on);
+ };
+ struct jh71xx_pmu {
+@@ -79,12 +87,12 @@ static int jh71xx_pmu_get_state(struct j
+       if (!mask)
+               return -EINVAL;
+-      *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask;
++      *is_on = readl(pmu->base + pmu->match_data->pmu_status) & mask;
+       return 0;
+ }
+-static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
++static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
+ {
+       struct jh71xx_pmu *pmu = pmd->pmu;
+       unsigned long flags;
+@@ -92,22 +100,8 @@ static int jh71xx_pmu_set_state(struct j
+       u32 mode;
+       u32 encourage_lo;
+       u32 encourage_hi;
+-      bool is_on;
+       int ret;
+-      ret = jh71xx_pmu_get_state(pmd, mask, &is_on);
+-      if (ret) {
+-              dev_dbg(pmu->dev, "unable to get current state for %s\n",
+-                      pmd->genpd.name);
+-              return ret;
+-      }
+-
+-      if (is_on == on) {
+-              dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n",
+-                      pmd->genpd.name, on ? "en" : "dis");
+-              return 0;
+-      }
+-
+       spin_lock_irqsave(&pmu->lock, flags);
+       /*
+@@ -166,6 +160,29 @@ static int jh71xx_pmu_set_state(struct j
+       return 0;
+ }
++static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
++{
++      struct jh71xx_pmu *pmu = pmd->pmu;
++      const struct jh71xx_pmu_match_data *match_data = pmu->match_data;
++      bool is_on;
++      int ret;
++
++      ret = jh71xx_pmu_get_state(pmd, mask, &is_on);
++      if (ret) {
++              dev_dbg(pmu->dev, "unable to get current state for %s\n",
++                      pmd->genpd.name);
++              return ret;
++      }
++
++      if (is_on == on) {
++              dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n",
++                      pmd->genpd.name, on ? "en" : "dis");
++              return 0;
++      }
++
++      return match_data->pmu_set_state(pmd, mask, on);
++}
++
+ static int jh71xx_pmu_on(struct generic_pm_domain *genpd)
+ {
+       struct jh71xx_pmu_dev *pmd = container_of(genpd,
+@@ -226,6 +243,25 @@ static irqreturn_t jh71xx_pmu_interrupt(
+       return IRQ_HANDLED;
+ }
++static int jh7110_pmu_parse_irq(struct platform_device *pdev, struct jh71xx_pmu *pmu)
++{
++      struct device *dev = &pdev->dev;
++      int ret;
++
++      pmu->irq = platform_get_irq(pdev, 0);
++      if (pmu->irq < 0)
++              return pmu->irq;
++
++      ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt,
++                             0, pdev->name, pmu);
++      if (ret)
++              dev_err(dev, "failed to request irq\n");
++
++      jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true);
++
++      return 0;
++}
++
+ static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index)
+ {
+       struct jh71xx_pmu_dev *pmd;
+@@ -275,19 +311,18 @@ static int jh71xx_pmu_probe(struct platf
+       if (IS_ERR(pmu->base))
+               return PTR_ERR(pmu->base);
+-      pmu->irq = platform_get_irq(pdev, 0);
+-      if (pmu->irq < 0)
+-              return pmu->irq;
+-
+-      ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt,
+-                             0, pdev->name, pmu);
+-      if (ret)
+-              dev_err(dev, "failed to request irq\n");
++      spin_lock_init(&pmu->lock);
+       match_data = of_device_get_match_data(dev);
+       if (!match_data)
+               return -EINVAL;
++      ret = match_data->pmu_parse_irq(pdev, pmu);
++      if (ret) {
++              dev_err(dev, "failed to parse irq\n");
++              return ret;
++      }
++
+       pmu->genpd = devm_kcalloc(dev, match_data->num_domains,
+                                 sizeof(struct generic_pm_domain *),
+                                 GFP_KERNEL);
+@@ -307,9 +342,6 @@ static int jh71xx_pmu_probe(struct platf
+               }
+       }
+-      spin_lock_init(&pmu->lock);
+-      jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true);
+-
+       ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data);
+       if (ret) {
+               dev_err(dev, "failed to register genpd driver: %d\n", ret);
+@@ -357,6 +389,9 @@ static const struct jh71xx_domain_info j
+ static const struct jh71xx_pmu_match_data jh7110_pmu = {
+       .num_domains = ARRAY_SIZE(jh7110_power_domains),
+       .domain_info = jh7110_power_domains,
++      .pmu_status = JH71XX_PMU_CURR_POWER_MODE,
++      .pmu_parse_irq = jh7110_pmu_parse_irq,
++      .pmu_set_state = jh7110_pmu_set_state,
+ };
+ static const struct of_device_id jh71xx_pmu_of_match[] = {
diff --git a/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch b/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch
new file mode 100644 (file)
index 0000000..2563d54
--- /dev/null
@@ -0,0 +1,122 @@
+From 1bf849b606d0b4cae643f96685d3d3981643683d Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 13 Sep 2023 14:54:28 +0100
+Subject: [PATCH 042/116] pmdomain: starfive: Add JH7110 AON PMU support
+
+Add AON PMU for StarFive JH7110 SoC. It can be used to turn on/off the
+dphy rx/tx power switch.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20230913-dude-imprecise-fc32622bc947@spud
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 57 +++++++++++++++++++++++---
+ 1 file changed, 52 insertions(+), 5 deletions(-)
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -2,7 +2,7 @@
+ /*
+  * StarFive JH71XX PMU (Power Management Unit) Controller Driver
+  *
+- * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
+  */
+ #include <linux/interrupt.h>
+@@ -24,6 +24,9 @@
+ #define JH71XX_PMU_EVENT_STATUS               0x88
+ #define JH71XX_PMU_INT_STATUS         0x8C
++/* aon pmu register offset */
++#define JH71XX_AON_PMU_SWITCH         0x00
++
+ /* sw encourage cfg */
+ #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05
+ #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50
+@@ -160,6 +163,26 @@ static int jh7110_pmu_set_state(struct j
+       return 0;
+ }
++static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
++{
++      struct jh71xx_pmu *pmu = pmd->pmu;
++      unsigned long flags;
++      u32 val;
++
++      spin_lock_irqsave(&pmu->lock, flags);
++      val = readl(pmu->base + JH71XX_AON_PMU_SWITCH);
++
++      if (on)
++              val |= mask;
++      else
++              val &= ~mask;
++
++      writel(val, pmu->base + JH71XX_AON_PMU_SWITCH);
++      spin_unlock_irqrestore(&pmu->lock, flags);
++
++      return 0;
++}
++
+ static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on)
+ {
+       struct jh71xx_pmu *pmu = pmd->pmu;
+@@ -317,10 +340,12 @@ static int jh71xx_pmu_probe(struct platf
+       if (!match_data)
+               return -EINVAL;
+-      ret = match_data->pmu_parse_irq(pdev, pmu);
+-      if (ret) {
+-              dev_err(dev, "failed to parse irq\n");
+-              return ret;
++      if (match_data->pmu_parse_irq) {
++              ret = match_data->pmu_parse_irq(pdev, pmu);
++              if (ret) {
++                      dev_err(dev, "failed to parse irq\n");
++                      return ret;
++              }
+       }
+       pmu->genpd = devm_kcalloc(dev, match_data->num_domains,
+@@ -394,11 +419,32 @@ static const struct jh71xx_pmu_match_dat
+       .pmu_set_state = jh7110_pmu_set_state,
+ };
++static const struct jh71xx_domain_info jh7110_aon_power_domains[] = {
++      [JH7110_PD_DPHY_TX] = {
++              .name = "DPHY-TX",
++              .bit = 30,
++      },
++      [JH7110_PD_DPHY_RX] = {
++              .name = "DPHY-RX",
++              .bit = 31,
++      },
++};
++
++static const struct jh71xx_pmu_match_data jh7110_aon_pmu = {
++      .num_domains = ARRAY_SIZE(jh7110_aon_power_domains),
++      .domain_info = jh7110_aon_power_domains,
++      .pmu_status = JH71XX_AON_PMU_SWITCH,
++      .pmu_set_state = jh7110_aon_pmu_set_state,
++};
++
+ static const struct of_device_id jh71xx_pmu_of_match[] = {
+       {
+               .compatible = "starfive,jh7110-pmu",
+               .data = (void *)&jh7110_pmu,
+       }, {
++              .compatible = "starfive,jh7110-aon-syscon",
++              .data = (void *)&jh7110_aon_pmu,
++      }, {
+               /* sentinel */
+       }
+ };
+@@ -414,5 +460,6 @@ static struct platform_driver jh71xx_pmu
+ builtin_platform_driver(jh71xx_pmu_driver);
+ MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
++MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
+ MODULE_DESCRIPTION("StarFive JH71XX PMU Driver");
+ MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch b/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch
new file mode 100644 (file)
index 0000000..52be89f
--- /dev/null
@@ -0,0 +1,36 @@
+From dff6605dcd1fc1e2af1437e59187a6f71ce389cd Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson@linaro.org>
+Date: Mon, 11 Sep 2023 17:22:15 +0200
+Subject: [PATCH 043/116] pmdomain: Prepare to move Kconfig files into the
+ pmdomain subsystem
+
+Rather than having the various Kconfig files for the genpd providers
+sprinkled across subsystems, let's prepare to move them into the pmdomain
+subsystem along with the implementations.
+
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/Kconfig          | 2 ++
+ drivers/pmdomain/Kconfig | 4 ++++
+ 2 files changed, 6 insertions(+)
+ create mode 100644 drivers/pmdomain/Kconfig
+
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -175,6 +175,8 @@ source "drivers/soundwire/Kconfig"
+ source "drivers/soc/Kconfig"
++source "drivers/pmdomain/Kconfig"
++
+ source "drivers/devfreq/Kconfig"
+ source "drivers/extcon/Kconfig"
+--- /dev/null
++++ b/drivers/pmdomain/Kconfig
+@@ -0,0 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0-only
++menu "PM Domains"
++
++endmenu
diff --git a/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch b/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch
new file mode 100644 (file)
index 0000000..909f4cc
--- /dev/null
@@ -0,0 +1,69 @@
+From de12fe43dbd0ea9fa980ffa05822bd7fd5eed330 Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson@linaro.org>
+Date: Tue, 12 Sep 2023 13:31:44 +0200
+Subject: [PATCH 044/116] pmdomain: starfive: Move Kconfig file to the pmdomain
+ subsystem
+
+The Kconfig belongs closer to the corresponding implementation, hence let's
+move it from the soc subsystem to the pmdomain subsystem.
+
+Cc: Walker Chen <walker.chen@starfivetech.com>
+Cc: Conor Dooley <conor@kernel.org>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/Kconfig                   | 2 ++
+ drivers/{soc => pmdomain}/starfive/Kconfig | 0
+ drivers/soc/Kconfig                        | 1 -
+ 3 files changed, 2 insertions(+), 1 deletion(-)
+ rename drivers/{soc => pmdomain}/starfive/Kconfig (100%)
+
+--- a/drivers/pmdomain/Kconfig
++++ b/drivers/pmdomain/Kconfig
+@@ -1,4 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ menu "PM Domains"
++source "drivers/pmdomain/starfive/Kconfig"
++
+ endmenu
+--- a/drivers/soc/Kconfig
++++ b/drivers/soc/Kconfig
+@@ -24,7 +24,6 @@ source "drivers/soc/renesas/Kconfig"
+ source "drivers/soc/rockchip/Kconfig"
+ source "drivers/soc/samsung/Kconfig"
+ source "drivers/soc/sifive/Kconfig"
+-source "drivers/soc/starfive/Kconfig"
+ source "drivers/soc/sunxi/Kconfig"
+ source "drivers/soc/tegra/Kconfig"
+ source "drivers/soc/ti/Kconfig"
+--- /dev/null
++++ b/drivers/pmdomain/starfive/Kconfig
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0
++
++config JH71XX_PMU
++      bool "Support PMU for StarFive JH71XX Soc"
++      depends on PM
++      depends on ARCH_STARFIVE || COMPILE_TEST
++      default ARCH_STARFIVE
++      select PM_GENERIC_DOMAINS
++      help
++        Say 'y' here to enable support power domain support.
++        In order to meet low power requirements, a Power Management Unit (PMU)
++        is designed for controlling power resources in StarFive JH71XX SoCs.
+--- a/drivers/soc/starfive/Kconfig
++++ /dev/null
+@@ -1,12 +0,0 @@
+-# SPDX-License-Identifier: GPL-2.0
+-
+-config JH71XX_PMU
+-      bool "Support PMU for StarFive JH71XX Soc"
+-      depends on PM
+-      depends on ARCH_STARFIVE || COMPILE_TEST
+-      default ARCH_STARFIVE
+-      select PM_GENERIC_DOMAINS
+-      help
+-        Say 'y' here to enable support power domain support.
+-        In order to meet low power requirements, a Power Management Unit (PMU)
+-        is designed for controlling power resources in StarFive JH71XX SoCs.
diff --git a/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch b/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch
new file mode 100644 (file)
index 0000000..c6e0012
--- /dev/null
@@ -0,0 +1,31 @@
+From cac9ce9c7f388a741389b1ec47af65420254db55 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 27 Sep 2023 06:07:33 -0700
+Subject: [PATCH 045/116] dt-bindings: power: Update prefixes for AON power
+ domain
+
+Use "JH7110_AON_PD_" prefix for AON power domain for JH7110 SoC.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://lore.kernel.org/r/20230927130734.9921-2-changhuang.liang@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ include/dt-bindings/power/starfive,jh7110-pmu.h | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/include/dt-bindings/power/starfive,jh7110-pmu.h
++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h
+@@ -14,7 +14,8 @@
+ #define JH7110_PD_ISP         5
+ #define JH7110_PD_VENC                6
+-#define JH7110_PD_DPHY_TX     0
+-#define JH7110_PD_DPHY_RX     1
++/* AON Power Domain */
++#define JH7110_AON_PD_DPHY_TX 0
++#define JH7110_AON_PD_DPHY_RX 1
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch b/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch
new file mode 100644 (file)
index 0000000..5cb89ae
--- /dev/null
@@ -0,0 +1,32 @@
+From 3ea89ffbd6cc5a15acca6bc2130572f8bd85b9d4 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 27 Sep 2023 06:07:34 -0700
+Subject: [PATCH 046/116] pmdomain: starfive: Update prefixes for AON power
+ domain
+
+Use "JH7110_AON_PD_" prefix for AON power doamin for JH7110 SoC.
+
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Link: https://lore.kernel.org/r/20230927130734.9921-3-changhuang.liang@starfivetech.com
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -420,11 +420,11 @@ static const struct jh71xx_pmu_match_dat
+ };
+ static const struct jh71xx_domain_info jh7110_aon_power_domains[] = {
+-      [JH7110_PD_DPHY_TX] = {
++      [JH7110_AON_PD_DPHY_TX] = {
+               .name = "DPHY-TX",
+               .bit = 30,
+       },
+-      [JH7110_PD_DPHY_RX] = {
++      [JH7110_AON_PD_DPHY_RX] = {
+               .name = "DPHY-RX",
+               .bit = 31,
+       },
diff --git a/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch b/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch
new file mode 100644 (file)
index 0000000..782e71a
--- /dev/null
@@ -0,0 +1,30 @@
+From e7e3d62b7a470ddf15e30574232b52b2e23ba606 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Mon, 21 Aug 2023 22:41:50 +0800
+Subject: [PATCH 047/116] riscv: dts: starfive: pinfunc: Fix the pins name of
+ I2STX1
+
+These pins are actually I2STX1 clock input, not I2STX0,
+so their names should be changed.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Reviewed-by: Walker Chen <walker.chen@starfivetech.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-pinfunc.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h
++++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h
+@@ -240,8 +240,8 @@
+ #define GPI_SYS_MCLK_EXT                      30
+ #define GPI_SYS_I2SRX_BCLK                    31
+ #define GPI_SYS_I2SRX_LRCK                    32
+-#define GPI_SYS_I2STX0_BCLK                   33
+-#define GPI_SYS_I2STX0_LRCK                   34
++#define GPI_SYS_I2STX1_BCLK                   33
++#define GPI_SYS_I2STX1_LRCK                   34
+ #define GPI_SYS_TDM_CLK                               35
+ #define GPI_SYS_TDM_RXD                               36
+ #define GPI_SYS_TDM_SYNC                      37
diff --git a/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch b/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch
new file mode 100644 (file)
index 0000000..ad6626c
--- /dev/null
@@ -0,0 +1,549 @@
+From a3d3f611f31fa2dca3deefa7cd443abca02e03fa Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 11 Apr 2023 16:31:15 +0800
+Subject: [PATCH 048/116] riscv: dts: starfive: Add full support (except VIN
+ and VOUT) for JH7110 and VisionFive 2 board
+
+Merge all StarFive dts patches together except VIN and VOUT.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../jh7110-starfive-visionfive-2.dtsi         | 199 +++++++++++++++
+ arch/riscv/boot/dts/starfive/jh7110.dtsi      | 233 ++++++++++++++++++
+ 2 files changed, 432 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -19,6 +19,8 @@
+               i2c6 = &i2c6;
+               mmc0 = &mmc0;
+               mmc1 = &mmc1;
++              pcie0 = &pcie0;
++              pcie1 = &pcie1;
+               serial0 = &uart0;
+       };
+@@ -40,6 +42,33 @@
+               gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
+               priority = <224>;
+       };
++
++      pwmdac_codec: pwmdac-codec {
++              compatible = "linux,spdif-dit";
++              #sound-dai-cells = <0>;
++      };
++
++      sound-pwmdac {
++              compatible = "simple-audio-card";
++              simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "left_j";
++                      bitclock-master = <&sndcpu0>;
++                      frame-master = <&sndcpu0>;
++
++                      sndcpu0: cpu {
++                              sound-dai = <&pwmdac>;
++                      };
++
++                      codec {
++                              sound-dai = <&pwmdac_codec>;
++                      };
++              };
++      };
+ };
+ &dvp_clk {
+@@ -202,8 +231,28 @@
+       status = "okay";
+ };
++&i2srx {
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2srx_pins>;
++      status = "okay";
++};
++
++&i2stx0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&mclk_ext_pins>;
++      status = "okay";
++};
++
++&i2stx1 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2stx1_pins>;
++      status = "okay";
++};
++
+ &mmc0 {
+       max-frequency = <100000000>;
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
+       bus-width = <8>;
+       cap-mmc-highspeed;
+       mmc-ddr-1_8v;
+@@ -220,6 +269,8 @@
+ &mmc1 {
+       max-frequency = <100000000>;
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>;
++      assigned-clock-rates = <50000000>;
+       bus-width = <4>;
+       no-sdio;
+       no-mmc;
+@@ -231,6 +282,34 @@
+       status = "okay";
+ };
++&pcie0 {
++      perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>;
++      phys = <&pciephy0>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&pcie0_pins>;
++      status = "okay";
++};
++
++&pcie1 {
++      perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>;
++      phys = <&pciephy1>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&pcie1_pins>;
++      status = "okay";
++};
++
++&pwm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwm_pins>;
++      status = "okay";
++};
++
++&pwmdac {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwmdac_pins>;
++      status = "okay";
++};
++
+ &qspi {
+       #address-cells = <1>;
+       #size-cells = <0>;
+@@ -336,6 +415,46 @@
+               };
+       };
++      i2srx_pins: i2srx-0 {
++              clk-sd-pins {
++                      pinmux = <GPIOMUX(38, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2SRX_BCLK)>,
++                               <GPIOMUX(63, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2SRX_LRCK)>,
++                               <GPIOMUX(38, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2STX1_BCLK)>,
++                               <GPIOMUX(63, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2STX1_LRCK)>,
++                               <GPIOMUX(61, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2SRX_SDIN0)>;
++                      input-enable;
++              };
++      };
++
++      i2stx1_pins: i2stx1-0 {
++              sd-pins {
++                      pinmux = <GPIOMUX(44, GPOUT_SYS_I2STX1_SDO0,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++              };
++      };
++
++      mclk_ext_pins: mclk-ext-0 {
++              mclk-ext-pins {
++                      pinmux = <GPIOMUX(4, GPOUT_LOW,
++                                           GPOEN_DISABLE,
++                                           GPI_SYS_MCLK_EXT)>;
++                      input-enable;
++              };
++      };
++
+       mmc0_pins: mmc0-0 {
+                rst-pins {
+                       pinmux = <GPIOMUX(62, GPOUT_SYS_SDIO0_RST,
+@@ -400,6 +519,86 @@
+                       slew-rate = <0>;
+               };
+       };
++
++      pcie0_pins: pcie0-0 {
++              clkreq-pins {
++                      pinmux = <GPIOMUX(27, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_NONE)>;
++                      bias-pull-down;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              wake-pins {
++                      pinmux = <GPIOMUX(32, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_NONE)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++      };
++
++      pcie1_pins: pcie1-0 {
++              clkreq-pins {
++                      pinmux = <GPIOMUX(29, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_NONE)>;
++                      bias-pull-down;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              wake-pins {
++                      pinmux = <GPIOMUX(21, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_NONE)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++      };
++
++      pwm_pins: pwm-0 {
++              pwm-pins {
++                      pinmux = <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL0,
++                                            GPOEN_SYS_PWM0_CHANNEL0,
++                                            GPI_NONE)>,
++                               <GPIOMUX(59, GPOUT_SYS_PWM_CHANNEL1,
++                                            GPOEN_SYS_PWM0_CHANNEL1,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++      };
++
++      pwmdac_pins: pwmdac-0 {
++              pwmdac-pins {
++                      pinmux = <GPIOMUX(33, GPOUT_SYS_PWMDAC_LEFT,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(34, GPOUT_SYS_PWMDAC_RIGHT,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <2>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++      };
+       spi0_pins: spi0-0 {
+               mosi-pins {
+--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
+@@ -244,6 +244,7 @@
+               clock-output-names = "dvp_clk";
+               #clock-cells = <0>;
+       };
++
+       gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock {
+               compatible = "fixed-clock";
+               clock-output-names = "gmac0_rgmii_rxin";
+@@ -512,6 +513,43 @@
+                       status = "disabled";
+               };
++              pwmdac: pwmdac@100b0000 {
++                      compatible = "starfive,jh7110-pwmdac";
++                      reg = <0x0 0x100b0000 0x0 0x1000>;
++                      clocks = <&syscrg JH7110_SYSCLK_PWMDAC_APB>,
++                               <&syscrg JH7110_SYSCLK_PWMDAC_CORE>;
++                      clock-names = "apb", "core";
++                      resets = <&syscrg JH7110_SYSRST_PWMDAC_APB>;
++                      dmas = <&dma 22>;
++                      dma-names = "tx";
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
++              i2srx: i2s@100e0000 {
++                      compatible = "starfive,jh7110-i2srx";
++                      reg = <0x0 0x100e0000 0x0 0x1000>;
++                      clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>,
++                               <&syscrg JH7110_SYSCLK_I2SRX_APB>,
++                               <&syscrg JH7110_SYSCLK_MCLK>,
++                               <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++                               <&mclk_ext>,
++                               <&syscrg JH7110_SYSCLK_I2SRX_BCLK>,
++                               <&syscrg JH7110_SYSCLK_I2SRX_LRCK>,
++                               <&i2srx_bclk_ext>,
++                               <&i2srx_lrck_ext>;
++                      clock-names = "i2sclk", "apb", "mclk",
++                                    "mclk_inner", "mclk_ext", "bclk",
++                                    "lrck", "bclk_ext", "lrck_ext";
++                      resets = <&syscrg JH7110_SYSRST_I2SRX_APB>,
++                               <&syscrg JH7110_SYSRST_I2SRX_BCLK>;
++                      dmas = <0>, <&dma 24>;
++                      dma-names = "tx", "rx";
++                      starfive,syscon = <&sys_syscon 0x18 0x2>;
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
+               usb0: usb@10100000 {
+                       compatible = "starfive,jh7110-usb";
+                       ranges = <0x0 0x0 0x10100000 0x100000>;
+@@ -736,6 +774,56 @@
+                       status = "disabled";
+               };
++              i2stx0: i2s@120b0000 {
++                      compatible = "starfive,jh7110-i2stx0";
++                      reg = <0x0 0x120b0000 0x0 0x1000>;
++                      clocks = <&syscrg JH7110_SYSCLK_I2STX0_BCLK_MST>,
++                               <&syscrg JH7110_SYSCLK_I2STX0_APB>,
++                               <&syscrg JH7110_SYSCLK_MCLK>,
++                               <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++                               <&mclk_ext>;
++                      clock-names = "i2sclk", "apb", "mclk",
++                                    "mclk_inner","mclk_ext";
++                      resets = <&syscrg JH7110_SYSRST_I2STX0_APB>,
++                               <&syscrg JH7110_SYSRST_I2STX0_BCLK>;
++                      dmas = <&dma 47>;
++                      dma-names = "tx";
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
++              i2stx1: i2s@120c0000 {
++                      compatible = "starfive,jh7110-i2stx1";
++                      reg = <0x0 0x120c0000 0x0 0x1000>;
++                      clocks = <&syscrg JH7110_SYSCLK_I2STX1_BCLK_MST>,
++                               <&syscrg JH7110_SYSCLK_I2STX1_APB>,
++                               <&syscrg JH7110_SYSCLK_MCLK>,
++                               <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++                               <&mclk_ext>,
++                               <&syscrg JH7110_SYSCLK_I2STX1_BCLK>,
++                               <&syscrg JH7110_SYSCLK_I2STX1_LRCK>,
++                               <&i2stx_bclk_ext>,
++                               <&i2stx_lrck_ext>;
++                      clock-names = "i2sclk", "apb", "mclk",
++                                    "mclk_inner", "mclk_ext", "bclk",
++                                    "lrck", "bclk_ext", "lrck_ext";
++                      resets = <&syscrg JH7110_SYSRST_I2STX1_APB>,
++                               <&syscrg JH7110_SYSRST_I2STX1_BCLK>;
++                      dmas = <&dma 48>;
++                      dma-names = "tx";
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
++              pwm: pwm@120d0000 {
++                      compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
++                      reg = <0x0 0x120d0000 0x0 0x10000>;
++                      clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
++                      resets = <&syscrg JH7110_SYSRST_PWM_APB>;
++                      #pwm-cells = <3>;
++                      status = "disabled";
++              };
++
+               sfctemp: temperature-sensor@120e0000 {
+                       compatible = "starfive,jh7110-temp";
+                       reg = <0x0 0x120e0000 0x0 0x10000>;
+@@ -811,6 +899,26 @@
+                       #gpio-cells = <2>;
+               };
++              timer@13050000 {
++                      compatible = "starfive,jh7110-timer";
++                      reg = <0x0 0x13050000 0x0 0x10000>;
++                      interrupts = <69>, <70>, <71>, <72>;
++                      clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>,
++                               <&syscrg JH7110_SYSCLK_TIMER0>,
++                               <&syscrg JH7110_SYSCLK_TIMER1>,
++                               <&syscrg JH7110_SYSCLK_TIMER2>,
++                               <&syscrg JH7110_SYSCLK_TIMER3>;
++                      clock-names = "apb", "ch0", "ch1",
++                                    "ch2", "ch3";
++                      resets = <&syscrg JH7110_SYSRST_TIMER_APB>,
++                               <&syscrg JH7110_SYSRST_TIMER0>,
++                               <&syscrg JH7110_SYSRST_TIMER1>,
++                               <&syscrg JH7110_SYSRST_TIMER2>,
++                               <&syscrg JH7110_SYSRST_TIMER3>;
++                      reset-names = "apb", "ch0", "ch1",
++                                    "ch2", "ch3";
++              };
++
+               watchdog@13070000 {
+                       compatible = "starfive,jh7110-wdt";
+                       reg = <0x0 0x13070000 0x0 0x10000>;
+@@ -1011,6 +1119,32 @@
+                       #power-domain-cells = <1>;
+               };
++              csi2rx: csi-bridge@19800000 {
++                      compatible = "starfive,jh7110-csi2rx";
++                      reg = <0x0 0x19800000 0x0 0x10000>;
++                      clocks = <&ispcrg JH7110_ISPCLK_VIN_SYS>,
++                               <&ispcrg JH7110_ISPCLK_VIN_APB>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>;
++                      clock-names = "sys_clk", "p_clk",
++                               "pixel_if0_clk", "pixel_if1_clk",
++                               "pixel_if2_clk", "pixel_if3_clk";
++                      resets = <&ispcrg JH7110_ISPRST_VIN_SYS>,
++                               <&ispcrg JH7110_ISPRST_VIN_APB>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>;
++                      reset-names = "sys", "reg_bank",
++                               "pixel_if0", "pixel_if1",
++                               "pixel_if2", "pixel_if3";
++                      phys = <&csi_phy>;
++                      phy-names = "dphy";
++                      status = "disabled";
++              };
++
+               ispcrg: clock-controller@19810000 {
+                       compatible = "starfive,jh7110-ispcrg";
+                       reg = <0x0 0x19810000 0x0 0x10000>;
+@@ -1028,6 +1162,19 @@
+                       power-domains = <&pwrc JH7110_PD_ISP>;
+               };
++              csi_phy: phy@19820000 {
++                      compatible = "starfive,jh7110-dphy-rx";
++                      reg = <0x0 0x19820000 0x0 0x10000>;
++                      clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>,
++                               <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>,
++                               <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>;
++                      clock-names = "cfg", "ref", "tx";
++                      resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>,
++                               <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>;
++                      power-domains = <&aon_syscon JH7110_AON_PD_DPHY_RX>;
++                      #phy-cells = <0>;
++              };
++
+               voutcrg: clock-controller@295c0000 {
+                       compatible = "starfive,jh7110-voutcrg";
+                       reg = <0x0 0x295c0000 0x0 0x10000>;
+@@ -1045,5 +1192,91 @@
+                       #reset-cells = <1>;
+                       power-domains = <&pwrc JH7110_PD_VOUT>;
+               };
++
++              pcie0: pcie@940000000 {
++                      compatible = "starfive,jh7110-pcie";
++                      reg = <0x9 0x40000000 0x0 0x1000000>,
++                            <0x0 0x2b000000 0x0 0x100000>;
++                      reg-names = "cfg", "apb";
++                      linux,pci-domain = <0>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++                      #interrupt-cells = <1>;
++                      ranges = <0x82000000  0x0 0x30000000  0x0 0x30000000 0x0 0x08000000>,
++                               <0xc3000000  0x9 0x00000000  0x9 0x00000000 0x0 0x40000000>;
++                      interrupts = <56>;
++                      interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++                      interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>,
++                                      <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>,
++                                      <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>,
++                                      <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>;
++                      msi-controller;
++                      device_type = "pci";
++                      starfive,stg-syscon = <&stg_syscon>;
++                      bus-range = <0x0 0xff>;
++                      clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>,
++                               <&stgcrg JH7110_STGCLK_PCIE0_TL>,
++                               <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>,
++                               <&stgcrg JH7110_STGCLK_PCIE0_APB>;
++                      clock-names = "noc", "tl", "axi_mst0", "apb";
++                      resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>,
++                               <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>,
++                               <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>,
++                               <&stgcrg JH7110_STGRST_PCIE0_BRG>,
++                               <&stgcrg JH7110_STGRST_PCIE0_CORE>,
++                               <&stgcrg JH7110_STGRST_PCIE0_APB>;
++                      reset-names = "mst0", "slv0", "slv", "brg",
++                                    "core", "apb";
++                      status = "disabled";
++
++                      pcie_intc0: interrupt-controller {
++                              #address-cells = <0>;
++                              #interrupt-cells = <1>;
++                              interrupt-controller;
++                      };
++              };
++
++              pcie1: pcie@9c0000000 {
++                      compatible = "starfive,jh7110-pcie";
++                      reg = <0x9 0xc0000000 0x0 0x1000000>,
++                            <0x0 0x2c000000 0x0 0x100000>;
++                      reg-names = "cfg", "apb";
++                      linux,pci-domain = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++                      #interrupt-cells = <1>;
++                      ranges = <0x82000000  0x0 0x38000000  0x0 0x38000000 0x0 0x08000000>,
++                               <0xc3000000  0x9 0x80000000  0x9 0x80000000 0x0 0x40000000>;
++                      interrupts = <57>;
++                      interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++                      interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc1 0x1>,
++                                      <0x0 0x0 0x0 0x2 &pcie_intc1 0x2>,
++                                      <0x0 0x0 0x0 0x3 &pcie_intc1 0x3>,
++                                      <0x0 0x0 0x0 0x4 &pcie_intc1 0x4>;
++                      msi-controller;
++                      device_type = "pci";
++                      starfive,stg-syscon = <&stg_syscon>;
++                      bus-range = <0x0 0xff>;
++                      clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>,
++                               <&stgcrg JH7110_STGCLK_PCIE1_TL>,
++                               <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>,
++                               <&stgcrg JH7110_STGCLK_PCIE1_APB>;
++                      clock-names = "noc", "tl", "axi_mst0", "apb";
++                      resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>,
++                               <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>,
++                               <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>,
++                               <&stgcrg JH7110_STGRST_PCIE1_BRG>,
++                               <&stgcrg JH7110_STGRST_PCIE1_CORE>,
++                               <&stgcrg JH7110_STGRST_PCIE1_APB>;
++                      reset-names = "mst0", "slv0", "slv", "brg",
++                                    "core", "apb";
++                      status = "disabled";
++
++                      pcie_intc1: interrupt-controller {
++                              #address-cells = <0>;
++                              #interrupt-cells = <1>;
++                              interrupt-controller;
++                      };
++              };
+       };
+ };
diff --git a/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch b/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch
new file mode 100644 (file)
index 0000000..a7a832d
--- /dev/null
@@ -0,0 +1,147 @@
+From ae7b57a0c69953f5ec06a378aedeed4c86637998 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 11 Apr 2023 16:25:57 +0800
+Subject: [PATCH 049/116] MAINTAINERS: Update all StarFive entries
+
+Merge all StarFive maintainers changes together.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ MAINTAINERS | 61 +++++++++++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 57 insertions(+), 4 deletions(-)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7053,6 +7053,14 @@ T:      git git://anongit.freedesktop.org/drm
+ F:    Documentation/devicetree/bindings/display/rockchip/
+ F:    drivers/gpu/drm/rockchip/
++DRM DRIVERS FOR STARFIVE
++M:    Keith Zhao <keith.zhao@starfivetech.com>
++L:    dri-devel@lists.freedesktop.org
++S:    Maintained
++T:    git git://anongit.freedesktop.org/drm/drm-misc
++F:    Documentation/devicetree/bindings/display/starfive/
++F:    drivers/gpu/drm/verisilicon/
++
+ DRM DRIVERS FOR STI
+ M:    Alain Volmat <alain.volmat@foss.st.com>
+ L:    dri-devel@lists.freedesktop.org
+@@ -16016,6 +16024,13 @@ F:    Documentation/i2c/busses/i2c-ocores.r
+ F:    drivers/i2c/busses/i2c-ocores.c
+ F:    include/linux/platform_data/i2c-ocores.h
++OPENCORES PWM DRIVER
++M:    William Qiu <william.qiu@starfivetech.com>
++M:    Hal Feng <hal.feng@starfivetech.com>
++S:    Supported
++F:    Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
++F:    drivers/pwm/pwm-ocores.c
++
+ OPENRISC ARCHITECTURE
+ M:    Jonas Bonn <jonas@southpole.se>
+ M:    Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
+@@ -16427,6 +16442,14 @@ S:    Maintained
+ F:    Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt
+ F:    drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
++PCI DRIVER FOR PLDA PCIE IP
++M:    Daire McNamara <daire.mcnamara@microchip.com>
++M:    Kevin Xie <kevin.xie@starfivetech.com>
++L:    linux-pci@vger.kernel.org
++S:    Maintained
++F:    Documentation/devicetree/bindings/pci/plda,*
++F:    drivers/pci/controller/plda/*plda*
++
+ PCI DRIVER FOR RENESAS R-CAR
+ M:    Marek Vasut <marek.vasut+renesas@gmail.com>
+ M:    Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+@@ -16658,7 +16681,7 @@ M:     Daire McNamara <daire.mcnamara@microc
+ L:    linux-pci@vger.kernel.org
+ S:    Supported
+ F:    Documentation/devicetree/bindings/pci/microchip*
+-F:    drivers/pci/controller/*microchip*
++F:    drivers/pci/controller/plda/*microchip*
+ PCIE DRIVER FOR QUALCOMM MSM
+ M:    Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+@@ -16682,6 +16705,13 @@ S:    Maintained
+ F:    Documentation/devicetree/bindings/pci/socionext,uniphier-pcie*
+ F:    drivers/pci/controller/dwc/pcie-uniphier*
++PCIE DRIVER FOR STARFIVE JH71x0
++M:    Kevin Xie <kevin.xie@starfivetech.com>
++L:    linux-pci@vger.kernel.org
++S:    Maintained
++F:    Documentation/devicetree/bindings/pci/starfive*
++F:    drivers/pci/controller/plda/pcie-starfive.c
++
+ PCIE DRIVER FOR ST SPEAR13XX
+ M:    Pratyush Anand <pratyush.anand@gmail.com>
+ L:    linux-pci@vger.kernel.org
+@@ -18454,7 +18484,7 @@ F:     drivers/char/hw_random/mpfs-rng.c
+ F:    drivers/clk/microchip/clk-mpfs*.c
+ F:    drivers/i2c/busses/i2c-microchip-corei2c.c
+ F:    drivers/mailbox/mailbox-mpfs.c
+-F:    drivers/pci/controller/pcie-microchip-host.c
++F:    drivers/pci/controller/plda/pcie-microchip-host.c
+ F:    drivers/pwm/pwm-microchip-core.c
+ F:    drivers/reset/reset-mpfs.c
+ F:    drivers/rtc/rtc-mpfs.c
+@@ -20435,6 +20465,15 @@ M:    Ion Badulescu <ionut@badula.org>
+ S:    Odd Fixes
+ F:    drivers/net/ethernet/adaptec/starfire*
++STARFIVE CAMERA SUBSYSTEM DRIVER
++M:    Jack Zhu <jack.zhu@starfivetech.com>
++M:    Changhuang Liang <changhuang.liang@starfivetech.com>
++L:    linux-media@vger.kernel.org
++S:    Maintained
++F:    Documentation/admin-guide/media/starfive_camss.rst
++F:    Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
++F:    drivers/staging/media/starfive/camss
++
+ STARFIVE CRYPTO DRIVER
+ M:    Jia Jie Ho <jiajie.ho@starfivetech.com>
+ M:    William Qiu <william.qiu@starfivetech.com>
+@@ -20473,6 +20512,13 @@ S:    Supported
+ F:    Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml
+ F:    drivers/clk/starfive/clk-starfive-jh7110-pll.c
++STARFIVE JH7110 PWMDAC DRIVER
++M:    Hal Feng <hal.feng@starfivetech.com>
++M:    Xingyu Wu <xingyu.wu@starfivetech.com>
++S:    Supported
++F:    Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml
++F:    sound/soc/starfive/jh7110_pwmdac.c
++
+ STARFIVE JH7110 SYSCON
+ M:    William Qiu <william.qiu@starfivetech.com>
+ M:    Xingyu Wu <xingyu.wu@starfivetech.com>
+@@ -20520,9 +20566,10 @@ F:    drivers/usb/cdns3/cdns3-starfive.c
+ STARFIVE JH71XX PMU CONTROLLER DRIVER
+ M:    Walker Chen <walker.chen@starfivetech.com>
++M:    Changhuang Liang <changhuang.liang@starfivetech.com>
+ S:    Supported
+ F:    Documentation/devicetree/bindings/power/starfive*
+-F:    drivers/pmdomain/starfive/jh71xx-pmu.c
++F:    drivers/pmdomain/starfive/
+ F:    include/dt-bindings/power/starfive,jh7110-pmu.h
+ STARFIVE SOC DRIVERS
+@@ -20530,7 +20577,13 @@ M:    Conor Dooley <conor@kernel.org>
+ S:    Maintained
+ T:    git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/
+ F:    Documentation/devicetree/bindings/soc/starfive/
+-F:    drivers/soc/starfive/
++
++STARFIVE JH7110 TIMER DRIVER
++M:    Samin Guo <samin.guo@starfivetech.com>
++M:    Xingyu Wu <xingyu.wu@starfivetech.com>
++S:    Supported
++F:    Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml
++F:    drivers/clocksource/timer-jh7110.c
+ STARFIVE TRNG DRIVER
+ M:    Jia Jie Ho <jiajie.ho@starfivetech.com>
diff --git a/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch b/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch
new file mode 100644 (file)
index 0000000..a38a885
--- /dev/null
@@ -0,0 +1,64 @@
+From e394195396995456ef98f52ac123c0cb64687748 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Mon, 9 Oct 2023 10:59:03 +0800
+Subject: [PATCH 050/116] mmc: starfive: Change the voltage to adapt to JH7110
+ EVB
+
+Change the voltage, so the driver can adapt to JH7110 EVB.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/mmc/host/dw_mmc-starfive.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+--- a/drivers/mmc/host/dw_mmc-starfive.c
++++ b/drivers/mmc/host/dw_mmc-starfive.c
+@@ -8,6 +8,7 @@
+ #include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
++#include <linux/gpio.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/mmc/host.h>
+ #include <linux/module.h>
+@@ -95,10 +96,39 @@ out:
+       return ret;
+ }
++static int dw_mci_starfive_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++
++      struct dw_mci_slot *slot = mmc_priv(mmc);
++      struct dw_mci *host = slot->host;
++      u32 ret;
++
++      if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
++              ret = gpio_direction_output(25, 0);
++      else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
++              ret = gpio_direction_output(25, 1);
++      if (ret)
++              return ret;
++
++      if (!IS_ERR(mmc->supply.vqmmc)) {
++              ret = mmc_regulator_set_vqmmc(mmc, ios);
++              if (ret < 0) {
++                      dev_err(host->dev, "Regulator set error %d\n", ret);
++                      return ret;
++              }
++      }
++
++      /* We should delay 20ms wait for timing setting finished. */
++      mdelay(20);
++
++      return 0;
++}
++
+ static const struct dw_mci_drv_data starfive_data = {
+       .common_caps            = MMC_CAP_CMD23,
+       .set_ios                = dw_mci_starfive_set_ios,
+       .execute_tuning         = dw_mci_starfive_execute_tuning,
++      .switch_voltage         = dw_mci_starfive_switch_voltage,
+ };
+ static const struct of_device_id dw_mci_starfive_match[] = {
diff --git a/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch b/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch
new file mode 100644 (file)
index 0000000..485ab0d
--- /dev/null
@@ -0,0 +1,60 @@
+From 2cd3e51cb76d49d8db6274ebdc1ba1eb5c872f10 Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfivetech.com>
+Date: Sun, 4 Feb 2024 10:35:24 +0800
+Subject: [PATCH 051/116] spi: spl022: Get and deassert reset in probe()
+
+This fix spi1~6 communication time out.
+
+Signed-off-by: ziv.xu <ziv.xu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -33,6 +33,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/of.h>
+ #include <linux/pinctrl/consumer.h>
++#include <linux/reset.h>
+ /*
+  * This macro is used to define some register default values.
+@@ -370,6 +371,7 @@ struct pl022 {
+       resource_size_t                 phybase;
+       void __iomem                    *virtbase;
+       struct clk                      *clk;
++      struct reset_control            *rst;
+       struct spi_controller           *host;
+       struct pl022_ssp_controller     *host_info;
+       /* Message per-transfer pump */
+@@ -2181,6 +2183,19 @@ static int pl022_probe(struct amba_devic
+               goto err_no_clk_en;
+       }
++      pl022->rst = devm_reset_control_get(&adev->dev, NULL);
++      if (IS_ERR(pl022->rst)) {
++              status = PTR_ERR(pl022->rst);
++              dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n");
++              goto err_no_rst;
++      }
++
++      status = reset_control_deassert(pl022->rst);
++      if (status) {
++              dev_err(&adev->dev, "could not deassert SSP/SPI bus reset\n");
++              goto err_no_rst_de;
++      }
++
+       /* Initialize transfer pump */
+       tasklet_init(&pl022->pump_transfers, pump_transfers,
+                    (unsigned long)pl022);
+@@ -2240,6 +2255,8 @@ static int pl022_probe(struct amba_devic
+       if (platform_info->enable_dma)
+               pl022_dma_remove(pl022);
+  err_no_irq:
++ err_no_rst_de:
++ err_no_rst:
+       clk_disable_unprepare(pl022->clk);
+  err_no_clk_en:
+  err_no_clk:
diff --git a/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch b/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch
new file mode 100644 (file)
index 0000000..e274e84
--- /dev/null
@@ -0,0 +1,30 @@
+From 9cc8de0cdc1600f460f618e342e1f524adad07c4 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Wed, 21 Feb 2024 10:23:48 +0800
+Subject: [PATCH 052/116] ASoC: dwc: i2s: Fix getting platform data error for
+ StarFive JH7110
+
+JH7110 need to use a DT specific function to get the platform data,
+otherwise, it fails in probe().
+
+Fixes: 9c97790a07dc ("ASoC: dwc: Fix non-DT instantiation")
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -917,7 +917,11 @@ static int jh7110_i2stx0_clk_cfg(struct
+ static int dw_i2s_probe(struct platform_device *pdev)
+ {
++#ifdef CONFIG_OF
++      const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev);
++#else
+       const struct i2s_platform_data *pdata = pdev->dev.platform_data;
++#endif
+       struct dw_i2s_dev *dev;
+       struct resource *res;
+       int ret, irq;
diff --git a/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch b/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch
new file mode 100644 (file)
index 0000000..8203bec
--- /dev/null
@@ -0,0 +1,67 @@
+From 1be9bd37fdb5f50162dba0158e1fee295ebca9aa Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Tue, 17 Oct 2023 17:22:52 +0800
+Subject: [PATCH 053/116] ASoC: dwc: i2s: Add RX master support for StarFive
+ JH7110 SoC
+
+Add JH7110 I2S RX master support, so the PDM can work on JH7110
+EVB board.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -906,6 +906,27 @@ static int jh7110_i2srx_crg_init(struct
+       return jh7110_i2s_crg_slave_init(dev);
+ }
++/* Special syscon initialization about RX channel with master mode on JH7110 SoC */
++static int jh7110_i2srx_mst_crg_init(struct dw_i2s_dev *dev)
++{
++      struct regmap *regmap;
++      unsigned int args[5];
++
++      regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node,
++                                                    "starfive,syscon",
++                                                    5, args);
++      if (IS_ERR(regmap))
++              return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n");
++
++      /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */
++      regmap_update_bits(regmap, args[0], args[1], args[1]);
++
++      /* Change I2Srx source (PDM) with syscon register, args[0]: offset, args[1]: mask */
++      regmap_update_bits(regmap, args[2], args[3], args[4]);
++
++      return jh7110_i2s_crg_master_init(dev);
++}
++
+ static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config)
+ {
+       struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config);
+@@ -1086,11 +1107,21 @@ static const struct i2s_platform_data jh
+       .i2s_pd_init = jh7110_i2srx_crg_init,
+ };
++static const struct i2s_platform_data jh7110_i2srx_mst_data = {
++      .cap = DWC_I2S_RECORD | DW_I2S_MASTER,
++      .channel = TWO_CHANNEL_SUPPORT,
++      .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
++      .snd_rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000,
++      .i2s_clk_cfg = jh7110_i2stx0_clk_cfg,
++      .i2s_pd_init = jh7110_i2srx_mst_crg_init,
++};
++
+ static const struct of_device_id dw_i2s_of_match[] = {
+       { .compatible = "snps,designware-i2s",   },
+       { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, },
+       { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,},
+       { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,},
++      { .compatible = "starfive,jh7110-i2srx-master", .data = &jh7110_i2srx_mst_data,},
+       {},
+ };
diff --git a/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch b/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch
new file mode 100644 (file)
index 0000000..51976c6
--- /dev/null
@@ -0,0 +1,24 @@
+From 1ec26ba377d8ae59cd09811ec78623a750a9c150 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 26 Feb 2024 11:35:44 +0800
+Subject: [PATCH 054/116] pinctrl: starfive: jh7110: Unset .strict in
+ pinmux_ops
+
+Allow simultaneous use of the same pin for GPIO and another function.
+This feature is used in HDMI hot plug detect.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c
++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c
+@@ -327,7 +327,6 @@ static const struct pinmux_ops jh7110_pi
+       .get_function_name   = pinmux_generic_get_function_name,
+       .get_function_groups = pinmux_generic_get_function_groups,
+       .set_mux             = jh7110_set_mux,
+-      .strict              = true,
+ };
+ static const u8 jh7110_drive_strength_mA[4] = { 2, 4, 8, 12 };
diff --git a/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch b/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch
new file mode 100644 (file)
index 0000000..39699e8
--- /dev/null
@@ -0,0 +1,22 @@
+From 005549a2bd839335b0e3dc4152f00f642b524f07 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Sat, 7 Oct 2023 18:10:20 +0800
+Subject: [PATCH 055/116] mm/pgtable-generic.c: Export symbol __pte_offset_map
+
+So JH7110 vdec can call pte_offset_map() when it is built as a module.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ mm/pgtable-generic.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/mm/pgtable-generic.c
++++ b/mm/pgtable-generic.c
+@@ -304,6 +304,7 @@ nomap:
+       rcu_read_unlock();
+       return NULL;
+ }
++EXPORT_SYMBOL(__pte_offset_map);
+ pte_t *pte_offset_map_nolock(struct mm_struct *mm, pmd_t *pmd,
+                            unsigned long addr, spinlock_t **ptlp)
diff --git a/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch b/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch
new file mode 100644 (file)
index 0000000..7b803d3
--- /dev/null
@@ -0,0 +1,2623 @@
+From 9f46b0d43f8945ff3a8b81ddc6567df370b60911 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 28 Jul 2023 17:19:12 +0800
+Subject: [PATCH 056/116] riscv: dts: starfive: Add JH7110 EVB device tree
+
+Add JH7110 evaluation board device tree.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile         |   3 +
+ arch/riscv/boot/dts/starfive/jh7110-clk.dtsi  |  80 ++
+ .../boot/dts/starfive/jh7110-evb-pinctrl.dtsi | 997 ++++++++++++++++++
+ arch/riscv/boot/dts/starfive/jh7110-evb.dts   |  35 +
+ arch/riscv/boot/dts/starfive/jh7110-evb.dtsi  | 854 +++++++++++++++
+ arch/riscv/boot/dts/starfive/jh7110.dtsi      | 482 ++++++++-
+ 6 files changed, 2444 insertions(+), 7 deletions(-)
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-clk.dtsi
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -4,9 +4,12 @@ DTC_FLAGS_jh7100-beaglev-starlight := -@
+ DTC_FLAGS_jh7100-starfive-visionfive-v1 := -@
+ DTC_FLAGS_jh7110-starfive-visionfive-2-v1.2a := -@
+ DTC_FLAGS_jh7110-starfive-visionfive-2-v1.3b := -@
++DTC_FLAGS_jh7110-evb := -@
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
++
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-clk.dtsi
+@@ -0,0 +1,80 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++/ {
++      ac108_mclk: ac108_mclk {
++              compatible = "fixed-clock";
++              #clock-cells = <0>;
++              clock-frequency = <24000000>;
++      };
++
++      clk_ext_camera: clk-ext-camera {
++              compatible = "fixed-clock";
++              #clock-cells = <0>;
++              clock-frequency = <24000000>;
++      };
++
++      wm8960_mclk: wm8960_mclk {
++              compatible = "fixed-clock";
++              #clock-cells = <0>;
++              clock-frequency = <24576000>;
++      };
++};
++
++&dvp_clk {
++      clock-frequency = <74250000>;
++};
++
++&gmac0_rgmii_rxin {
++      clock-frequency = <125000000>;
++};
++
++&gmac0_rmii_refin {
++      clock-frequency = <50000000>;
++};
++
++&gmac1_rgmii_rxin {
++      clock-frequency = <125000000>;
++};
++
++&gmac1_rmii_refin {
++      clock-frequency = <50000000>;
++};
++
++&hdmitx0_pixelclk {
++      clock-frequency = <297000000>;
++};
++
++&i2srx_bclk_ext {
++      clock-frequency = <12288000>;
++};
++
++&i2srx_lrck_ext {
++      clock-frequency = <192000>;
++};
++
++&i2stx_bclk_ext {
++      clock-frequency = <12288000>;
++};
++
++&i2stx_lrck_ext {
++      clock-frequency = <192000>;
++};
++
++&mclk_ext {
++      clock-frequency = <12288000>;
++};
++
++&osc {
++      clock-frequency = <24000000>;
++};
++
++&rtc_osc {
++      clock-frequency = <32768>;
++};
++
++&tdm_ext {
++      clock-frequency = <49152000>;
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi
+@@ -0,0 +1,997 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ * Author: Hal Feng <hal.feng@starfivetech.com>
++ */
++
++#include "jh7110-pinfunc.h"
++
++&sysgpio {
++      can0_pins: can0-0 {
++              can-pins {
++                      pinmux = <GPIOMUX(30, GPOUT_SYS_CAN0_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(31, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_CAN0_RXD)>,
++                               <GPIOMUX(32, GPOUT_SYS_CAN0_STBY,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      can1_pins: can1-0 {
++              can-pins {
++                      pinmux = <GPIOMUX(29, GPOUT_SYS_CAN1_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(27, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_CAN1_RXD)>,
++                               <GPIOMUX(45, GPOUT_SYS_CAN1_STBY,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      drive-strength = <12>;
++                      input-enable;
++              };
++      };
++
++      dvp_pins: dvp-0 {
++              dvp-pins{
++                      pinmux = <PINMUX(21, 2)>,
++                               <PINMUX(22, 2)>,
++                               <PINMUX(23, 2)>,
++                               <PINMUX(24, 2)>,
++                               <PINMUX(25, 2)>,
++                               <PINMUX(26, 2)>,
++                               <PINMUX(27, 2)>,
++                               <PINMUX(28, 2)>,
++                               <PINMUX(29, 2)>,
++                               <PINMUX(30, 2)>,
++                               <PINMUX(31, 2)>,
++                               <PINMUX(32, 2)>,
++                               <PINMUX(33, 2)>,
++                               <PINMUX(34, 2)>,
++                               <PINMUX(35, 2)>;
++                      input-enable;
++              };
++      };
++
++      emmc0_pins: emmc0-0 {
++              emmc-pins {
++                      pinmux = <GPIOMUX(22, GPOUT_SYS_SDIO0_RST,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <PINMUX(64, 0)>,
++                               <PINMUX(65, 0)>,
++                               <PINMUX(66, 0)>,
++                               <PINMUX(67, 0)>,
++                               <PINMUX(68, 0)>,
++                               <PINMUX(69, 0)>,
++                               <PINMUX(70, 0)>,
++                               <PINMUX(71, 0)>,
++                               <PINMUX(72, 0)>,
++                               <PINMUX(73, 0)>;
++                      bias-pull-up;
++                      drive-strength = <12>;
++                      input-enable;
++                      slew-rate = <1>;
++              };
++      };
++
++      emmc1_pins: emmc1-0 {
++              emmc-pins {
++                      pinmux = <GPIOMUX(51, GPOUT_SYS_SDIO1_RST,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(38, GPOUT_SYS_SDIO1_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(36, GPOUT_SYS_SDIO1_CMD,
++                                            GPOEN_SYS_SDIO1_CMD,
++                                            GPI_SYS_SDIO1_CMD)>,
++                               <GPIOMUX(43, GPOUT_SYS_SDIO1_DATA0,
++                                            GPOEN_SYS_SDIO1_DATA0,
++                                            GPI_SYS_SDIO1_DATA0)>,
++                               <GPIOMUX(48, GPOUT_SYS_SDIO1_DATA1,
++                                            GPOEN_SYS_SDIO1_DATA1,
++                                            GPI_SYS_SDIO1_DATA1)>,
++                               <GPIOMUX(53, GPOUT_SYS_SDIO1_DATA2,
++                                            GPOEN_SYS_SDIO1_DATA2,
++                                            GPI_SYS_SDIO1_DATA2)>,
++                               <GPIOMUX(63, GPOUT_SYS_SDIO1_DATA3,
++                                            GPOEN_SYS_SDIO1_DATA3,
++                                            GPI_SYS_SDIO1_DATA3)>,
++                               <GPIOMUX(52, GPOUT_SYS_SDIO1_DATA4,
++                                            GPOEN_SYS_SDIO1_DATA4,
++                                            GPI_SYS_SDIO1_DATA4)>,
++                               <GPIOMUX(39, GPOUT_SYS_SDIO1_DATA5,
++                                            GPOEN_SYS_SDIO1_DATA5,
++                                            GPI_SYS_SDIO1_DATA5)>,
++                               <GPIOMUX(46, GPOUT_SYS_SDIO1_DATA6,
++                                            GPOEN_SYS_SDIO1_DATA6,
++                                            GPI_SYS_SDIO1_DATA6)>,
++                               <GPIOMUX(47, GPOUT_SYS_SDIO1_DATA7,
++                                            GPOEN_SYS_SDIO1_DATA7,
++                                            GPI_SYS_SDIO1_DATA7)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++      };
++
++      gmac0_pins: gmac0-0 {
++              reset-pins {
++                      pinmux = <GPIOMUX(13, GPOUT_HIGH,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-pull-up;
++              };
++      };
++
++      gmac1_pins: gmac1-0 {
++              mdc-pins {
++                      pinmux = <PINMUX(75, 0)>;
++              };
++      };
++
++      hdmi_pins: hdmi-0 {
++              scl-pins {
++                      pinmux = <GPIOMUX(7, GPOUT_SYS_HDMI_DDC_SCL,
++                                           GPOEN_SYS_HDMI_DDC_SCL,
++                                           GPI_SYS_HDMI_DDC_SCL)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++
++              sda-pins {
++                      pinmux = <GPIOMUX(8, GPOUT_SYS_HDMI_DDC_SDA,
++                                           GPOEN_SYS_HDMI_DDC_SDA,
++                                           GPI_SYS_HDMI_DDC_SDA)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++
++              cec-pins {
++                      pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
++                                            GPOEN_SYS_HDMI_CEC_SDA,
++                                            GPI_SYS_HDMI_CEC_SDA)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++
++              hpd-pins {
++                      pinmux = <GPIOMUX(15, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_HDMI_HPD)>;
++                      bias-disable; /* external pull-up */
++                      input-enable;
++              };
++      };
++
++      i2c0_pins: i2c0-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(57, GPOUT_LOW,
++                                            GPOEN_SYS_I2C0_CLK,
++                                            GPI_SYS_I2C0_CLK)>,
++                               <GPIOMUX(58, GPOUT_LOW,
++                                            GPOEN_SYS_I2C0_DATA,
++                                            GPI_SYS_I2C0_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2c1_pins: i2c1-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(49, GPOUT_LOW,
++                                            GPOEN_SYS_I2C1_CLK,
++                                            GPI_SYS_I2C1_CLK)>,
++                               <GPIOMUX(50, GPOUT_LOW,
++                                            GPOEN_SYS_I2C1_DATA,
++                                            GPI_SYS_I2C1_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2c2_pins: i2c2-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(11, GPOUT_LOW,
++                                            GPOEN_SYS_I2C2_CLK,
++                                            GPI_SYS_I2C2_CLK)>,
++                               <GPIOMUX(9, GPOUT_LOW,
++                                           GPOEN_SYS_I2C2_DATA,
++                                           GPI_SYS_I2C2_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2c3_pins: i2c3-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(51, GPOUT_LOW,
++                                            GPOEN_SYS_I2C3_CLK,
++                                            GPI_SYS_I2C3_CLK)>,
++                               <GPIOMUX(52, GPOUT_LOW,
++                                            GPOEN_SYS_I2C3_DATA,
++                                            GPI_SYS_I2C3_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2c4_pins: i2c4-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(18, GPOUT_LOW,
++                                            GPOEN_SYS_I2C4_CLK,
++                                            GPI_SYS_I2C4_CLK)>,
++                               <GPIOMUX(12, GPOUT_LOW,
++                                            GPOEN_SYS_I2C4_DATA,
++                                            GPI_SYS_I2C4_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2c5_pins: i2c5-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(19, GPOUT_LOW,
++                                            GPOEN_SYS_I2C5_CLK,
++                                            GPI_SYS_I2C5_CLK)>,
++                               <GPIOMUX(20, GPOUT_LOW,
++                                            GPOEN_SYS_I2C5_DATA,
++                                            GPI_SYS_I2C5_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2c6_pins: i2c6-0 {
++              i2c-pins {
++                      pinmux = <GPIOMUX(16, GPOUT_LOW,
++                                            GPOEN_SYS_I2C6_CLK,
++                                            GPI_SYS_I2C6_CLK)>,
++                               <GPIOMUX(17, GPOUT_LOW,
++                                            GPOEN_SYS_I2C6_DATA,
++                                            GPI_SYS_I2C6_DATA)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      i2s_clk_pins: i2s-clk-0 {
++              bclk-lrck-pins {
++                      pinmux = <GPIOMUX(38, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2STX1_BCLK)>,
++                               <GPIOMUX(38, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2SRX_BCLK)>,
++                               <GPIOMUX(63, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2STX1_LRCK)>,
++                               <GPIOMUX(63, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2SRX_LRCK)>;
++                      input-enable;
++              };
++      };
++
++      i2srx_clk_pins: i2srx-clk-0 {
++              mclk-pins {
++                      pinmux = <GPIOMUX(58, GPOUT_SYS_MCLK,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      i2srx_pins: i2srx-0 {
++              i2srx-pins {
++                      pinmux = <GPIOMUX(61, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_I2SRX_SDIN0)>;
++                      input-enable;
++              };
++      };
++
++      i2stx_pins: i2stx-0 {
++              i2stx-pins {
++                      pinmux = <GPIOMUX(44, GPOUT_SYS_I2STX1_SDO0,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      mclk_ext_pins: mclk-ext-0 {
++              mclk-ext-pins {
++                      pinmux = <GPIOMUX(4, GPOUT_LOW,
++                                           GPOEN_DISABLE,
++                                           GPI_SYS_MCLK_EXT)>;
++                      input-enable;
++              };
++      };
++
++      pdm_pins: pdm-0 {
++              pdm-pins {
++                      pinmux = <GPIOMUX(54, GPOUT_SYS_PDM_MCLK,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(60, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_PDM_DMIC0)>;
++                      input-enable;
++              };
++      };
++
++      pwm_ch0to3_pins: pwm-ch0to3-0 {
++              pwm-pins {
++                      pinmux = <GPIOMUX(45, GPOUT_SYS_PWM_CHANNEL0,
++                                            GPOEN_SYS_PWM0_CHANNEL0,
++                                            GPI_NONE)>,
++                               <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL1,
++                                            GPOEN_SYS_PWM0_CHANNEL1,
++                                            GPI_NONE)>,
++                               <GPIOMUX(47, GPOUT_SYS_PWM_CHANNEL2,
++                                            GPOEN_SYS_PWM0_CHANNEL2,
++                                            GPI_NONE)>,
++                               <GPIOMUX(48, GPOUT_SYS_PWM_CHANNEL3,
++                                            GPOEN_SYS_PWM0_CHANNEL3,
++                                            GPI_NONE)>;
++                      drive-strength = <12>;
++              };
++      };
++
++      pwmdac_pins: pwmdac-0 {
++              pwmdac-pins {
++                      pinmux = <GPIOMUX(57, GPOUT_SYS_PWMDAC_LEFT,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(42, GPOUT_SYS_PWMDAC_RIGHT,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++              };
++      };
++
++      rgb_pad_pins: rgb-pad-pins {
++              rgb-0-pins {
++                      pinmux = <PINMUX(36, 1)>;
++                      drive-strength = <12>;
++                      input-disable;
++                      slew-rate = <1>;
++              };
++
++              rgb-pins {
++                      pinmux = <PINMUX(37, 1)>,
++                               <PINMUX(38, 1)>,
++                               <PINMUX(39, 1)>,
++                               <PINMUX(40, 1)>,
++                               <PINMUX(41, 1)>,
++                               <PINMUX(42, 1)>,
++                               <PINMUX(43, 1)>,
++                               <PINMUX(44, 1)>,
++                               <PINMUX(45, 1)>,
++                               <PINMUX(46, 1)>,
++                               <PINMUX(47, 1)>,
++                               <PINMUX(48, 1)>,
++                               <PINMUX(49, 1)>,
++                               <PINMUX(50, 1)>,
++                               <PINMUX(51, 1)>,
++                               <PINMUX(52, 1)>,
++                               <PINMUX(53, 1)>,
++                               <PINMUX(54, 1)>,
++                               <PINMUX(55, 1)>,
++                               <PINMUX(56, 1)>,
++                               <PINMUX(57, 1)>,
++                               <PINMUX(58, 1)>,
++                               <PINMUX(59, 1)>,
++                               <PINMUX(60, 1)>,
++                               <PINMUX(61, 1)>,
++                               <PINMUX(62, 1)>,
++                               <PINMUX(63, 1)>;
++                      drive-strength = <12>;
++                      input-disable;
++              };
++      };
++
++      sdcard0_pins: sdcard0-0 {
++              sdcard-pins {
++                      pinmux = <GPIOMUX(24, GPOUT_SYS_SDIO0_RST,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <PINMUX(64, 0)>,
++                               <PINMUX(65, 0)>,
++                               <PINMUX(66, 0)>,
++                               <PINMUX(67, 0)>,
++                               <PINMUX(68, 0)>,
++                               <PINMUX(69, 0)>;
++                      bias-pull-up;
++                      drive-strength = <12>;
++                      input-enable;
++                      slew-rate = <1>;
++              };
++      };
++
++      sdcard1_pins: sdcard1-0 {
++              sdcard-pins {
++                      pinmux = <GPIOMUX(56, GPOUT_SYS_SDIO1_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(50, GPOUT_SYS_SDIO1_CMD,
++                                            GPOEN_SYS_SDIO1_CMD,
++                                            GPI_SYS_SDIO1_CMD)>,
++                               <GPIOMUX(49, GPOUT_SYS_SDIO1_DATA0,
++                                            GPOEN_SYS_SDIO1_DATA0,
++                                            GPI_SYS_SDIO1_DATA0)>,
++                               <GPIOMUX(45, GPOUT_SYS_SDIO1_DATA1,
++                                            GPOEN_SYS_SDIO1_DATA1,
++                                            GPI_SYS_SDIO1_DATA1)>,
++                               <GPIOMUX(62, GPOUT_SYS_SDIO1_DATA2,
++                                            GPOEN_SYS_SDIO1_DATA2,
++                                            GPI_SYS_SDIO1_DATA2)>,
++                               <GPIOMUX(40, GPOUT_SYS_SDIO1_DATA3,
++                                            GPOEN_SYS_SDIO1_DATA3,
++                                            GPI_SYS_SDIO1_DATA3)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++      };
++
++      spdif_pins: spdif-0 {
++              spdif-pins {
++                      pinmux = <GPIOMUX(57, GPOUT_SYS_SPDIF,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++      };
++
++      spi0_pins: spi0-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(38, GPOUT_SYS_SPI0_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(39, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI0_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(36, GPOUT_SYS_SPI0_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI0_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(37, GPOUT_SYS_SPI0_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI0_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      spi1_pins: spi1-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(42, GPOUT_SYS_SPI1_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(43, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI1_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(40, GPOUT_SYS_SPI1_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI1_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(41, GPOUT_SYS_SPI1_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI1_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      spi2_pins: spi2-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(46, GPOUT_SYS_SPI2_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(47, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI2_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(44, GPOUT_SYS_SPI2_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI2_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(45, GPOUT_SYS_SPI2_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI2_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      spi3_pins: spi3-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(50, GPOUT_SYS_SPI3_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(51, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI3_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(48, GPOUT_SYS_SPI3_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI3_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(49, GPOUT_SYS_SPI3_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI3_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      spi4_pins: spi4-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(54, GPOUT_SYS_SPI4_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(55, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI4_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(52, GPOUT_SYS_SPI4_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI4_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(53, GPOUT_SYS_SPI4_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI4_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      spi5_pins: spi5-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(58, GPOUT_SYS_SPI5_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(59, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI5_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(56, GPOUT_SYS_SPI5_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI5_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(57, GPOUT_SYS_SPI5_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI5_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      spi6_pins: spi6-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(62, GPOUT_SYS_SPI6_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              miso-pins {
++                      pinmux = <GPIOMUX(63, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_SPI6_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++
++              sck-pins {
++                      pinmux = <GPIOMUX(60, GPOUT_SYS_SPI6_CLK,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI6_CLK)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++
++              ss-pins {
++                      pinmux = <GPIOMUX(61, GPOUT_SYS_SPI6_FSS,
++                                            GPOEN_ENABLE,
++                                            GPI_SYS_SPI6_FSS)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      tdm_pins: tdm-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(44, GPOUT_SYS_TDM_TXD,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(61, GPOUT_HIGH,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_TDM_RXD)>;
++                      input-enable;
++              };
++
++              sync-pins {
++                      pinmux = <GPIOMUX(63, GPOUT_HIGH,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_TDM_SYNC)>;
++                      input-enable;
++              };
++
++              pcmclk-pins {
++                      pinmux = <GPIOMUX(38, GPOUT_HIGH,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_TDM_CLK)>;
++                      input-enable;
++              };
++      };
++
++      uart0_pins: uart0-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(5, GPOUT_SYS_UART0_TX,
++                                           GPOEN_ENABLE,
++                                           GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(6, GPOUT_LOW,
++                                           GPOEN_DISABLE,
++                                           GPI_SYS_UART0_RX)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++      };
++
++      uart1_pins: uart1-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(30, GPOUT_SYS_UART1_TX,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(31, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART1_RX)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++
++              cts-pins {
++                      pinmux = <GPIOMUX(29, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART1_CTS)>;
++                      input-enable;
++              };
++
++              rts-pins {
++                      pinmux = <GPIOMUX(27, GPOUT_SYS_UART1_RTS,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      uart2_pins: uart2-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(30, GPOUT_SYS_UART2_TX,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(31, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART2_RX)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++
++              cts-pins {
++                      pinmux = <GPIOMUX(29, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART2_CTS)>;
++                      input-enable;
++              };
++
++              rts-pins {
++                      pinmux = <GPIOMUX(27, GPOUT_SYS_UART2_RTS,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      uart3_pins: uart3-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(30, GPOUT_SYS_UART3_TX,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(31, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART3_RX)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++      };
++
++      uart4_pins: uart4-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(30, GPOUT_SYS_UART4_TX,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(31, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART4_RX)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++
++              cts-pins {
++                      pinmux = <GPIOMUX(29, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART4_CTS)>;
++                      input-enable;
++              };
++
++              rts-pins {
++                      pinmux = <GPIOMUX(27, GPOUT_SYS_UART4_RTS,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      uart5_pins: uart5-0 {
++              tx-pins {
++                      pinmux = <GPIOMUX(30, GPOUT_SYS_UART5_TX,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <12>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++
++              rx-pins {
++                      pinmux = <GPIOMUX(31, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART5_RX)>;
++                      bias-pull-up;
++                      drive-strength = <2>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++
++              cts-pins {
++                      pinmux = <GPIOMUX(29, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_UART5_CTS)>;
++                      input-enable;
++              };
++
++              rts-pins {
++                      pinmux = <GPIOMUX(27, GPOUT_SYS_UART5_RTS,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>;
++                      input-enable;
++              };
++      };
++
++      usb_pins: usb-0 {
++              usb-pins {
++                      pinmux = <GPIOMUX(33, GPOUT_HIGH,
++                                            GPOEN_ENABLE,
++                                            GPI_NONE)>,
++                               <GPIOMUX(34, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_USB_OVERCURRENT)>;
++                      input-enable;
++              };
++      };
++};
++
++&aongpio {
++      pwm_ch4to5_pins: pwm-ch4to5-0 {
++              pwm-pins {
++                      pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM4, /* PAD_RGPIO0 */
++                                           GPOEN_AON_PTC0_OE_N_4,
++                                           GPI_NONE)>,
++                               <GPIOMUX(2, GPOUT_AON_PTC0_PWM5, /* PAD_RGPIO1 */
++                                           GPOEN_AON_PTC0_OE_N_5,
++                                           GPI_NONE)>;
++                      drive-strength = <12>;
++              };
++      };
++
++      pwm_ch6to7_pins: pwm-ch6to7-0 {
++              pwm-pins {
++                      pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM6, /* PAD_RGPIO0 */
++                                           GPOEN_AON_PTC0_OE_N_6,
++                                           GPI_NONE)>,
++                               <GPIOMUX(2, GPOUT_AON_PTC0_PWM7, /* PAD_RGPIO1 */
++                                           GPOEN_AON_PTC0_OE_N_7,
++                                           GPI_NONE)>;
++                      drive-strength = <12>;
++              };
++      };
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      no-sdio;
++      no-mmc;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&usb0 {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
+@@ -0,0 +1,854 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2023 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110.dtsi"
++#include "jh7110-clk.dtsi"
++#include "jh7110-evb-pinctrl.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++      aliases {
++              ethernet0 = &gmac0;
++              ethernet1 = &gmac1;
++              i2c0 = &i2c0;
++              i2c1 = &i2c1;
++              i2c2 = &i2c2;
++              i2c3 = &i2c3;
++              i2c4 = &i2c4;
++              i2c5 = &i2c5;
++              i2c6 = &i2c6;
++              pcie0 = &pcie0;
++              pcie1 = &pcie1;
++              serial0 = &uart0;
++              serial3 = &uart3;
++      };
++
++      chosen {
++              stdout-path = "serial0:115200n8";
++      };
++
++      cpus {
++              timebase-frequency = <4000000>;
++      };
++
++      memory@40000000 {
++              device_type = "memory";
++              reg = <0x0 0x40000000 0x1 0x0>;
++      };
++
++      reserved-memory {
++              #address-cells = <2>;
++              #size-cells = <2>;
++              ranges;
++
++              linux,cma {
++                      compatible = "shared-dma-pool";
++                      reusable;
++                      size = <0x0 0x20000000>;
++                      alignment = <0x0 0x1000>;
++                      alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
++                      linux,cma-default;
++              };
++
++              e24_mem: e24@c0000000 {
++                      reg = <0x0 0x6ce00000 0x0 0x1600000>;
++              };
++
++              xrp_reserved: xrpbuffer@f0000000 {
++                      reg = <0x0 0x69c00000 0x0 0x01ffffff
++                             0x0 0x6bc00000 0x0 0x00001000
++                             0x0 0x6bc01000 0x0 0x00fff000
++                             0x0 0x6cc00000 0x0 0x00001000>;
++              };
++      };
++
++      /* i2s + hdmi */
++      sound1: snd-card1 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-HDMI-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "i2s";
++                      bitclock-master = <&sndi2s0>;
++                      frame-master = <&sndi2s0>;
++                      mclk-fs = <256>;
++                      status = "okay";
++
++                      sndi2s0: cpu {
++                              sound-dai = <&i2stx0>;
++                      };
++
++                      codec {
++                              sound-dai = <&hdmi>;
++                      };
++              };
++      };
++};
++
++&U74_1 {
++      /delete-property/ clocks;
++      /delete-property/ clock-names;
++};
++
++&U74_2 {
++      /delete-property/ clocks;
++      /delete-property/ clock-names;
++};
++
++&U74_3 {
++      /delete-property/ clocks;
++      /delete-property/ clock-names;
++};
++
++&U74_4 {
++      /delete-property/ clocks;
++      /delete-property/ clock-names;
++};
++
++&can0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&can0_pins>;
++      status = "disabled";
++};
++
++&can1 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&can1_pins>;
++      status = "disabled";
++};
++
++&co_process {
++      memory-region = <&e24_mem>;
++      status = "okay";
++};
++
++&dc8200 {
++      status = "okay";
++
++      dc_out: port {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              dc_out_dpi0: endpoint@0 {
++                      reg = <0>;
++                      remote-endpoint = <&hdmi_input0>;
++              };
++              dc_out_dpi1: endpoint@1 {
++                      reg = <1>;
++                      remote-endpoint = <&hdmi_in_lcdc>;
++              };
++              dc_out_dpi2: endpoint@2 {
++                      reg = <2>;
++                      remote-endpoint = <&mipi_in>;
++              };
++      };
++};
++
++&display {
++      ports = <&dc_out_dpi0>;
++      status = "okay";
++};
++
++&dsi_output {
++      status = "okay";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@0 {
++                      reg = <0>;
++                      mipi_in: endpoint {
++                              remote-endpoint = <&dc_out_dpi2>;
++                      };
++              };
++
++              port@1 {
++                      reg = <1>;
++                      mipi_out: endpoint {
++                              remote-endpoint = <&dsi_in_port>;
++                      };
++              };
++      };
++};
++
++&gmac0 {
++      phy-handle = <&phy0>;
++      phy-mode = "rgmii-id";
++      status = "okay";
++
++      mdio {
++              #address-cells = <1>;
++              #size-cells = <0>;
++              compatible = "snps,dwmac-mdio";
++
++              phy0: ethernet-phy@0 {
++                      reg = <0>;
++                      rx-internal-delay-ps = <1900>;
++                      tx-internal-delay-ps = <1650>;
++              };
++      };
++};
++
++&gmac1 {
++      #address-cells = <1>;
++      #size-cells = <0>;
++      status = "okay";
++
++      phy1: ethernet-phy@1 {
++              reg = <0>;
++              rxc-skew-ps = <1060>;
++              txc-skew-ps = <1800>;
++      };
++};
++
++&gpu {
++      status = "okay";
++};
++
++&hdmi {
++      status = "okay";
++      pinctrl-names = "default";
++      pinctrl-0 = <&hdmi_pins>;
++      hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
++
++      hdmi_in: port {
++              #address-cells = <1>;
++              #size-cells = <0>;
++              hdmi_in_lcdc: endpoint@0 {
++                      reg = <0>;
++                      remote-endpoint = <&dc_out_dpi1>;
++              };
++      };
++};
++
++&i2c0 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c0_pins>;
++      status = "disabled";
++
++      wm8960: codec@1a {
++              compatible = "wlf,wm8960";
++              reg = <0x1a>;
++              wlf,shared-lrclk;
++              #sound-dai-cells = <0>;
++      };
++
++      ac108: ac108@3b {
++              compatible = "x-power,ac108_0";
++              reg = <0x3b>;
++              #sound-dai-cells = <0>;
++              data-protocol = <0>;
++      };
++};
++
++&i2c1 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c1_pins>;
++      status = "disabled";
++};
++
++&i2c2 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c2_pins>;
++      status = "okay";
++
++      tinker_ft5406: tinker_ft5406@38 {
++              compatible = "tinker_ft5406";
++              reg = <0x38>;
++      };
++
++      seeed_plane_i2c@45 {
++              compatible = "seeed_panel";
++              reg = <0x45>;
++
++              port {
++                      panel_dsi_port: endpoint {
++                              remote-endpoint = <&dsi_out_port>;
++                      };
++              };
++      };
++};
++
++&i2c3 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c3_pins>;
++      status = "disabled";
++};
++
++&i2c4 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c4_pins>;
++      status = "okay";
++
++      sc2235: sc2235@30 {
++              compatible = "smartsens,sc2235";
++              reg = <0x30>;
++              clocks = <&clk_ext_camera>;
++              clock-names = "xclk";
++
++              port {
++                      /* Parallel bus endpoint */
++                      sc2235_to_parallel: endpoint {
++                              remote-endpoint = <&parallel_from_sc2235>;
++                              bus-type = <5>;      /* Parallel */
++                              bus-width = <8>;
++                              data-shift = <2>; /* lines 13:6 are used */
++                              hsync-active = <1>;
++                              vsync-active = <1>;
++                              pclk-sample = <1>;
++                      };
++              };
++      };
++
++      tda998x@70 {
++              compatible = "nxp,tda998x";
++              reg = <0x70>;
++
++              port {
++                      tda998x_0_input: endpoint {
++                              remote-endpoint = <&hdmi_out>;
++                      };
++              };
++      };
++};
++
++&i2c5 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c5_pins>;
++      status = "okay";
++
++      pmic: jh7110_evb_reg@50 {
++              compatible = "starfive,jh7110-evb-regulator";
++              reg = <0x50>;
++
++              regulators {
++                      hdmi_1p8: LDO_REG1 {
++                              regulator-name = "hdmi_1p8";
++                              regulator-min-microvolt = <1800000>;
++                              regulator-max-microvolt = <1800000>;
++                      };
++                      mipitx_1p8: LDO_REG2 {
++                              regulator-name = "mipitx_1p8";
++                              regulator-min-microvolt = <1800000>;
++                              regulator-max-microvolt = <1800000>;
++                      };
++                      mipirx_1p8: LDO_REG3 {
++                              regulator-name = "mipirx_1p8";
++                              regulator-min-microvolt = <1800000>;
++                              regulator-max-microvolt = <1800000>;
++                      };
++                      hdmi_0p9: LDO_REG4 {
++                              regulator-name = "hdmi_0p9";
++                              regulator-min-microvolt = <900000>;
++                              regulator-max-microvolt = <900000>;
++                      };
++                      mipitx_0p9: LDO_REG5 {
++                              regulator-name = "mipitx_0p9";
++                              regulator-min-microvolt = <900000>;
++                              regulator-max-microvolt = <900000>;
++                      };
++                      mipirx_0p9: LDO_REG6 {
++                              regulator-name = "mipirx_0p9";
++                              regulator-min-microvolt = <900000>;
++                              regulator-max-microvolt = <900000>;
++                      };
++                      sdio_vdd: LDO_REG7 {
++                              regulator-name = "sdio_vdd";
++                              regulator-min-microvolt = <1800000>;
++                              regulator-max-microvolt = <3300000>;
++                      };
++              };
++      };
++};
++
++&i2c6 {
++      clock-frequency = <100000>;
++      i2c-sda-hold-time-ns = <300>;
++      i2c-sda-falling-time-ns = <510>;
++      i2c-scl-falling-time-ns = <510>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2c6_pins>;
++      status = "okay";
++
++      ov4689: ov4689@36 {
++              compatible = "ovti,ov4689";
++              reg = <0x36>;
++              clocks = <&clk_ext_camera>;
++              clock-names = "xclk";
++              //reset-gpio = <&sysgpio 18 0>;
++              rotation = <180>;
++
++              port {
++                      /* Parallel bus endpoint */
++                      ov4689_to_csi2rx0: endpoint {
++                              remote-endpoint = <&csi2rx0_from_ov4689>;
++                              bus-type = <4>;         /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <0>;
++                              data-lanes = <1 2 3 4>;
++                      };
++              };
++      };
++
++      imx219: imx219@10 {
++              compatible = "sony,imx219";
++              reg = <0x10>;
++              clocks = <&clk_ext_camera>;
++              clock-names = "xclk";
++              reset-gpio = <&sysgpio 10 0>;
++              //DOVDD-supply = <&v2v8>;
++              rotation = <0>;
++              orientation = <1>; //CAMERA_ORIENTATION_BACK
++
++              port {
++                      /* CSI2 bus endpoint */
++                      imx219_to_csi2rx0: endpoint {
++                              remote-endpoint = <&csi2rx0_from_imx219>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <0>;
++                              data-lanes = <2 1>;
++                              lane-polarities = <1 1 1>;
++                              link-frequencies = /bits/ 64 <456000000>;
++                      };
++              };
++      };
++
++      imx708: imx708@1a {
++              compatible = "sony,imx708";
++              reg = <0x1a>;
++              clocks = <&clk_ext_camera>;
++              reset-gpio = <&sysgpio 10 0>;
++
++              port {
++                      imx708_to_csi2rx0: endpoint {
++                              remote-endpoint = <&csi2rx0_from_imx708>;
++                              data-lanes = <1 2>;
++                              clock-noncontinuous;
++                              link-frequencies = /bits/ 64 <450000000>;
++                      };
++              };
++      };
++};
++
++&i2srx {
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2s_clk_pins &i2srx_pins>;
++};
++
++&i2srx_mst {
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2srx_clk_pins>;
++};
++
++&i2stx0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&mclk_ext_pins>;
++      status = "okay";
++};
++
++&i2stx1 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&i2stx_pins>;
++};
++
++&jpu {
++      status = "okay";
++};
++
++&mailbox_contrl0 {
++      status = "okay";
++};
++
++&mailbox_client0 {
++      status = "okay";
++};
++
++&mipi_dphy {
++      status = "okay";
++};
++
++&mipi_dsi {
++      status = "okay";
++
++      port {
++              dsi_out_port: endpoint@0 {
++                      remote-endpoint = <&panel_dsi_port>;
++              };
++              dsi_in_port: endpoint@1 {
++                      remote-endpoint = <&mipi_out>;
++              };
++      };
++
++      mipi_panel: panel@0 {
++              /*compatible = "";*/
++              status = "okay";
++      };
++};
++
++&pcie0 {
++      enable-gpios = <&sysgpio 32 GPIO_ACTIVE_HIGH>;
++      perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>;
++      phys = <&pciephy0>;
++      status = "disabled";
++};
++
++&pcie1 {
++      enable-gpios = <&sysgpio 21 GPIO_ACTIVE_HIGH>;
++      perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>;
++      phys = <&pciephy1>;
++      status = "disabled";
++};
++
++&pciephy0 {
++      starfive,sys-syscon = <&sys_syscon 0x18>;
++      starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>;
++};
++
++&pdm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pdm_pins>;
++      status = "disabled";
++};
++
++&pwmdac {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwmdac_pins>;
++};
++
++&qspi {
++      #address-cells = <1>;
++      #size-cells = <0>;
++      status = "okay";
++
++      nor_flash: flash@0 {
++              compatible = "jedec,spi-nor";
++              reg=<0>;
++              cdns,read-delay = <5>;
++              spi-max-frequency = <4687500>;
++              cdns,tshsl-ns = <1>;
++              cdns,tsd2d-ns = <1>;
++              cdns,tchsh-ns = <1>;
++              cdns,tslch-ns = <1>;
++
++              partitions {
++                      compatible = "fixed-partitions";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++
++                      spl@0 {
++                              reg = <0x0 0x40000>;
++                      };
++                      uboot@100000 {
++                              reg = <0x100000 0x300000>;
++                      };
++                      data@f00000 {
++                              reg = <0xf00000 0x100000>;
++                      };
++              };
++      };
++};
++
++&rgb_output {
++      status = "okay";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@0 {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      reg = <0>;
++
++                      hdmi_input0:endpoint@0 {
++                              reg = <0>;
++                              remote-endpoint = <&dc_out_dpi0>;
++                      };
++              };
++
++              port@1 {
++                      reg = <1>;
++
++                      hdmi_out:endpoint {
++                              remote-endpoint = <&tda998x_0_input>;
++                      };
++              };
++      };
++};
++
++&spdif {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spdif_pins>;
++};
++
++&spi0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi0_pins>;
++      status = "disabled";
++
++      spi_dev0: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&spi1 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi1_pins>;
++      status = "disabled";
++
++      spi_dev1: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&spi2 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi2_pins>;
++      status = "disabled";
++
++      spi_dev2: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&spi3 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi3_pins>;
++      status = "disabled";
++
++      spi_dev3: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&spi4 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi4_pins>;
++      status = "disabled";
++
++      spi_dev4: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&spi5 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi5_pins>;
++      status = "disabled";
++
++      spi_dev5: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&spi6 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi6_pins>;
++      status = "disabled";
++
++      spi_dev6: spi_dev@0 {
++              compatible = "rohm,dh2228fv";
++              reg = <0>;
++              pl022,com-mode = <1>;
++              spi-max-frequency = <10000000>;
++      };
++};
++
++&tda988x_pin {
++      pinctrl-names = "default";
++      pinctrl-0 = <&rgb_pad_pins>;
++      status = "disabled";
++};
++
++&tdm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&tdm_pins>;
++      status = "disabled";
++};
++
++&uart0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart0_pins>;
++      status = "okay";
++};
++
++&uart1 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart1_pins>;
++      status = "disabled";
++};
++
++&uart2 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart2_pins>;
++      status = "disabled";
++};
++
++&uart3 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart3_pins>;
++      status = "disabled";
++};
++
++&uart4 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart4_pins>;
++      status = "disabled";
++};
++
++&uart5 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart5_pins>;
++      status = "disabled";
++};
++
++&usb0 {
++      clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>,
++               <&stgcrg JH7110_STGCLK_USB0_STB>,
++               <&stgcrg JH7110_STGCLK_USB0_APB>,
++               <&stgcrg JH7110_STGCLK_USB0_AXI>,
++               <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>,
++               <&stgcrg JH7110_STGCLK_PCIE0_APB>;
++      clock-names = "lpm", "stb", "apb", "axi", "utmi_apb", "phy";
++      resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>,
++               <&stgcrg JH7110_STGRST_USB0_APB>,
++               <&stgcrg JH7110_STGRST_USB0_AXI>,
++               <&stgcrg JH7110_STGRST_USB0_UTMI_APB>,
++               <&stgcrg JH7110_STGRST_PCIE0_APB>;
++      reset-names = "pwrup", "apb", "axi", "utmi_apb", "phy";
++      pinctrl-names = "default";
++      pinctrl-0 = <&usb_pins>;
++      dr_mode = "host"; /* host or peripheral */
++      status = "disabled";
++};
++
++&usb_cdns3 {
++      phys = <&usbphy0>, <&pciephy0>;
++      phy-names = "cdns3,usb2-phy", "cdns3,usb3-phy";
++};
++
++&vin_sysctl {
++      status = "okay";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@0 {
++                      reg = <0>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++
++                      /* Parallel bus endpoint */
++                      parallel_from_sc2235: endpoint@0 {
++                              reg = <0>;
++                              remote-endpoint = <&sc2235_to_parallel>;
++                              bus-type = <5>;         /* Parallel */
++                              bus-width = <8>;
++                              data-shift = <2>;       /* lines 9:2 are used */
++                              hsync-active = <1>;
++                              vsync-active = <0>;
++                              pclk-sample = <1>;
++                              status = "okay";
++                      };
++              };
++
++              port@1 {
++                      reg = <1>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++
++                      /* CSI2 bus endpoint */
++                      csi2rx0_from_ov4689: endpoint@0 {
++                              reg = <0>;
++                              remote-endpoint = <&ov4689_to_csi2rx0>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <0>;
++                              data-lanes = <1 2 3 4>;
++                              status = "okay";
++                      };
++
++                      /* CSI2 bus endpoint */
++                      csi2rx0_from_imx219: endpoint@1 {
++                              reg = <1>;
++                              remote-endpoint = <&imx219_to_csi2rx0>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <0>;
++                              data-lanes = <2 1>;
++                              lane-polarities = <1 1 1>;
++                              status = "okay";
++                      };
++
++                      csi2rx0_from_imx708: endpoint@2 {
++                              reg = <2>;
++                              remote-endpoint = <&imx708_to_csi2rx0>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <0>;
++                              data-lanes = <2 1>;
++                              lane-polarities = <1 1 1>;
++                              status = "okay";
++                      };
++              };
++      };
++};
++
++&vpu_dec {
++      status = "okay";
++};
++
++&vpu_enc {
++      status = "okay";
++};
++
++&xrp {
++      memory-region = <&xrp_reserved>;
++      status = "okay";
++};
+--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
+@@ -196,11 +196,60 @@
+                       opp-750000000 {
+                                       opp-hz = /bits/ 64 <750000000>;
+                                       opp-microvolt = <800000>;
++                                      opp-suspend;
+                       };
+                       opp-1500000000 {
+                                       opp-hz = /bits/ 64 <1500000000>;
+                                       opp-microvolt = <1040000>;
+                       };
++                      /* CPU opp table for 1.25GHz */
++                      opp-312500000 {
++                                      opp-hz = /bits/ 64 <312500000>;
++                                      opp-microvolt = <800000>;
++                      };
++                      opp-417000000 {
++                                      opp-hz = /bits/ 64 <417000000>;
++                                      opp-microvolt = <800000>;
++                      };
++                      opp-625000000 {
++                                      opp-hz = /bits/ 64 <625000000>;
++                                      opp-microvolt = <800000>;
++                                      opp-suspend;
++                      };
++                      opp-1250000000 {
++                                      opp-hz = /bits/ 64 <1250000000>;
++                                      opp-microvolt = <1000000>;
++                      };
++      };
++
++      display: display-subsystem {
++              compatible = "starfive,jh7110-display","verisilicon,display-subsystem";
++              status = "disabled";
++      };
++
++      dsi_output: dsi-output {
++              compatible = "starfive,jh7110-display-encoder","verisilicon,dsi-encoder";
++              status = "disabled";
++      };
++
++      mailbox_client0: mailbox_client {
++              compatible = "starfive,mailbox-test";
++              mbox-names = "rx", "tx";
++              mboxes = <&mailbox_contrl0 0 1>,<&mailbox_contrl0 1 0>;
++              status = "disabled";
++      };
++
++      rgb_output: rgb-output {
++              compatible = "starfive,jh7110-rgb_output","verisilicon,rgb-encoder";
++              //verisilicon,dss-syscon = <&dssctrl>;
++              //verisilicon,mux-mask = <0x70 0x380>;
++              //verisilicon,mux-val = <0x40 0x280>;
++              status = "disabled";
++      };
++
++      tda988x_pin: tda988x_pin {
++              compatible = "starfive,tda998x_rgb_pin";
++              status = "disabled";
+       };
+       thermal-zones {
+@@ -349,7 +398,9 @@
+               ccache: cache-controller@2010000 {
+                       compatible = "starfive,jh7110-ccache", "sifive,ccache0", "cache";
+-                      reg = <0x0 0x2010000 0x0 0x4000>;
++                      reg = <0x0 0x2010000 0x0 0x4000>,
++                            <0x0 0x8000000 0x0 0x2000000>,
++                            <0x0 0xa000000 0x0 0x2000000>;
+                       interrupts = <1>, <3>, <4>, <2>;
+                       cache-block-size = <64>;
+                       cache-level = <2>;
+@@ -378,7 +429,8 @@
+                       clocks = <&syscrg JH7110_SYSCLK_UART0_CORE>,
+                                <&syscrg JH7110_SYSCLK_UART0_APB>;
+                       clock-names = "baudclk", "apb_pclk";
+-                      resets = <&syscrg JH7110_SYSRST_UART0_APB>;
++                      resets = <&syscrg JH7110_SYSRST_UART0_APB>,
++                               <&syscrg JH7110_SYSRST_UART0_CORE>;
+                       interrupts = <32>;
+                       reg-io-width = <4>;
+                       reg-shift = <2>;
+@@ -391,7 +443,8 @@
+                       clocks = <&syscrg JH7110_SYSCLK_UART1_CORE>,
+                                <&syscrg JH7110_SYSCLK_UART1_APB>;
+                       clock-names = "baudclk", "apb_pclk";
+-                      resets = <&syscrg JH7110_SYSRST_UART1_APB>;
++                      resets = <&syscrg JH7110_SYSRST_UART1_APB>,
++                               <&syscrg JH7110_SYSRST_UART1_CORE>;
+                       interrupts = <33>;
+                       reg-io-width = <4>;
+                       reg-shift = <2>;
+@@ -404,7 +457,8 @@
+                       clocks = <&syscrg JH7110_SYSCLK_UART2_CORE>,
+                                <&syscrg JH7110_SYSCLK_UART2_APB>;
+                       clock-names = "baudclk", "apb_pclk";
+-                      resets = <&syscrg JH7110_SYSRST_UART2_APB>;
++                      resets = <&syscrg JH7110_SYSRST_UART2_APB>,
++                               <&syscrg JH7110_SYSRST_UART2_CORE>;
+                       interrupts = <34>;
+                       reg-io-width = <4>;
+                       reg-shift = <2>;
+@@ -513,6 +567,25 @@
+                       status = "disabled";
+               };
++              spdif: spdif@100a0000 {
++                      compatible = "starfive,jh7110-spdif";
++                      reg = <0x0 0x100a0000 0x0 0x1000>;
++                      clocks = <&syscrg JH7110_SYSCLK_SPDIF_APB>,
++                               <&syscrg JH7110_SYSCLK_SPDIF_CORE>,
++                               <&syscrg JH7110_SYSCLK_AUDIO_ROOT>,
++                               <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++                               <&mclk_ext>, <&syscrg JH7110_SYSCLK_MCLK>;
++                      clock-names = "apb", "core",
++                                    "audroot", "mclk_inner",
++                                    "mclk_ext", "mclk";
++                      resets = <&syscrg JH7110_SYSRST_SPDIF_APB>;
++                      reset-names = "apb";
++                      interrupts = <84>;
++                      interrupt-names = "tx";
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
+               pwmdac: pwmdac@100b0000 {
+                       compatible = "starfive,jh7110-pwmdac";
+                       reg = <0x0 0x100b0000 0x0 0x1000>;
+@@ -526,6 +599,42 @@
+                       status = "disabled";
+               };
++              pdm: pdm@100d0000 {
++                      compatible = "starfive,jh7110-pdm";
++                      reg = <0x0 0x100d0000 0x0 0x1000>;
++                      reg-names = "pdm";
++                      clocks = <&syscrg JH7110_SYSCLK_PDM_DMIC>,
++                               <&syscrg JH7110_SYSCLK_PDM_APB>,
++                               <&syscrg JH7110_SYSCLK_MCLK>,
++                               <&mclk_ext>;
++                      clock-names = "pdm_mclk", "pdm_apb",
++                                    "clk_mclk", "mclk_ext";
++                      resets = <&syscrg JH7110_SYSRST_PDM_DMIC>,
++                               <&syscrg JH7110_SYSRST_PDM_APB>;
++                      reset-names = "pdm_dmic", "pdm_apb";
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
++              i2srx_mst: i2srx_mst@100e0000 {
++                      compatible = "starfive,jh7110-i2srx-master";
++                      reg = <0x0 0x100e0000 0x0 0x1000>;
++                      clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>,
++                               <&syscrg JH7110_SYSCLK_I2SRX_APB>,
++                               <&syscrg JH7110_SYSCLK_MCLK>,
++                               <&syscrg JH7110_SYSCLK_MCLK_INNER>,
++                               <&mclk_ext>;
++                      clock-names = "i2sclk", "apb", "mclk",
++                                    "mclk_inner","mclk_ext";
++                      resets = <&syscrg JH7110_SYSRST_I2SRX_APB>,
++                               <&syscrg JH7110_SYSRST_I2SRX_BCLK>;
++                      dmas = <&dma 24>;
++                      dma-names = "rx";
++                      starfive,syscon = <&sys_syscon 0x18 0x2 0x34 0x3FC00 0x24400>;
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
+               i2srx: i2s@100e0000 {
+                       compatible = "starfive,jh7110-i2srx";
+                       reg = <0x0 0x100e0000 0x0 0x1000>;
+@@ -622,6 +731,26 @@
+                       #reset-cells = <1>;
+               };
++              xrp: xrp@10230000 {
++                      compatible = "cdns,xrp";
++                      dma-coherent;
++                      reg = <0x0 0x10230000 0x0 0x00010000
++                             0x0 0x10240000 0x0 0x00010000>;
++                      clocks = <&stgcrg JH7110_STGCLK_HIFI4_CLK_CORE>;
++                      clock-names = "core_clk";
++                      resets = <&stgcrg JH7110_STGRST_HIFI4_CORE>,
++                               <&stgcrg JH7110_STGRST_HIFI4_AXI>;
++                      reset-names = "rst_core","rst_axi";
++                      starfive,stg-syscon = <&stg_syscon>;
++                      firmware-name = "hifi4_elf";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      ranges = <0x40000000 0x0 0x20000000 0x040000
++                              0x69c00000 0x0 0x69c00000 0x03000000>;
++                      status = "disabled";
++                      dsp@0 {};
++              };
++
+               stg_syscon: syscon@10240000 {
+                       compatible = "starfive,jh7110-stg-syscon", "syscon";
+                       reg = <0x0 0x10240000 0x0 0x1000>;
+@@ -633,7 +762,8 @@
+                       clocks = <&syscrg JH7110_SYSCLK_UART3_CORE>,
+                                <&syscrg JH7110_SYSCLK_UART3_APB>;
+                       clock-names = "baudclk", "apb_pclk";
+-                      resets = <&syscrg JH7110_SYSRST_UART3_APB>;
++                      resets = <&syscrg JH7110_SYSRST_UART3_APB>,
++                               <&syscrg JH7110_SYSRST_UART3_CORE>;
+                       interrupts = <45>;
+                       reg-io-width = <4>;
+                       reg-shift = <2>;
+@@ -646,7 +776,8 @@
+                       clocks = <&syscrg JH7110_SYSCLK_UART4_CORE>,
+                                <&syscrg JH7110_SYSCLK_UART4_APB>;
+                       clock-names = "baudclk", "apb_pclk";
+-                      resets = <&syscrg JH7110_SYSRST_UART4_APB>;
++                      resets = <&syscrg JH7110_SYSRST_UART4_APB>,
++                               <&syscrg JH7110_SYSRST_UART4_CORE>;
+                       interrupts = <46>;
+                       reg-io-width = <4>;
+                       reg-shift = <2>;
+@@ -659,7 +790,8 @@
+                       clocks = <&syscrg JH7110_SYSCLK_UART5_CORE>,
+                                <&syscrg JH7110_SYSCLK_UART5_APB>;
+                       clock-names = "baudclk", "apb_pclk";
+-                      resets = <&syscrg JH7110_SYSRST_UART5_APB>;
++                      resets = <&syscrg JH7110_SYSRST_UART5_APB>,
++                               <&syscrg JH7110_SYSRST_UART5_CORE>;
+                       interrupts = <47>;
+                       reg-io-width = <4>;
+                       reg-shift = <2>;
+@@ -919,6 +1051,18 @@
+                                     "ch2", "ch3";
+               };
++              mailbox_contrl0: mailbox@13060000 {
++                      compatible = "starfive,mail_box";
++                      reg = <0x0 0x13060000 0x0 0x0001000>;
++                      clocks = <&syscrg JH7110_SYSCLK_MAILBOX_APB>;
++                      clock-names = "clk_apb";
++                      resets = <&syscrg JH7110_SYSRST_MAILBOX_APB>;
++                      reset-names = "mbx_rre";
++                      interrupts = <26 27>;
++                      #mbox-cells = <2>;
++                      status = "disabled";
++              };
++
+               watchdog@13070000 {
+                       compatible = "starfive,jh7110-wdt";
+                       reg = <0x0 0x13070000 0x0 0x10000>;
+@@ -929,6 +1073,112 @@
+                                <&syscrg JH7110_SYSRST_WDT_CORE>;
+               };
++              jpu: jpu@13090000 {
++                      compatible = "starfive,jpu";
++                      dma-coherent;
++                      reg = <0x0 0x13090000 0x0 0x300>;
++                      interrupts = <14>;
++                      clocks = <&syscrg JH7110_SYSCLK_CODAJ12_AXI>,
++                               <&syscrg JH7110_SYSCLK_CODAJ12_CORE>,
++                               <&syscrg JH7110_SYSCLK_CODAJ12_APB>,
++                               <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>,
++                               <&syscrg JH7110_SYSCLK_VDEC_MAIN>,
++                               <&syscrg JH7110_SYSCLK_VDEC_JPG>;
++                      clock-names = "axi_clk", "core_clk", "apb_clk",
++                                    "noc_bus", "main_clk", "dec_clk";
++                      resets = <&syscrg JH7110_SYSRST_CODAJ12_AXI>,
++                               <&syscrg JH7110_SYSRST_CODAJ12_CORE>,
++                               <&syscrg JH7110_SYSRST_CODAJ12_APB>;
++                      reset-names = "rst_axi", "rst_core", "rst_apb";
++                      power-domains = <&pwrc JH7110_PD_VDEC>;
++                      status = "disabled";
++              };
++
++              vpu_dec: vpu_dec@130a0000 {
++                      compatible = "starfive,vdec";
++                      dma-coherent;
++                      reg = <0x0 0x130a0000 0x0 0x10000>;
++                      interrupts = <13>;
++                      clocks = <&syscrg JH7110_SYSCLK_WAVE511_AXI>,
++                               <&syscrg JH7110_SYSCLK_WAVE511_BPU>,
++                               <&syscrg JH7110_SYSCLK_WAVE511_VCE>,
++                               <&syscrg JH7110_SYSCLK_WAVE511_APB>,
++                               <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>,
++                               <&syscrg JH7110_SYSCLK_VDEC_MAIN>;
++                      clock-names = "axi_clk", "bpu_clk", "vce_clk",
++                                    "apb_clk", "noc_bus", "main_clk";
++                      resets = <&syscrg JH7110_SYSRST_WAVE511_AXI>,
++                               <&syscrg JH7110_SYSRST_WAVE511_BPU>,
++                               <&syscrg JH7110_SYSRST_WAVE511_VCE>,
++                               <&syscrg JH7110_SYSRST_WAVE511_APB>,
++                               <&syscrg JH7110_SYSRST_AXIMEM0_AXI>;
++                      reset-names = "rst_axi", "rst_bpu", "rst_vce",
++                                    "rst_apb", "rst_sram";
++                      starfive,vdec_noc_ctrl;
++                      power-domains = <&pwrc JH7110_PD_VDEC>;
++                      status = "disabled";
++              };
++
++              vpu_enc: vpu_enc@130b0000 {
++                      compatible = "starfive,venc";
++                      dma-coherent;
++                      reg = <0x0 0x130b0000 0x0 0x10000>;
++                      interrupts = <15>;
++                      clocks = <&syscrg JH7110_SYSCLK_WAVE420L_AXI>,
++                               <&syscrg JH7110_SYSCLK_WAVE420L_BPU>,
++                               <&syscrg JH7110_SYSCLK_WAVE420L_VCE>,
++                               <&syscrg JH7110_SYSCLK_WAVE420L_APB>,
++                               <&syscrg JH7110_SYSCLK_NOC_BUS_VENC_AXI>;
++                      clock-names = "axi_clk", "bpu_clk", "vce_clk",
++                                    "apb_clk", "noc_bus";
++                      resets = <&syscrg JH7110_SYSRST_WAVE420L_AXI>,
++                               <&syscrg JH7110_SYSRST_WAVE420L_BPU>,
++                               <&syscrg JH7110_SYSRST_WAVE420L_VCE>,
++                               <&syscrg JH7110_SYSRST_WAVE420L_APB>,
++                               <&syscrg JH7110_SYSRST_AXIMEM1_AXI>;
++                      reset-names = "rst_axi", "rst_bpu", "rst_vce",
++                                    "rst_apb", "rst_sram";
++                      starfive,venc_noc_ctrl;
++                      power-domains = <&pwrc JH7110_PD_VENC>;
++                      status = "disabled";
++              };
++
++              can0: can@130d0000 {
++                      compatible = "starfive,jh7110-can", "ipms,can";
++                      reg = <0x0 0x130d0000 0x0 0x1000>;
++                      interrupts = <112>;
++                      clocks = <&syscrg JH7110_SYSCLK_CAN0_APB>,
++                               <&syscrg JH7110_SYSCLK_CAN0_CAN>,
++                               <&syscrg JH7110_SYSCLK_CAN0_TIMER>;
++                      clock-names = "apb_clk", "core_clk", "timer_clk";
++                      resets = <&syscrg JH7110_SYSRST_CAN0_APB>,
++                               <&syscrg JH7110_SYSRST_CAN0_CORE>,
++                               <&syscrg JH7110_SYSRST_CAN0_TIMER>;
++                      reset-names = "rst_apb", "rst_core", "rst_timer";
++                      frequency = <40000000>;
++                      starfive,sys-syscon = <&sys_syscon 0x10 0x3 0x8>;
++                      syscon,can_or_canfd = <0>;
++                      status = "disabled";
++              };
++
++              can1: can@130e0000 {
++                      compatible = "starfive,jh7110-can", "ipms,can";
++                      reg = <0x0 0x130e0000 0x0 0x1000>;
++                      interrupts = <113>;
++                      clocks = <&syscrg JH7110_SYSCLK_CAN1_APB>,
++                               <&syscrg JH7110_SYSCLK_CAN1_CAN>,
++                               <&syscrg JH7110_SYSCLK_CAN1_TIMER>;
++                      clock-names = "apb_clk", "core_clk", "timer_clk";
++                      resets = <&syscrg JH7110_SYSRST_CAN1_APB>,
++                               <&syscrg JH7110_SYSRST_CAN1_CORE>,
++                               <&syscrg JH7110_SYSRST_CAN1_TIMER>;
++                      reset-names = "rst_apb", "rst_core", "rst_timer";
++                      frequency = <40000000>;
++                      starfive,sys-syscon = <&sys_syscon 0x88 0x12 0x40000>;
++                      syscon,can_or_canfd = <0>;
++                      status = "disabled";
++              };
++
+               crypto: crypto@16000000 {
+                       compatible = "starfive,jh7110-crypto";
+                       reg = <0x0 0x16000000 0x0 0x4000>;
+@@ -1119,6 +1369,42 @@
+                       #power-domain-cells = <1>;
+               };
++              rtc: rtc@17040000 {
++                      compatible = "starfive,jh7110-rtc";
++                      reg = <0x0 0x17040000 0x0 0x10000>;
++                      interrupts = <10>, <11>, <12>;
++                      interrupt-names = "rtc_ms_pulse", "rtc_sec_pulse", "rtc";
++                      clocks = <&aoncrg JH7110_AONCLK_RTC_APB>,
++                               <&aoncrg JH7110_AONCLK_RTC_CAL>;
++                      clock-names = "pclk", "cal_clk";
++                      resets = <&aoncrg JH7110_AONRST_RTC_32K>,
++                               <&aoncrg JH7110_AONRST_RTC_APB>,
++                               <&aoncrg JH7110_AONRST_RTC_CAL>;
++                      reset-names = "rst_osc", "rst_apb", "rst_cal";
++                      rtc,cal-clock-freq = <1000000>;
++              };
++
++              gpu: gpu@18000000 {
++                      compatible = "img-gpu";
++                      reg = <0x0 0x18000000 0x0 0x100000>,
++                              <0x0 0x130C000 0x0 0x10000>;
++                      clocks = <&syscrg JH7110_SYSCLK_GPU_CORE>,
++                               <&syscrg JH7110_SYSCLK_GPU_APB>,
++                               <&syscrg JH7110_SYSCLK_GPU_RTC_TOGGLE>,
++                               <&syscrg JH7110_SYSCLK_GPU_CORE_CLK>,
++                               <&syscrg JH7110_SYSCLK_GPU_SYS_CLK>,
++                               <&syscrg JH7110_SYSCLK_NOC_BUS_GPU_AXI>;
++                      clock-names = "clk_bv", "clk_apb", "clk_rtc",
++                                      "clk_core", "clk_sys", "clk_axi";
++                      resets = <&syscrg JH7110_SYSRST_GPU_APB>,
++                               <&syscrg JH7110_SYSRST_GPU_DOMA>;
++                      reset-names = "rst_apb", "rst_doma";
++                      power-domains = <&pwrc JH7110_PD_GPUA>;
++                      interrupts = <82>;
++                      current-clock = <8000000>;
++                      status = "disabled";
++              };
++
+               csi2rx: csi-bridge@19800000 {
+                       compatible = "starfive,jh7110-csi2rx";
+                       reg = <0x0 0x19800000 0x0 0x10000>;
+@@ -1145,6 +1431,67 @@
+                       status = "disabled";
+               };
++              vin_sysctl: vin_sysctl@19800000 {
++                      compatible = "starfive,jh7110-vin";
++                      reg = <0x0 0x19800000 0x0 0x10000>,
++                              <0x0 0x19810000 0x0 0x10000>,
++                              <0x0 0x19820000 0x0 0x10000>,
++                              <0x0 0x19840000 0x0 0x10000>,
++                              <0x0 0x19870000 0x0 0x30000>,
++                              <0x0 0x11840000 0x0 0x10000>,
++                              <0x0 0x17030000 0x0 0x10000>,
++                              <0x0 0x13020000 0x0 0x10000>;
++                      reg-names = "csi2rx", "vclk", "vrst", "sctrl",
++                              "isp", "trst", "pmu", "syscrg";
++                      clocks = <&ispcrg JH7110_ISPCLK_DOM4_APB_FUNC>,
++                               <&ispcrg JH7110_ISPCLK_VIN_APB>,
++                               <&ispcrg JH7110_ISPCLK_VIN_SYS>,
++                               <&ispcrg JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C>,
++                               <&ispcrg JH7110_ISPCLK_DVP_INV>,
++                               <&ispcrg JH7110_ISPCLK_VIN_P_AXI_WR>,
++                               <&ispcrg JH7110_ISPCLK_MIPI_RX0_PXL>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>,
++                               <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>,
++                               <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>,
++                               <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>,
++                               <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>,
++                               <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>,
++                               <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>;
++                      clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk",
++                              "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr",
++                              "clk_mipi_rx0_pxl", "clk_pixel_clk_if0",
++                              "clk_pixel_clk_if1", "clk_pixel_clk_if2",
++                              "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in",
++                              "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0",
++                              "clk_ispcore_2x", "clk_isp_axi";
++                      resets = <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_P>,
++                               <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_C>,
++                               <&ispcrg JH7110_ISPRST_VIN_APB>,
++                               <&ispcrg JH7110_ISPRST_VIN_SYS>,
++                               <&ispcrg JH7110_ISPRST_VIN_P_AXI_RD>,
++                               <&ispcrg JH7110_ISPRST_VIN_P_AXI_WR>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>,
++                               <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>,
++                               <&ispcrg JH7110_ISPRST_M31DPHY_HW>,
++                               <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>,
++                               <&syscrg JH7110_SYSRST_ISP_TOP>,
++                               <&syscrg JH7110_SYSRST_ISP_TOP_AXI>;
++                      reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk",
++                              "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0",
++                              "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3",
++                              "rst_m31dphy_hw", "rst_m31dphy_b09_always_on",
++                              "rst_isp_top_n", "rst_isp_top_axi";
++                      starfive,aon-syscon = <&aon_syscon 0x00>;
++                      power-domains = <&pwrc JH7110_PD_ISP>;
++                      /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */
++                      interrupts = <92 87 88 89 90>;
++                      status = "disabled";
++              };
++
+               ispcrg: clock-controller@19810000 {
+                       compatible = "starfive,jh7110-ispcrg";
+                       reg = <0x0 0x19810000 0x0 0x10000>;
+@@ -1175,6 +1522,66 @@
+                       #phy-cells = <0>;
+               };
++              dc8200: dc8200@29400000 {
++                      compatible = "starfive,jh7110-dc8200","verisilicon,dc8200";
++                      verisilicon,dss-syscon = <&dssctrl>;//20220624 panel syscon
++                      reg = <0x0 0x29400000 0x0 0x100>,
++                            <0x0 0x29400800 0x0 0x2000>,
++                            <0x0 0x17030000 0x0 0x1000>;
++                      interrupts = <95>;
++                      clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>,
++                               <&syscrg JH7110_SYSCLK_VOUT_SRC>,
++                               <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>,
++                               <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>,
++                               <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>,
++                               <&voutcrg JH7110_VOUTCLK_DC8200_PIX1>,
++                               <&voutcrg JH7110_VOUTCLK_DC8200_AXI>,
++                               <&voutcrg JH7110_VOUTCLK_DC8200_CORE>,
++                               <&voutcrg JH7110_VOUTCLK_DC8200_AHB>,
++                               <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>,
++                               <&voutcrg JH7110_VOUTCLK_DOM_VOUT_TOP_LCD>,
++                               <&hdmitx0_pixelclk>,
++                               <&voutcrg JH7110_VOUTCLK_DC8200_PIX>;
++                      clock-names = "noc_disp","vout_src",
++                         "top_vout_axi","top_vout_ahb",
++                         "pix_clk","vout_pix1",
++                         "axi_clk","core_clk","vout_ahb",
++                         "vout_top_axi","vout_top_lcd","hdmitx0_pixelclk","dc8200_pix0";
++                      resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>,
++                               <&voutcrg JH7110_VOUTRST_DC8200_AXI>,
++                               <&voutcrg JH7110_VOUTRST_DC8200_AHB>,
++                               <&voutcrg JH7110_VOUTRST_DC8200_CORE>,
++                               <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>;
++                      reset-names = "rst_vout_src","rst_axi","rst_ahb","rst_core",
++                                      "rst_noc_disp";
++                      status = "disabled";
++              };
++
++              hdmi: hdmi@29590000 {
++                      compatible = "starfive,jh7110-hdmi","inno,hdmi";
++                      reg = <0x0 0x29590000 0x0 0x4000>;
++                      interrupts = <99>;
++                      /*interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;*/
++                      /*clocks = <&cru  PCLK_HDMI>;*/
++                      /*clock-names = "pclk";*/
++                      /*pinctrl-names = "default";*/
++                      /*pinctrl-0 = <&hdmi_ctl>;*/
++                      clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>,
++                               <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>,
++                               <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>,
++                               <&hdmitx0_pixelclk>;
++                      clock-names = "sysclk", "mclk","bclk","pclk";
++                      resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>;
++                      reset-names = "hdmi_tx";
++                      #sound-dai-cells = <0>;
++                      status = "disabled";
++              };
++
++              dssctrl: dssctrl@295B0000 {
++                      compatible = "starfive,jh7110-dssctrl","verisilicon,dss-ctrl", "syscon";
++                      reg = <0 0x295B0000 0 0x90>;
++              };
++
+               voutcrg: clock-controller@295c0000 {
+                       compatible = "starfive,jh7110-voutcrg";
+                       reg = <0x0 0x295c0000 0x0 0x10000>;
+@@ -1193,6 +1600,67 @@
+                       power-domains = <&pwrc JH7110_PD_VOUT>;
+               };
++              mipi_dsi: mipi@295d0000 {
++                      compatible = "starfive,jh7110-mipi_dsi","cdns,dsi";
++                      reg = <0x0 0x295d0000 0x0 0x10000>;
++                      interrupts = <98>;
++                      reg-names = "dsi";
++                      clocks = <&voutcrg JH7110_VOUTCLK_DSITX_SYS>,
++                               <&voutcrg JH7110_VOUTCLK_DSITX_APB>,
++                               <&voutcrg JH7110_VOUTCLK_DSITX_TXESC>,
++                               <&voutcrg JH7110_VOUTCLK_DSITX_DPI>;
++                      clock-names = "dpi", "apb", "txesc", "sys";
++                      resets = <&voutcrg JH7110_VOUTRST_DSITX_DPI>,
++                               <&voutcrg JH7110_VOUTRST_DSITX_APB>,
++                               <&voutcrg JH7110_VOUTRST_DSITX_RXESC>,
++                               <&voutcrg JH7110_VOUTRST_DSITX_SYS>,
++                               <&voutcrg JH7110_VOUTRST_DSITX_TXBYTEHS>,
++                               <&voutcrg JH7110_VOUTRST_DSITX_TXESC>;
++                      reset-names = "dsi_dpi", "dsi_apb", "dsi_rxesc",
++                                      "dsi_sys", "dsi_txbytehs", "dsi_txesc";
++                      phys = <&mipi_dphy>;
++                      phy-names = "dphy";
++                      status = "disabled";
++              };
++
++              mipi_dphy: mipi-dphy@295e0000{
++                      compatible = "starfive,jh7110-mipi-dphy-tx","m31,mipi-dphy-tx";
++                      reg = <0x0 0x295e0000 0x0 0x10000>;
++                      clocks = <&voutcrg JH7110_VOUTCLK_MIPITX_DPHY_TXESC>;
++                      clock-names = "dphy_txesc";
++                      resets = <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_SYS>,
++                               <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS>;
++                      reset-names = "dphy_sys", "dphy_txbytehs";
++                      #phy-cells = <0>;
++                      status = "disabled";
++              };
++
++              co_process: e24@6e210000 {
++                      compatible = "starfive,e24";
++                      dma-coherent;
++                      reg = <0x0 0x6e210000 0x0 0x00001000>,
++                              <0x0 0x6e211000 0x0 0x0003f000>;
++                      reg-names = "ecmd", "espace";
++                      clocks = <&stgcrg JH7110_STGCLK_E2_RTC>,
++                               <&stgcrg JH7110_STGCLK_E2_CORE>,
++                               <&stgcrg JH7110_STGCLK_E2_DBG>;
++                      clock-names = "clk_rtc", "clk_core", "clk_dbg";
++                      resets = <&stgcrg JH7110_STGRST_E24_CORE>;
++                      reset-names = "e24_core";
++                      starfive,stg-syscon = <&stg_syscon>;
++                      interrupt-parent = <&plic>;
++                      firmware-name = "e24_elf";
++                      irq-mode = <1>;
++                      mbox-names = "tx", "rx";
++                      mboxes = <&mailbox_contrl0 0 2>,
++                               <&mailbox_contrl0 2 0>;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      ranges = <0x6ce00000 0x0 0x6ce00000 0x1600000>;
++                      status = "disabled";
++                      dsp@0 {};
++              };
++
+               pcie0: pcie@940000000 {
+                       compatible = "starfive,jh7110-pcie";
+                       reg = <0x9 0x40000000 0x0 0x1000000>,
diff --git a/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch b/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch
new file mode 100644 (file)
index 0000000..7a23bf9
--- /dev/null
@@ -0,0 +1,728 @@
+From cae7550054ca0cd940bbc1501ae5611f5d2957e6 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Wed, 20 Sep 2023 14:53:22 +0800
+Subject: [PATCH 057/116] riscv: dts: starfive: Add JH7110 EVB expanded device
+ tree
+
+Add JH7110 EVB expanded device tree.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile         |  11 +-
+ .../starfive/jh7110-evb-can-pdm-pwmdac.dts    | 102 ++++++++++++++++
+ .../dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts  |  37 ++++++
+ .../dts/starfive/jh7110-evb-i2s-ac108.dts     |  72 ++++++++++++
+ .../dts/starfive/jh7110-evb-pcie-i2s-sd.dts   | 111 ++++++++++++++++++
+ .../dts/starfive/jh7110-evb-spi-uart2.dts     |  65 ++++++++++
+ .../starfive/jh7110-evb-uart1-rgb2hdmi.dts    |  57 +++++++++
+ .../starfive/jh7110-evb-uart4-emmc-spdif.dts  |  78 ++++++++++++
+ .../starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts |  95 +++++++++++++++
+ .../dts/starfive/jh7110-evb-usbdevice.dts     |  35 ++++++
+ 10 files changed, 662 insertions(+), 1 deletion(-)
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -12,4 +12,13 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
+-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb                 \
++                             jh7110-evb-pcie-i2s-sd.dtb       \
++                             jh7110-evb-spi-uart2.dtb         \
++                             jh7110-evb-uart4-emmc-spdif.dtb  \
++                             jh7110-evb-uart5-pwm-i2c-tdm.dtb \
++                             jh7110-evb-dvp-rgb2hdmi.dtb      \
++                             jh7110-evb-can-pdm-pwmdac.dtb    \
++                             jh7110-evb-i2s-ac108.dtb         \
++                             jh7110-evb-usbdevice.dtb         \
++                             jh7110-evb-uart1-rgb2hdmi.dtb
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts
+@@ -0,0 +1,102 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++      sound2: snd-card2 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-PDM-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "i2s";
++                      bitclock-master = <&dailink_master>;
++                      frame-master = <&dailink_master>;
++
++                      dailink_master:cpu {
++                              sound-dai = <&i2srx_mst>;
++                      };
++
++                      dailink_slave:codec {
++                              sound-dai = <&pdm>;
++                      };
++              };
++      };
++
++      pwmdac_codec: pwmdac-codec {
++              compatible = "linux,spdif-dit";
++              #sound-dai-cells = <0>;
++      };
++
++      sound3: snd-card3 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "left_j";
++                      bitclock-master = <&sndcpu0>;
++                      frame-master = <&sndcpu0>;
++
++                      sndcpu0: cpu {
++                              sound-dai = <&pwmdac>;
++                      };
++
++                      codec {
++                              sound-dai = <&pwmdac_codec>;
++                      };
++              };
++      };
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&usb0 {
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&can0 {
++      status = "okay";
++};
++
++&can1 {
++      status = "okay";
++};
++
++&i2srx_mst {
++      status = "okay";
++};
++
++&pwmdac {
++      status = "okay";
++};
++
++&pdm {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts
+@@ -0,0 +1,37 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&vin_sysctl {
++      pinctrl-names = "default";
++      pinctrl-0 = <&dvp_pins>;
++};
++
++&rgb_output {
++      status = "okay";
++};
++
++&tda988x_pin {
++      status = "okay";
++};
++
++&dsi_output {
++      status = "disabled";
++};
++
++&mipi_dsi {
++      status = "disabled";
++};
++
++&mipi_dphy {
++      status = "disabled";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts
+@@ -0,0 +1,72 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++      /* i2s + ac108 */
++      sound0: snd-card0 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-AC108-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "i2s";
++                      bitclock-master = <&sndcodec1>;
++                      frame-master = <&sndcodec1>;
++
++                      widgets = "Microphone", "Mic Jack",
++                                "Line", "Line In",
++                                "Line", "Line Out",
++                                "Speaker", "Speaker",
++                                "Headphone", "Headphone Jack";
++                      routing = "Headphone Jack", "HP_L",
++                                "Headphone Jack", "HP_R",
++                                "Speaker", "SPK_LP",
++                                "Speaker", "SPK_LN",
++                                "LINPUT1", "Mic Jack",
++                                "LINPUT3", "Mic Jack",
++                                "RINPUT1", "Mic Jack",
++                                "RINPUT2", "Mic Jack";
++
++                      cpu {
++                              sound-dai = <&i2srx>;
++                      };
++
++                      sndcodec1: codec {
++                              sound-dai = <&ac108>;
++                              clocks = <&ac108_mclk>;
++                              clock-names = "mclk";
++                      };
++              };
++      };
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&i2c0 {
++      status = "okay";
++};
++
++&i2srx {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts
+@@ -0,0 +1,111 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++      /* i2s + wm8960 */
++      sound6: snd-card6 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-WM8960-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      status = "okay";
++                      format = "i2s";
++                      bitclock-master = <&sndcodec1>;
++                      frame-master = <&sndcodec1>;
++
++                      widgets = "Microphone", "Mic Jack",
++                                "Line", "Line In",
++                                "Line", "Line Out",
++                                "Speaker", "Speaker",
++                                "Headphone", "Headphone Jack";
++                      routing = "Headphone Jack", "HP_L",
++                                "Headphone Jack", "HP_R",
++                                "Speaker", "SPK_LP",
++                                "Speaker", "SPK_LN",
++                                "LINPUT1", "Mic Jack",
++                                "LINPUT3", "Mic Jack",
++                                "RINPUT1", "Mic Jack",
++                                "RINPUT2", "Mic Jack";
++                      cpu0 {
++                              sound-dai = <&i2srx>;
++                      };
++                      cpu1 {
++                              sound-dai = <&i2stx1>;
++                      };
++
++                      sndcodec1:codec {
++                              sound-dai = <&wm8960>;
++                              clocks = <&wm8960_mclk>;
++                              clock-names = "mclk";
++                      };
++              };
++      };
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&pcie0 {
++      status = "okay";
++};
++
++&uart3 {
++      status = "okay";
++};
++
++&i2c0 {
++      status = "okay";
++};
++
++&usb0 {
++      clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>,
++               <&stgcrg JH7110_STGCLK_USB0_STB>,
++               <&stgcrg JH7110_STGCLK_USB0_APB>,
++               <&stgcrg JH7110_STGCLK_USB0_AXI>,
++               <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>;
++      clock-names = "lpm", "stb", "apb", "axi", "utmi_apb";
++      resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>,
++               <&stgcrg JH7110_STGRST_USB0_APB>,
++               <&stgcrg JH7110_STGRST_USB0_AXI>,
++               <&stgcrg JH7110_STGRST_USB0_UTMI_APB>;
++      reset-names = "pwrup", "apb", "axi", "utmi_apb";
++      dr_mode = "host"; /*host or peripheral*/
++      starfive,usb2-only;
++      pinctrl-names = "default";
++      pinctrl-0 = <&usb_pins>;
++      status = "okay";
++};
++
++&i2srx {
++      status = "okay";
++};
++
++&i2stx1 {
++      status = "okay";
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts
+@@ -0,0 +1,65 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&usb0 {
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&uart2 {
++      status = "okay";
++};
++
++&spi0 {
++      status = "okay";
++};
++
++&spi1 {
++      status = "okay";
++};
++
++&spi2 {
++      status = "okay";
++};
++
++&spi3 {
++      status = "okay";
++};
++
++&spi4 {
++      status = "okay";
++};
++
++&spi5 {
++      status = "okay";
++};
++
++&spi6 {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts
+@@ -0,0 +1,57 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&usb0 {
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&uart1 {
++      status = "okay";
++};
++
++&rgb_output {
++      status = "okay";
++};
++
++&tda988x_pin {
++      status = "okay";
++};
++
++&dsi_output {
++      status = "disabled";
++};
++
++&mipi_dsi {
++      status = "disabled";
++};
++
++&mipi_dphy {
++      status = "disabled";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts
+@@ -0,0 +1,78 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++      spdif_transmitter: spdif_transmitter {
++              compatible = "linux,spdif-dit";
++              #sound-dai-cells = <0>;
++      };
++
++      sound4: snd-card4 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-SPDIF-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "left_j";
++                      bitclock-master = <&sndcpu0>;
++                      frame-master = <&sndcpu0>;
++
++                      sndcpu0: cpu {
++                              sound-dai = <&spdif>;
++                      };
++
++                      codec {
++                              sound-dai = <&spdif_transmitter>;
++                      };
++              };
++      };
++};
++
++&usb0 {
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&uart4 {
++      status = "okay";
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&emmc0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <8>;
++      cap-mmc-highspeed;
++      mmc-hs200-1_8v;
++      non-removable;
++      cap-mmc-hw-reset;
++      board-is-evb;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&pwm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwm_ch6to7_pins>;
++      status = "okay";
++};
++
++&spdif {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts
+@@ -0,0 +1,95 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++
++      sound5: snd-card5 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-TDM-Sound-Card";
++              simple-audio-card,widgets = "Microphone", "Mic Jack",
++                                          "Line", "Line In",
++                                          "Line", "Line Out",
++                                          "Speaker", "Speaker",
++                                          "Headphone", "Headphone Jack";
++              simple-audio-card,routing = "Headphone Jack", "HP_L",
++                                          "Headphone Jack", "HP_R",
++                                          "Speaker", "SPK_LP",
++                                          "Speaker", "SPK_LN",
++                                          "LINPUT1", "Mic Jack",
++                                          "LINPUT3", "Mic Jack",
++                                          "RINPUT1", "Mic Jack",
++                                          "RINPUT2", "Mic Jack";
++
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "dsp_a";
++                      bitclock-master = <&dailink_master>;
++                      frame-master = <&dailink_master>;
++
++                      cpu {
++                              sound-dai = <&tdm>;
++                      };
++                      dailink_master: codec {
++                              sound-dai = <&wm8960>;
++                              clocks = <&wm8960_mclk>;
++                      };
++              };
++      };
++};
++
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&usb0 {
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
++
++&uart5 {
++      status = "okay";
++};
++
++&pwm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwm_ch0to3_pins &pwm_ch4to5_pins>;
++      status = "okay";
++};
++
++&tdm {
++      status = "okay";
++};
++
++&i2c0 {
++      status = "okay";
++};
++
++&i2c1 {
++      status = "okay";
++};
++
++&i2c3 {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts
+@@ -0,0 +1,35 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++/dts-v1/;
++#include "jh7110-evb.dtsi"
++
++/ {
++      model = "StarFive JH7110 EVB";
++      compatible = "starfive,jh7110-evb", "starfive,jh7110";
++};
++
++/* default sd card */
++&mmc0 {
++      assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>;
++      assigned-clock-rates = <50000000>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdcard0_pins>;
++      max-frequency = <100000000>;
++      card-detect-delay = <300>;
++      bus-width = <4>;
++      broken-cd;
++      post-power-on-delay-ms = <200>;
++      status = "okay";
++};
++
++&usb0 {
++      dr_mode = "peripheral"; /*host or peripheral*/
++      status = "okay";
++};
++
++&pcie1 {
++      status = "okay";
++};
diff --git a/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch b/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch
new file mode 100644 (file)
index 0000000..a75ffc2
--- /dev/null
@@ -0,0 +1,485 @@
+From e9122ceaf2d8767753e2a126c14b29b78280446d Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 19 Sep 2023 21:35:39 +0800
+Subject: [PATCH 058/116] riscv: dts: starfive: Add evb-overlay dtso subdir
+
+Create subdir evb-overlay/ and add overlay .dtso for JH7110 EVB.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile         |   1 +
+ .../boot/dts/starfive/evb-overlay/Makefile    |   7 +
+ .../evb-overlay/jh7110-evb-overlay-can.dtso   |  24 ++++
+ .../jh7110-evb-overlay-rgb2hdmi.dtso          |  24 ++++
+ .../evb-overlay/jh7110-evb-overlay-sdio.dtso  |  78 +++++++++++
+ .../evb-overlay/jh7110-evb-overlay-spi.dtso   |  72 ++++++++++
+ .../jh7110-evb-overlay-uart4-emmc.dtso        | 130 ++++++++++++++++++
+ .../jh7110-evb-overlay-uart5-pwm.dtso         |  92 +++++++++++++
+ 8 files changed, 428 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/Makefile
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
++subdir-y += evb-overlay
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb                 \
+                              jh7110-evb-pcie-i2s-sd.dtb       \
+                              jh7110-evb-spi-uart2.dtb         \
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb-overlay-can.dtbo            \
++                              jh7110-evb-overlay-sdio.dtbo            \
++                              jh7110-evb-overlay-spi.dtbo             \
++                              jh7110-evb-overlay-uart4-emmc.dtbo      \
++                              jh7110-evb-overlay-uart5-pwm.dtbo       \
++                              jh7110-evb-overlay-rgb2hdmi.dtbo
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso
+@@ -0,0 +1,24 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //can0
++      fragment@0 {
++              target-path = "/soc/can@130d0000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //can1
++      fragment@1 {
++              target-path = "/soc/can@130e0000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso
+@@ -0,0 +1,24 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //hdmi_output
++      fragment@0 {
++              target-path = "/tda988x_pin";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //uart1
++      fragment@1 {
++              target-path = "/soc/serial@10010000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso
+@@ -0,0 +1,78 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //sysgpio
++      fragment@0 {
++              target-path = "/soc/pinctrl@13040000";
++              __overlay__ {
++                      dt_sdcard1_pins: dt-sdcard1-0 {
++                              sdcard-pins {
++                                      pinmux = <GPIOMUX(56, GPOUT_SYS_SDIO1_CLK,
++                                                            GPOEN_ENABLE,
++                                                            GPI_NONE)>,
++                                               <GPIOMUX(50, GPOUT_SYS_SDIO1_CMD,
++                                                            GPOEN_SYS_SDIO1_CMD,
++                                                            GPI_SYS_SDIO1_CMD)>,
++                                               <GPIOMUX(49, GPOUT_SYS_SDIO1_DATA0,
++                                                            GPOEN_SYS_SDIO1_DATA0,
++                                                            GPI_SYS_SDIO1_DATA0)>,
++                                               <GPIOMUX(45, GPOUT_SYS_SDIO1_DATA1,
++                                                            GPOEN_SYS_SDIO1_DATA1,
++                                                            GPI_SYS_SDIO1_DATA1)>,
++                                               <GPIOMUX(62, GPOUT_SYS_SDIO1_DATA2,
++                                                            GPOEN_SYS_SDIO1_DATA2,
++                                                            GPI_SYS_SDIO1_DATA2)>,
++                                               <GPIOMUX(40, GPOUT_SYS_SDIO1_DATA3,
++                                                            GPOEN_SYS_SDIO1_DATA3,
++                                                            GPI_SYS_SDIO1_DATA3)>;
++                                      bias-pull-up;
++                                      input-enable;
++                              };
++                      };
++              };
++      };
++
++      //uart3
++      fragment@1 {
++              target-path = "/soc/serial@12000000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //i2c0
++      fragment@2 {
++              target-path = "/soc/i2c@10030000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //mmc1
++      fragment@3 {
++              target-path = "/soc/mmc@16020000";
++              __overlay__ {
++                      max-frequency = <100000000>;
++                      card-detect-delay = <300>;
++                      bus-width = <4>;
++                      no-sdio;
++                      no-mmc;
++                      broken-cd;
++                      sd-uhs-sdr12;
++                      sd-uhs-sdr25;
++                      sd-uhs-sdr50;
++                      sd-uhs-sdr104;
++                      sd-uhs-ddr50;
++                      cap-sd-highspeed;
++                      post-power-on-delay-ms = <200>;
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&dt_sdcard1_pins>;
++                      status = "okay";
++              };
++      };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
+@@ -0,0 +1,72 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //spi0
++      fragment@0 {
++              target-path = "/soc/spi@10060000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //spi1
++      fragment@1 {
++              target-path = "/soc/spi@10070000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //spi2
++      fragment@2 {
++              target-path = "/soc/spi@10080000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //spi3
++      fragment@3 {
++              target-path = "/soc/spi@12070000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //spi4
++      fragment@4 {
++              target-path = "/soc/spi@12080000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //spi5
++      fragment@5 {
++              target-path = "/soc/spi@12090000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //spi6
++      fragment@6 {
++              target-path = "/soc/spi@120a0000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //uart2
++      fragment@7 {
++              target-path = "/soc/serial@10020000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso
+@@ -0,0 +1,130 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //sysgpio
++      fragment@0 {
++              target-path = "/soc/pinctrl@13040000";
++              __overlay__ {
++                      dt_emmc0_pins: dt-emmc0-0 {
++                              emmc-pins {
++                                      pinmux = <GPIOMUX(22, GPOUT_SYS_SDIO0_RST,
++                                                            GPOEN_ENABLE,
++                                                            GPI_NONE)>,
++                                               <PINMUX(64, 0)>,
++                                               <PINMUX(65, 0)>,
++                                               <PINMUX(66, 0)>,
++                                               <PINMUX(67, 0)>,
++                                               <PINMUX(68, 0)>,
++                                               <PINMUX(69, 0)>,
++                                               <PINMUX(70, 0)>,
++                                               <PINMUX(71, 0)>,
++                                               <PINMUX(72, 0)>,
++                                               <PINMUX(73, 0)>;
++                                      bias-pull-up;
++                                      drive-strength = <12>;
++                                      input-enable;
++                                      slew-rate = <1>;
++                              };
++                      };
++
++                      dt_emmc1_pins: dt-emmc1-0 {
++                              emmc-pins {
++                                      pinmux = <GPIOMUX(51, GPOUT_SYS_SDIO1_RST,
++                                                            GPOEN_ENABLE,
++                                                            GPI_NONE)>,
++                                               <GPIOMUX(38, GPOUT_SYS_SDIO1_CLK,
++                                                            GPOEN_ENABLE,
++                                                            GPI_NONE)>,
++                                               <GPIOMUX(36, GPOUT_SYS_SDIO1_CMD,
++                                                            GPOEN_SYS_SDIO1_CMD,
++                                                            GPI_SYS_SDIO1_CMD)>,
++                                               <GPIOMUX(43, GPOUT_SYS_SDIO1_DATA0,
++                                                            GPOEN_SYS_SDIO1_DATA0,
++                                                            GPI_SYS_SDIO1_DATA0)>,
++                                               <GPIOMUX(48, GPOUT_SYS_SDIO1_DATA1,
++                                                            GPOEN_SYS_SDIO1_DATA1,
++                                                            GPI_SYS_SDIO1_DATA1)>,
++                                               <GPIOMUX(53, GPOUT_SYS_SDIO1_DATA2,
++                                                            GPOEN_SYS_SDIO1_DATA2,
++                                                            GPI_SYS_SDIO1_DATA2)>,
++                                               <GPIOMUX(63, GPOUT_SYS_SDIO1_DATA3,
++                                                            GPOEN_SYS_SDIO1_DATA3,
++                                                            GPI_SYS_SDIO1_DATA3)>,
++                                               <GPIOMUX(52, GPOUT_SYS_SDIO1_DATA4,
++                                                            GPOEN_SYS_SDIO1_DATA4,
++                                                            GPI_SYS_SDIO1_DATA4)>,
++                                               <GPIOMUX(39, GPOUT_SYS_SDIO1_DATA5,
++                                                            GPOEN_SYS_SDIO1_DATA5,
++                                                            GPI_SYS_SDIO1_DATA5)>,
++                                               <GPIOMUX(46, GPOUT_SYS_SDIO1_DATA6,
++                                                            GPOEN_SYS_SDIO1_DATA6,
++                                                            GPI_SYS_SDIO1_DATA6)>,
++                                               <GPIOMUX(47, GPOUT_SYS_SDIO1_DATA7,
++                                                            GPOEN_SYS_SDIO1_DATA7,
++                                                            GPI_SYS_SDIO1_DATA7)>;
++                                      bias-pull-up;
++                                      input-enable;
++                              };
++                      };
++              };
++      };
++
++      //aongpio
++      fragment@1 {
++              target-path = "/soc/pinctrl@17020000";
++              __overlay__ {
++                      dt_pwm_ch6to7_pins: dt-pwm-ch6to7-0 {
++                              pwm-pins {
++                                      pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM6, /* PAD_RGPIO0 */
++                                                           GPOEN_AON_PTC0_OE_N_6,
++                                                           GPI_NONE)>,
++                                               <GPIOMUX(2, GPOUT_AON_PTC0_PWM7, /* PAD_RGPIO1 */
++                                                           GPOEN_AON_PTC0_OE_N_7,
++                                                           GPI_NONE)>;
++                                      drive-strength = <12>;
++                              };
++                      };
++              };
++      };
++
++      //uart4
++      fragment@2 {
++              target-path = "/soc/serial@12010000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //mmc1
++      fragment@3 {
++              target-path = "/soc/mmc@16020000";
++              __overlay__ {
++                      clock-frequency = <102400000>;
++                      max-frequency = <100000000>;
++                      card-detect-delay = <300>;
++                      bus-width = <8>;
++                      cap-mmc-hw-reset;
++                      non-removable;
++                      cap-mmc-highspeed;
++                      post-power-on-delay-ms = <200>;
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&dt_emmc1_pins>;
++                      status = "okay";
++              };
++      };
++
++      //ptc
++      fragment@4 {
++              target-path = "/soc/pwm@120d0000";
++              __overlay__ {
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&dt_pwm_ch6to7_pins>;
++                      status = "okay";
++              };
++      };
++};
++
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso
+@@ -0,0 +1,92 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //sysgpio
++      fragment@0 {
++              target-path = "/soc/pinctrl@13040000";
++              __overlay__ {
++                      dt_pwm_ch0to3_pins: dt-pwm-ch0to3-0 {
++                              pwm-pins {
++                                      pinmux = <GPIOMUX(45, GPOUT_SYS_PWM_CHANNEL0,
++                                                            GPOEN_SYS_PWM0_CHANNEL0,
++                                                            GPI_NONE)>,
++                                               <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL1,
++                                                            GPOEN_SYS_PWM0_CHANNEL1,
++                                                            GPI_NONE)>,
++                                               <GPIOMUX(47, GPOUT_SYS_PWM_CHANNEL2,
++                                                            GPOEN_SYS_PWM0_CHANNEL2,
++                                                            GPI_NONE)>,
++                                               <GPIOMUX(48, GPOUT_SYS_PWM_CHANNEL3,
++                                                            GPOEN_SYS_PWM0_CHANNEL3,
++                                                            GPI_NONE)>;
++                                      drive-strength = <12>;
++                              };
++                      };
++              };
++      };
++
++      //aongpio
++      fragment@1 {
++              target-path = "/soc/pinctrl@17020000";
++              __overlay__ {
++                      dt_pwm_ch4to5_pins: dt-pwm-ch4to5-0 {
++                              pwm-pins {
++                                      pinmux = <GPIOMUX(1, GPOUT_AON_PTC0_PWM4, /* PAD_RGPIO0 */
++                                                           GPOEN_AON_PTC0_OE_N_4,
++                                                           GPI_NONE)>,
++                                               <GPIOMUX(2, GPOUT_AON_PTC0_PWM5, /* PAD_RGPIO1 */
++                                                           GPOEN_AON_PTC0_OE_N_5,
++                                                           GPI_NONE)>;
++                                      drive-strength = <12>;
++                              };
++                      };
++              };
++      };
++
++      //uart5
++      fragment@2 {
++              target-path = "/soc/serial@12020000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //ptc
++      fragment@3 {
++              target-path = "/soc/pwm@120d0000";
++              __overlay__ {
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&dt_pwm_ch0to3_pins &dt_pwm_ch4to5_pins>;
++                      status = "okay";
++              };
++      };
++
++      //i2c0
++      fragment@4 {
++              target-path = "/soc/i2c@10030000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //i2c1
++      fragment@5 {
++              target-path = "/soc/i2c@10040000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //i2c3
++      fragment@6 {
++              target-path = "/soc/i2c@12030000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++};
++
diff --git a/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch b/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch
new file mode 100644 (file)
index 0000000..ddf7245
--- /dev/null
@@ -0,0 +1,385 @@
+From 5c888fa081caf5d9473e733931d1c7b3d4b61e61 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 28 Jul 2023 18:42:55 +0800
+Subject: [PATCH 059/116] riscv: configs: Add starfive_jh7110_defconfig
+
+Add starfive_jh7110_defconfig for JH7110 EVB.
+The code is ported from tag JH7110_SDK_6.1_v5.11.3
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/configs/starfive_jh7110_defconfig | 368 +++++++++++++++++++
+ 1 file changed, 368 insertions(+)
+ create mode 100644 arch/riscv/configs/starfive_jh7110_defconfig
+
+--- /dev/null
++++ b/arch/riscv/configs/starfive_jh7110_defconfig
+@@ -0,0 +1,368 @@
++CONFIG_COMPILE_TEST=y
++# CONFIG_WERROR is not set
++CONFIG_DEFAULT_HOSTNAME="StarFive"
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_USELIB=y
++CONFIG_NO_HZ_IDLE=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_BPF_SYSCALL=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_CGROUPS=y
++CONFIG_MEMCG=y
++CONFIG_CGROUP_SCHED=y
++CONFIG_CFS_BANDWIDTH=y
++CONFIG_RT_GROUP_SCHED=y
++CONFIG_CGROUP_PIDS=y
++CONFIG_CGROUP_FREEZER=y
++CONFIG_CGROUP_HUGETLB=y
++CONFIG_CPUSETS=y
++CONFIG_CGROUP_DEVICE=y
++CONFIG_CGROUP_CPUACCT=y
++CONFIG_CGROUP_PERF=y
++CONFIG_CGROUP_BPF=y
++CONFIG_NAMESPACES=y
++CONFIG_USER_NS=y
++CONFIG_CHECKPOINT_RESTORE=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_EXPERT=y
++# CONFIG_SYSFS_SYSCALL is not set
++CONFIG_PROFILING=y
++CONFIG_SOC_MICROCHIP_POLARFIRE=y
++CONFIG_SOC_STARFIVE=y
++CONFIG_SOC_VIRT=y
++CONFIG_ERRATA_SIFIVE=y
++CONFIG_NONPORTABLE=y
++CONFIG_SMP=y
++CONFIG_RISCV_SBI_V01=y
++# CONFIG_RISCV_BOOT_SPINWAIT is not set
++CONFIG_HIBERNATION=y
++CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation"
++CONFIG_PM_DEBUG=y
++CONFIG_PM_ADVANCED_DEBUG=y
++CONFIG_PM_TEST_SUSPEND=y
++CONFIG_ENERGY_MODEL=y
++CONFIG_CPU_IDLE=y
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_STAT=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
++CONFIG_CPUFREQ_DT=y
++CONFIG_VIRTUALIZATION=y
++CONFIG_KVM=m
++CONFIG_JUMP_LABEL=y
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++CONFIG_BINFMT_MISC=y
++CONFIG_CMA=y
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_XFRM_USER=m
++CONFIG_IP_MULTICAST=y
++CONFIG_IP_ADVANCED_ROUTER=y
++CONFIG_IP_MULTIPLE_TABLES=y
++CONFIG_IP_PNP=y
++CONFIG_IP_PNP_DHCP=y
++CONFIG_IP_PNP_BOOTP=y
++CONFIG_IP_PNP_RARP=y
++CONFIG_INET_ESP=m
++CONFIG_NETFILTER=y
++CONFIG_BRIDGE_NETFILTER=m
++CONFIG_NF_CONNTRACK=m
++CONFIG_NF_CONNTRACK_FTP=m
++CONFIG_NF_CONNTRACK_TFTP=m
++CONFIG_NETFILTER_XT_MARK=m
++CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
++CONFIG_NETFILTER_XT_MATCH_IPVS=m
++CONFIG_IP_VS=m
++CONFIG_IP_VS_PROTO_TCP=y
++CONFIG_IP_VS_PROTO_UDP=y
++CONFIG_IP_VS_RR=m
++CONFIG_IP_VS_NFCT=y
++CONFIG_NF_LOG_ARP=m
++CONFIG_NF_LOG_IPV4=m
++CONFIG_IP_NF_IPTABLES=m
++CONFIG_IP_NF_FILTER=m
++CONFIG_IP_NF_TARGET_REJECT=m
++CONFIG_IP_NF_NAT=m
++CONFIG_IP_NF_TARGET_MASQUERADE=m
++CONFIG_IP_NF_TARGET_REDIRECT=m
++CONFIG_IP_NF_MANGLE=m
++CONFIG_NF_LOG_IPV6=m
++CONFIG_IP6_NF_IPTABLES=m
++CONFIG_IP6_NF_MATCH_IPV6HEADER=m
++CONFIG_IP6_NF_FILTER=m
++CONFIG_IP6_NF_TARGET_REJECT=m
++CONFIG_IP6_NF_MANGLE=m
++CONFIG_BRIDGE=m
++CONFIG_BRIDGE_VLAN_FILTERING=y
++CONFIG_VLAN_8021Q=m
++CONFIG_NET_SCHED=y
++CONFIG_NET_CLS_CGROUP=m
++CONFIG_NETLINK_DIAG=y
++CONFIG_CGROUP_NET_PRIO=y
++CONFIG_CAN=y
++CONFIG_BT=y
++CONFIG_BT_RFCOMM=y
++CONFIG_BT_RFCOMM_TTY=y
++CONFIG_BT_BNEP=y
++CONFIG_BT_BNEP_MC_FILTER=y
++CONFIG_BT_BNEP_PROTO_FILTER=y
++CONFIG_BT_HCIUART=y
++CONFIG_BT_HCIUART_H4=y
++CONFIG_CFG80211=y
++CONFIG_MAC80211=y
++CONFIG_RFKILL=y
++CONFIG_NET_9P=y
++CONFIG_NET_9P_VIRTIO=y
++CONFIG_PCI=y
++CONFIG_PCIEPORTBUS=y
++# CONFIG_PCIEASPM is not set
++CONFIG_PCI_HOST_GENERIC=y
++CONFIG_PCIE_FU740=y
++CONFIG_PCIE_STARFIVE_HOST=y
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_MTD=y
++CONFIG_MTD_BLOCK=y
++CONFIG_MTD_SPI_NOR=y
++CONFIG_OF_CONFIGFS=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_VIRTIO_BLK=y
++CONFIG_BLK_DEV_NVME=y
++CONFIG_BLK_DEV_SD=y
++CONFIG_BLK_DEV_SR=y
++CONFIG_SCSI_VIRTIO=y
++CONFIG_ATA=y
++CONFIG_SATA_AHCI=y
++CONFIG_SATA_AHCI_PLATFORM=y
++CONFIG_MD=y
++CONFIG_BLK_DEV_DM=m
++CONFIG_DM_THIN_PROVISIONING=m
++CONFIG_NETDEVICES=y
++CONFIG_DUMMY=m
++CONFIG_MACVLAN=m
++CONFIG_IPVLAN=m
++CONFIG_VXLAN=m
++CONFIG_VETH=m
++CONFIG_VIRTIO_NET=y
++CONFIG_MACB=y
++CONFIG_E1000E=y
++CONFIG_R8169=y
++CONFIG_STMMAC_ETH=y
++CONFIG_DWMAC_DWC_QOS_ETH=y
++# CONFIG_DWMAC_GENERIC is not set
++CONFIG_DWMAC_STARFIVE=y
++CONFIG_MARVELL_PHY=y
++CONFIG_MICREL_PHY=y
++CONFIG_MICROCHIP_PHY=y
++CONFIG_MICROSEMI_PHY=y
++CONFIG_MOTORCOMM_PHY=y
++CONFIG_IPMS_CAN=y
++CONFIG_IWLWIFI=y
++CONFIG_IWLDVM=y
++CONFIG_IWLMVM=y
++CONFIG_INPUT_MOUSEDEV=y
++CONFIG_INPUT_EVDEV=y
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_TOUCHSCREEN_TINKER_FT5406=y
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_SERIAL_8250_NR_UARTS=6
++CONFIG_SERIAL_8250_RUNTIME_UARTS=6
++CONFIG_SERIAL_8250_EXTENDED=y
++CONFIG_SERIAL_8250_MANY_PORTS=y
++CONFIG_SERIAL_8250_DW=y
++CONFIG_SERIAL_OF_PLATFORM=y
++CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
++CONFIG_TTY_PRINTK=y
++CONFIG_VIRTIO_CONSOLE=y
++CONFIG_HW_RANDOM=y
++CONFIG_HW_RANDOM_VIRTIO=y
++CONFIG_HW_RANDOM_JH7110=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_DESIGNWARE_PLATFORM=y
++CONFIG_SPI=y
++CONFIG_SPI_CADENCE_QUADSPI=y
++CONFIG_SPI_PL022=y
++CONFIG_SPI_SIFIVE=y
++CONFIG_SPI_SPIDEV=y
++# CONFIG_PTP_1588_CLOCK is not set
++CONFIG_GPIO_SYSFS=y
++CONFIG_GPIO_SIFIVE=y
++CONFIG_SENSORS_SFCTEMP=y
++CONFIG_THERMAL=y
++CONFIG_THERMAL_WRITABLE_TRIPS=y
++CONFIG_CPU_THERMAL=y
++CONFIG_THERMAL_EMULATION=y
++CONFIG_WATCHDOG=y
++CONFIG_WATCHDOG_SYSFS=y
++CONFIG_MFD_AXP20X_I2C=y
++CONFIG_REGULATOR=y
++CONFIG_REGULATOR_AXP20X=y
++CONFIG_REGULATOR_STARFIVE_JH7110=y
++# CONFIG_MEDIA_CEC_SUPPORT is not set
++CONFIG_MEDIA_SUPPORT=y
++CONFIG_MEDIA_USB_SUPPORT=y
++CONFIG_USB_VIDEO_CLASS=y
++CONFIG_V4L_PLATFORM_DRIVERS=y
++CONFIG_V4L_MEM2MEM_DRIVERS=y
++CONFIG_VIDEO_WAVE_VPU=m
++CONFIG_VIN_SENSOR_SC2235=y
++CONFIG_VIN_SENSOR_OV4689=y
++CONFIG_VIN_SENSOR_IMX219=y
++CONFIG_VIDEO_STF_VIN=y
++CONFIG_VIDEO_IMX708=y
++CONFIG_DRM_I2C_NXP_TDA998X=y
++CONFIG_DRM_I2C_NXP_TDA9950=y
++CONFIG_DRM_RADEON=m
++CONFIG_DRM_VIRTIO_GPU=m
++CONFIG_DRM_VERISILICON=y
++CONFIG_STARFIVE_INNO_HDMI=y
++CONFIG_STARFIVE_DSI=y
++CONFIG_DRM_IMG_ROGUE=y
++CONFIG_DRM_LEGACY=y
++CONFIG_FB=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_USB_AUDIO=y
++CONFIG_SND_SOC=y
++CONFIG_SND_DESIGNWARE_I2S=y
++# CONFIG_SND_SOC_INTEL_SST_TOPLEVEL is not set
++CONFIG_SND_SOC_STARFIVE=y
++CONFIG_SND_SOC_JH7110_PDM=y
++CONFIG_SND_SOC_JH7110_PWMDAC=y
++CONFIG_SND_SOC_JH7110_SPDIF=y
++CONFIG_SND_SOC_JH7110_TDM=y
++CONFIG_SND_SOC_AC108=y
++CONFIG_SND_SOC_WM8960=y
++CONFIG_SND_SIMPLE_CARD=y
++CONFIG_USB=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_HCD_PLATFORM=y
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_OHCI_HCD_PLATFORM=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_UAS=y
++CONFIG_USB_CDNS_SUPPORT=y
++CONFIG_USB_CDNS3=y
++CONFIG_USB_CDNS3_GADGET=y
++CONFIG_USB_CDNS3_HOST=y
++CONFIG_USB_CDNS3_STARFIVE=y
++CONFIG_USB_GADGET=y
++CONFIG_USB_CONFIGFS=y
++CONFIG_USB_CONFIGFS_MASS_STORAGE=y
++CONFIG_USB_CONFIGFS_F_FS=y
++CONFIG_MMC=y
++CONFIG_MMC_SDHCI=y
++CONFIG_MMC_SDHCI_PLTFM=y
++CONFIG_MMC_SDHCI_CADENCE=y
++CONFIG_MMC_SPI=y
++CONFIG_MMC_DW=y
++CONFIG_MMC_DW_STARFIVE=y
++CONFIG_RTC_CLASS=y
++# CONFIG_RTC_DRV_SPEAR is not set
++CONFIG_RTC_DRV_STARFIVE=y
++CONFIG_DMADEVICES=y
++CONFIG_AMBA_PL08X=y
++CONFIG_DW_AXI_DMAC=y
++# CONFIG_SH_DMAE_BASE is not set
++# CONFIG_TI_EDMA is not set
++# CONFIG_DMA_OMAP is not set
++CONFIG_DMATEST=y
++CONFIG_VIRTIO_PCI=y
++CONFIG_VIRTIO_BALLOON=y
++CONFIG_VIRTIO_INPUT=y
++CONFIG_VIRTIO_MMIO=y
++CONFIG_STAGING=y
++CONFIG_STAGING_MEDIA=y
++CONFIG_CLK_STARFIVE_JH7110_AON=y
++CONFIG_CLK_STARFIVE_JH7110_STG=y
++CONFIG_CLK_STARFIVE_JH7110_ISP=y
++CONFIG_CLK_STARFIVE_JH7110_VOUT=y
++CONFIG_MAILBOX=y
++CONFIG_STARFIVE_MBOX=m
++CONFIG_STARFIVE_MBOX_TEST=m
++CONFIG_RPMSG_CHAR=y
++CONFIG_RPMSG_CTRL=y
++CONFIG_RPMSG_VIRTIO=y
++CONFIG_SIFIVE_CCACHE=y
++CONFIG_PWM=y
++CONFIG_PWM_OCORES=y
++CONFIG_PHY_STARFIVE_JH7110_PCIE=y
++CONFIG_PHY_STARFIVE_JH7110_USB=y
++CONFIG_PHY_M31_DPHY_RX0=y
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_POSIX_ACL=y
++CONFIG_EXT4_FS_SECURITY=y
++CONFIG_BTRFS_FS=m
++CONFIG_BTRFS_FS_POSIX_ACL=y
++CONFIG_AUTOFS_FS=y
++CONFIG_FUSE_FS=y
++CONFIG_OVERLAY_FS=y
++CONFIG_OVERLAY_FS_INDEX=y
++CONFIG_OVERLAY_FS_XINO_AUTO=y
++CONFIG_OVERLAY_FS_METACOPY=y
++CONFIG_ISO9660_FS=y
++CONFIG_JOLIET=y
++CONFIG_ZISOFS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_EXFAT_FS=y
++CONFIG_TMPFS=y
++CONFIG_TMPFS_POSIX_ACL=y
++CONFIG_HUGETLBFS=y
++CONFIG_JFFS2_FS=y
++CONFIG_NFS_FS=y
++CONFIG_NFS_V4=y
++CONFIG_NFS_V4_1=y
++CONFIG_NFS_V4_2=y
++CONFIG_ROOT_NFS=y
++CONFIG_9P_FS=y
++CONFIG_NLS_CODEPAGE_437=y
++CONFIG_NLS_ISO8859_1=m
++CONFIG_SECURITY=y
++CONFIG_SECURITY_SELINUX=y
++CONFIG_SECURITY_APPARMOR=y
++CONFIG_DEFAULT_SECURITY_DAC=y
++CONFIG_LSM=""
++CONFIG_INIT_STACK_NONE=y
++CONFIG_CRYPTO_USER=y
++CONFIG_CRYPTO_ZSTD=y
++CONFIG_CRYPTO_USER_API_HASH=y
++CONFIG_CRYPTO_USER_API_SKCIPHER=y
++CONFIG_CRYPTO_USER_API_AEAD=y
++CONFIG_CRYPTO_STATS=y
++CONFIG_CRYPTO_DEV_VIRTIO=y
++CONFIG_CRYPTO_DEV_JH7110=y
++CONFIG_DMA_CMA=y
++CONFIG_PRINTK_TIME=y
++CONFIG_DEBUG_FS=y
++CONFIG_DEBUG_PAGEALLOC=y
++CONFIG_SCHED_STACK_END_CHECK=y
++CONFIG_DEBUG_VM=y
++CONFIG_DEBUG_VM_PGFLAGS=y
++CONFIG_DEBUG_MEMORY_INIT=y
++CONFIG_DEBUG_PER_CPU_MAPS=y
++CONFIG_SOFTLOCKUP_DETECTOR=y
++CONFIG_WQ_WATCHDOG=y
++CONFIG_DEBUG_TIMEKEEPING=y
++CONFIG_DEBUG_RT_MUTEXES=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_MUTEXES=y
++CONFIG_DEBUG_RWSEMS=y
++CONFIG_DEBUG_LIST=y
++CONFIG_DEBUG_PLIST=y
++CONFIG_DEBUG_SG=y
++CONFIG_RCU_CPU_STALL_TIMEOUT=60
++# CONFIG_RCU_TRACE is not set
++CONFIG_RCU_EQS_DEBUG=y
++# CONFIG_FTRACE is not set
++# CONFIG_RUNTIME_TESTING_MENU is not set
++CONFIG_MEMTEST=y
diff --git a/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch b/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch
new file mode 100644 (file)
index 0000000..68deb70
--- /dev/null
@@ -0,0 +1,318 @@
+From 95c702022f5e4cb786719fcf90170334b1e562cc Mon Sep 17 00:00:00 2001
+From: Jianlong Huang <jianlong.huang@starfivetech.com>
+Date: Thu, 16 Jun 2022 17:13:57 +0800
+Subject: [PATCH 060/116] of: configfs: Add configfs function
+
+Signed-off-by: Jianlong Huang <jianlong.huang@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/of/Kconfig    |   7 ++
+ drivers/of/Makefile   |   1 +
+ drivers/of/configfs.c | 277 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 285 insertions(+)
+ create mode 100644 drivers/of/configfs.c
+
+--- a/drivers/of/Kconfig
++++ b/drivers/of/Kconfig
+@@ -102,4 +102,11 @@ config OF_OVERLAY
+ config OF_NUMA
+       bool
++config OF_CONFIGFS
++        bool "Device Tree Overlay ConfigFS interface"
++        select CONFIGFS_FS
++        select OF_OVERLAY
++        help
++          Enable a simple user-space driven DT overlay interface.
++
+ endif # OF
+--- a/drivers/of/Makefile
++++ b/drivers/of/Makefile
+@@ -11,6 +11,7 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o
+ obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+ obj-$(CONFIG_OF_RESOLVE)  += resolver.o
+ obj-$(CONFIG_OF_OVERLAY) += overlay.o
++obj-$(CONFIG_OF_CONFIGFS) += configfs.o
+ obj-$(CONFIG_OF_NUMA) += of_numa.o
+ ifdef CONFIG_KEXEC_FILE
+--- /dev/null
++++ b/drivers/of/configfs.c
+@@ -0,0 +1,277 @@
++/*
++ * Configfs entries for device-tree
++ *
++ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++#include <linux/ctype.h>
++#include <linux/cpu.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_fdt.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/proc_fs.h>
++#include <linux/configfs.h>
++#include <linux/types.h>
++#include <linux/stat.h>
++#include <linux/limits.h>
++#include <linux/file.h>
++#include <linux/vmalloc.h>
++#include <linux/firmware.h>
++#include <linux/sizes.h>
++
++#include "of_private.h"
++
++struct cfs_overlay_item {
++      struct config_item      item;
++
++      char                    path[PATH_MAX];
++
++      const struct firmware   *fw;
++      struct device_node      *overlay;
++      int                     ov_id;
++
++      void                    *dtbo;
++      int                     dtbo_size;
++};
++
++static inline struct cfs_overlay_item *to_cfs_overlay_item(
++              struct config_item *item)
++{
++      return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
++}
++
++static ssize_t cfs_overlay_item_path_show(struct config_item *item,
++              char *page)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++      return sprintf(page, "%s\n", overlay->path);
++}
++
++static ssize_t cfs_overlay_item_path_store(struct config_item *item,
++              const char *page, size_t count)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++      const char *p = page;
++      char *s;
++      int err;
++
++      /* if it's set do not allow changes */
++      if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
++              return -EPERM;
++
++      /* copy to path buffer (and make sure it's always zero terminated */
++      count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
++      overlay->path[sizeof(overlay->path) - 1] = '\0';
++
++      /* strip trailing newlines */
++      s = overlay->path + strlen(overlay->path);
++      while (s > overlay->path && *--s == '\n')
++              *s = '\0';
++
++      pr_debug("%s: path is '%s'\n", __func__, overlay->path);
++
++      err = request_firmware(&overlay->fw, overlay->path, NULL);
++      if (err != 0)
++              return err;
++
++      err = of_overlay_fdt_apply((void *)overlay->fw->data,
++                                 (u32)overlay->fw->size, &overlay->ov_id, NULL);
++      if (err != 0)
++              goto out_err;
++
++      return count;
++
++out_err:
++
++      release_firmware(overlay->fw);
++      overlay->fw = NULL;
++
++      overlay->path[0] = '\0';
++      return err;
++}
++
++static ssize_t cfs_overlay_item_status_show(struct config_item *item,
++              char *page)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++      return sprintf(page, "%s\n",
++                      overlay->ov_id > 0 ? "applied" : "unapplied");
++}
++
++CONFIGFS_ATTR(cfs_overlay_item_, path);
++CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
++
++static struct configfs_attribute *cfs_overlay_attrs[] = {
++      &cfs_overlay_item_attr_path,
++      &cfs_overlay_item_attr_status,
++      NULL,
++};
++
++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
++              void *buf, size_t max_count)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++      pr_debug("%s: buf=%p max_count=%zu\n", __func__,
++                      buf, max_count);
++
++      if (overlay->dtbo == NULL)
++              return 0;
++
++      /* copy if buffer provided */
++      if (buf != NULL) {
++              /* the buffer must be large enough */
++              if (overlay->dtbo_size > max_count)
++                      return -ENOSPC;
++
++              memcpy(buf, overlay->dtbo, overlay->dtbo_size);
++      }
++
++      return overlay->dtbo_size;
++}
++
++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
++              const void *buf, size_t count)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++      int err;
++
++      /* if it's set do not allow changes */
++      if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
++              return -EPERM;
++
++      /* copy the contents */
++      overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
++      if (overlay->dtbo == NULL)
++              return -ENOMEM;
++
++      overlay->dtbo_size = count;
++
++      err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size,
++                                 &overlay->ov_id, NULL);
++      if (err != 0)
++              goto out_err;
++
++      return count;
++
++out_err:
++      kfree(overlay->dtbo);
++      overlay->dtbo = NULL;
++      overlay->dtbo_size = 0;
++      overlay->ov_id = 0;
++
++      return err;
++}
++
++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
++
++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
++      &cfs_overlay_item_attr_dtbo,
++      NULL,
++};
++
++static void cfs_overlay_release(struct config_item *item)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++      if (overlay->ov_id > 0)
++              of_overlay_remove(&overlay->ov_id);
++      if (overlay->fw)
++              release_firmware(overlay->fw);
++      /* kfree with NULL is safe */
++      kfree(overlay->dtbo);
++      kfree(overlay);
++}
++
++static struct configfs_item_operations cfs_overlay_item_ops = {
++      .release        = cfs_overlay_release,
++};
++
++static struct config_item_type cfs_overlay_type = {
++      .ct_item_ops    = &cfs_overlay_item_ops,
++      .ct_attrs       = cfs_overlay_attrs,
++      .ct_bin_attrs   = cfs_overlay_bin_attrs,
++      .ct_owner       = THIS_MODULE,
++};
++
++static struct config_item *cfs_overlay_group_make_item(
++              struct config_group *group, const char *name)
++{
++      struct cfs_overlay_item *overlay;
++
++      overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
++      if (!overlay)
++              return ERR_PTR(-ENOMEM);
++
++      config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
++      return &overlay->item;
++}
++
++static void cfs_overlay_group_drop_item(struct config_group *group,
++              struct config_item *item)
++{
++      struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
++
++      config_item_put(&overlay->item);
++}
++
++static struct configfs_group_operations overlays_ops = {
++      .make_item      = cfs_overlay_group_make_item,
++      .drop_item      = cfs_overlay_group_drop_item,
++};
++
++static struct config_item_type overlays_type = {
++      .ct_group_ops   = &overlays_ops,
++      .ct_owner       = THIS_MODULE,
++};
++
++static struct configfs_group_operations of_cfs_ops = {
++      /* empty - we don't allow anything to be created */
++};
++
++static struct config_item_type of_cfs_type = {
++      .ct_group_ops   = &of_cfs_ops,
++      .ct_owner       = THIS_MODULE,
++};
++
++struct config_group of_cfs_overlay_group;
++
++static struct configfs_subsystem of_cfs_subsys = {
++      .su_group = {
++              .cg_item = {
++                      .ci_namebuf = "device-tree",
++                      .ci_type = &of_cfs_type,
++              },
++      },
++      .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
++};
++
++static int __init of_cfs_init(void)
++{
++      int ret;
++
++      pr_info("%s\n", __func__);
++
++      config_group_init(&of_cfs_subsys.su_group);
++      config_group_init_type_name(&of_cfs_overlay_group, "overlays",
++                      &overlays_type);
++      configfs_add_default_group(&of_cfs_overlay_group,
++                      &of_cfs_subsys.su_group);
++
++      ret = configfs_register_subsystem(&of_cfs_subsys);
++      if (ret != 0) {
++              pr_err("%s: failed to register subsys\n", __func__);
++              goto out;
++      }
++      pr_info("%s: OK\n", __func__);
++out:
++      return ret;
++}
++late_initcall(of_cfs_init);
diff --git a/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch b/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch
new file mode 100644 (file)
index 0000000..ade0f46
--- /dev/null
@@ -0,0 +1,344 @@
+From 7891826f8c2de9ee0f6459cf969f7b082e29b154 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Thu, 1 Jun 2023 23:10:09 -0700
+Subject: [PATCH 061/116] usr: Add gen_initramfs_list.sh
+
+Add gen_initramfs_list.sh
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ usr/gen_initramfs_list.sh | 328 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 328 insertions(+)
+ create mode 100644 usr/gen_initramfs_list.sh
+
+--- /dev/null
++++ b/usr/gen_initramfs_list.sh
+@@ -0,0 +1,328 @@
++#!/bin/sh
++# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
++# Copyright (C) 2006 Sam Ravnborg <sam@ravnborg.org>
++#
++# Released under the terms of the GNU GPL
++#
++# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
++# the cpio archive, and then compresses it.
++# The script may also be used to generate the inputfile used for gen_init_cpio
++# This script assumes that gen_init_cpio is located in usr/ directory
++
++# error out on errors
++set -e
++
++usage() {
++cat << EOF
++Usage:
++$0 [-o <file>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ...
++      -o <file>      Create compressed initramfs file named <file> using
++                     gen_init_cpio and compressor depending on the extension
++      -u <uid>       User ID to map to user ID 0 (root).
++                     <uid> is only meaningful if <cpio_source> is a
++                     directory.  "squash" forces all files to uid 0.
++      -g <gid>       Group ID to map to group ID 0 (root).
++                     <gid> is only meaningful if <cpio_source> is a
++                     directory.  "squash" forces all files to gid 0.
++      <cpio_source>  File list or directory for cpio archive.
++                     If <cpio_source> is a .cpio file it will be used
++                     as direct input to initramfs.
++      -d             Output the default cpio list.
++
++All options except -o and -l may be repeated and are interpreted
++sequentially and immediately.  -u and -g states are preserved across
++<cpio_source> options so an explicit "-u 0 -g 0" is required
++to reset the root/group mapping.
++EOF
++}
++
++# awk style field access
++# $1 - field number; rest is argument string
++field() {
++      shift $1 ; echo $1
++}
++
++list_default_initramfs() {
++      # echo usr/kinit/kinit
++      :
++}
++
++default_initramfs() {
++      cat <<-EOF >> ${output}
++              # This is a very simple, default initramfs
++
++              dir /dev 0755 0 0
++              nod /dev/console 0600 0 0 c 5 1
++              dir /root 0700 0 0
++              # file /kinit usr/kinit/kinit 0755 0 0
++              # slink /init kinit 0755 0 0
++      EOF
++}
++
++filetype() {
++      local argv1="$1"
++
++      # symlink test must come before file test
++      if [ -L "${argv1}" ]; then
++              echo "slink"
++      elif [ -f "${argv1}" ]; then
++              echo "file"
++      elif [ -d "${argv1}" ]; then
++              echo "dir"
++      elif [ -b "${argv1}" -o -c "${argv1}" ]; then
++              echo "nod"
++      elif [ -p "${argv1}" ]; then
++              echo "pipe"
++      elif [ -S "${argv1}" ]; then
++              echo "sock"
++      else
++              echo "invalid"
++      fi
++      return 0
++}
++
++list_print_mtime() {
++      :
++}
++
++print_mtime() {
++      local my_mtime="0"
++
++      if [ -e "$1" ]; then
++              my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
++      fi
++
++      echo "# Last modified: ${my_mtime}" >> ${output}
++      echo "" >> ${output}
++}
++
++list_parse() {
++      if [ -L "$1" ]; then
++              return
++      fi
++      echo "$1" | sed 's/:/\\:/g; s/$/ \\/'
++}
++
++# for each file print a line in following format
++# <filetype> <name> <path to file> <octal mode> <uid> <gid>
++# for links, devices etc the format differs. See gen_init_cpio for details
++parse() {
++      local location="$1"
++      local name="/${location#${srcdir}}"
++      # change '//' into '/'
++      name=$(echo "$name" | sed -e 's://*:/:g')
++      local mode="$2"
++      local uid="$3"
++      local gid="$4"
++      local ftype=$(filetype "${location}")
++      # remap uid/gid to 0 if necessary
++      [ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0
++      [ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0
++      local str="${mode} ${uid} ${gid}"
++
++      [ "${ftype}" = "invalid" ] && return 0
++      [ "${location}" = "${srcdir}" ] && return 0
++
++      case "${ftype}" in
++              "file")
++                      str="${ftype} ${name} ${location} ${str}"
++                      ;;
++              "nod")
++                      local dev=`LC_ALL=C ls -l "${location}"`
++                      local maj=`field 5 ${dev}`
++                      local min=`field 6 ${dev}`
++                      maj=${maj%,}
++
++                      [ -b "${location}" ] && dev="b" || dev="c"
++
++                      str="${ftype} ${name} ${str} ${dev} ${maj} ${min}"
++                      ;;
++              "slink")
++                      local target=`readlink "${location}"`
++                      str="${ftype} ${name} ${target} ${str}"
++                      ;;
++              *)
++                      str="${ftype} ${name} ${str}"
++                      ;;
++      esac
++
++      echo "${str}" >> ${output}
++
++      return 0
++}
++
++unknown_option() {
++      printf "ERROR: unknown option \"$arg\"\n" >&2
++      printf "If the filename validly begins with '-', " >&2
++      printf "then it must be prefixed\n" >&2
++      printf "by './' so that it won't be interpreted as an option." >&2
++      printf "\n" >&2
++      usage >&2
++      exit 1
++}
++
++list_header() {
++      :
++}
++
++header() {
++      printf "\n#####################\n# $1\n" >> ${output}
++}
++
++# process one directory (incl sub-directories)
++dir_filelist() {
++      ${dep_list}header "$1"
++
++      srcdir=$(echo "$1" | sed -e 's://*:/:g')
++      dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" | LANG=C sort)
++
++      # If $dirlist is only one line, then the directory is empty
++      if [  "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
++              ${dep_list}print_mtime "$1"
++
++              echo "${dirlist}" | \
++              while read x; do
++                      ${dep_list}parse ${x}
++              done
++      fi
++}
++
++# if only one file is specified and it is .cpio file then use it direct as fs
++# if a directory is specified then add all files in given direcotry to fs
++# if a regular file is specified assume it is in gen_initramfs format
++input_file() {
++      source="$1"
++      if [ -f "$1" ]; then
++              ${dep_list}header "$1"
++              is_cpio="$(echo "$1" | sed 's/^.*\.cpio\(\..*\)\{0,1\}/cpio/')"
++              if [ $2 -eq 0 -a ${is_cpio} = "cpio" ]; then
++                      cpio_file=$1
++                      echo "$1" | grep -q '^.*\.cpio\..*' && is_cpio_compressed="compressed"
++                      [ ! -z ${dep_list} ] && echo "$1"
++                      return 0
++              fi
++              if [ -z ${dep_list} ]; then
++                      print_mtime "$1" >> ${output}
++                      cat "$1"         >> ${output}
++              else
++                      echo "$1 \\"
++                      cat "$1" | while read type dir file perm ; do
++                              if [ "$type" = "file" ]; then
++                                      echo "$file \\";
++                              fi
++                      done
++              fi
++      elif [ -d "$1" ]; then
++              dir_filelist "$1"
++      else
++              echo "  ${prog}: Cannot open '$1'" >&2
++              exit 1
++      fi
++}
++
++prog=$0
++root_uid=0
++root_gid=0
++dep_list=
++cpio_file=
++cpio_list=
++output="/dev/stdout"
++output_file=""
++is_cpio_compressed=
++compr="gzip -n -9 -f"
++
++arg="$1"
++case "$arg" in
++      "-l")   # files included in initramfs - used by kbuild
++              dep_list="list_"
++              echo "deps_initramfs := $0 \\"
++              shift
++              ;;
++      "-o")   # generate compressed cpio image named $1
++              shift
++              output_file="$1"
++              cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
++              output=${cpio_list}
++              echo "$output_file" | grep -q "\.gz$" \
++                && [ -x "`which gzip 2> /dev/null`" ] \
++                && compr="gzip -n -9 -f"
++              echo "$output_file" | grep -q "\.bz2$" \
++                && [ -x "`which bzip2 2> /dev/null`" ] \
++                && compr="bzip2 -9 -f"
++              echo "$output_file" | grep -q "\.lzma$" \
++                && [ -x "`which lzma 2> /dev/null`" ] \
++                && compr="lzma -9 -f"
++              echo "$output_file" | grep -q "\.xz$" \
++                && [ -x "`which xz 2> /dev/null`" ] \
++                && compr="xz --check=crc32 --lzma2=dict=1MiB"
++              echo "$output_file" | grep -q "\.lzo$" \
++                && [ -x "`which lzop 2> /dev/null`" ] \
++                && compr="lzop -9 -f"
++              echo "$output_file" | grep -q "\.lz4$" \
++                && [ -x "`which lz4 2> /dev/null`" ] \
++                && compr="lz4 -l -9 -f"
++              echo "$output_file" | grep -q "\.cpio$" && compr="cat"
++              shift
++              ;;
++esac
++while [ $# -gt 0 ]; do
++      arg="$1"
++      shift
++      case "$arg" in
++              "-u")   # map $1 to uid=0 (root)
++                      root_uid="$1"
++                      [ "$root_uid" = "-1" ] && root_uid=$(id -u || echo 0)
++                      shift
++                      ;;
++              "-g")   # map $1 to gid=0 (root)
++                      root_gid="$1"
++                      [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0)
++                      shift
++                      ;;
++              "-d")   # display default initramfs list
++                      default_list="$arg"
++                      ${dep_list}default_initramfs
++                      ;;
++              "-h")
++                      usage
++                      exit 0
++                      ;;
++              *)
++                      case "$arg" in
++                              "-"*)
++                                      unknown_option
++                                      ;;
++                              *)      # input file/dir - process it
++                                      input_file "$arg" "$#"
++                                      ;;
++                      esac
++                      ;;
++      esac
++done
++
++# If output_file is set we will generate cpio archive and compress it
++# we are careful to delete tmp files
++if [ ! -z ${output_file} ]; then
++      if [ -z ${cpio_file} ]; then
++              timestamp=
++              if test -n "$KBUILD_BUILD_TIMESTAMP"; then
++                      timestamp="$(date -d"$KBUILD_BUILD_TIMESTAMP" +%s || :)"
++                      if test -n "$timestamp"; then
++                              timestamp="-t $timestamp"
++                      fi
++              fi
++              cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
++              usr/gen_init_cpio $timestamp ${cpio_list} > ${cpio_tfile}
++      else
++              cpio_tfile=${cpio_file}
++      fi
++      rm ${cpio_list}
++      if [ "${is_cpio_compressed}" = "compressed" ]; then
++              cat ${cpio_tfile} > ${output_file}
++      else
++              (cat ${cpio_tfile} | ${compr}  - > ${output_file}) \
++              || (rm -f ${output_file} ; false)
++      fi
++      [ -z ${cpio_file} ] && rm ${cpio_tfile}
++fi
++exit 0
diff --git a/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch b/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch
new file mode 100644 (file)
index 0000000..05561f1
--- /dev/null
@@ -0,0 +1,33 @@
+From dcc2827ed6e701a65731c05b0297745559837217 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 12 May 2023 17:33:20 +0800
+Subject: [PATCH 062/116] i2c: designware: Delete SMBus functionalities
+
+The driver didn't implement the smbus interface,
+so replace the SMBus functionalities with
+I2C_FUNC_SMBUS_EMUL.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/i2c/busses/i2c-designware-core.h | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-designware-core.h
++++ b/drivers/i2c/busses/i2c-designware-core.h
+@@ -18,12 +18,10 @@
+ #include <linux/regmap.h>
+ #include <linux/types.h>
+-#define DW_IC_DEFAULT_FUNCTIONALITY           (I2C_FUNC_I2C | \
+-                                               I2C_FUNC_SMBUS_BYTE | \
+-                                               I2C_FUNC_SMBUS_BYTE_DATA | \
+-                                               I2C_FUNC_SMBUS_WORD_DATA | \
+-                                               I2C_FUNC_SMBUS_BLOCK_DATA | \
+-                                               I2C_FUNC_SMBUS_I2C_BLOCK)
++#define DW_IC_DEFAULT_FUNCTIONALITY   (I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL \
++                                      & ~I2C_FUNC_SMBUS_QUICK \
++                                      & ~I2C_FUNC_SMBUS_PROC_CALL \
++                                      & ~I2C_FUNC_SMBUS_PEC))
+ #define DW_IC_CON_MASTER                      BIT(0)
+ #define DW_IC_CON_SPEED_STD                   (1 << 1)
diff --git a/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch b/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch
new file mode 100644 (file)
index 0000000..43a7e5b
--- /dev/null
@@ -0,0 +1,26 @@
+From b61cefc6c785aa8a7177a0b535db746fd0047bd8 Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Fri, 19 Jan 2024 15:22:55 +0800
+Subject: [PATCH 063/116] drivers: mtd: gigadevice: add gd25lq256d 32M flash
+ support
+
+add gd25lq256d 32M flash support
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ drivers/mtd/spi-nor/gigadevice.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -66,6 +66,10 @@ static const struct flash_info gigadevic
+               FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+                             SPI_NOR_QUAD_READ) },
++      { "gd25lq256d", INFO(0xc86019, 0, 64 * 1024, 512)
++              FLAGS( SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_QUAD_PP)
++              NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
++                              SPI_NOR_QUAD_READ) },
+       { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512)
+               PARSE_SFDP
+               FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
diff --git a/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch b/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch
new file mode 100644 (file)
index 0000000..b733110
--- /dev/null
@@ -0,0 +1,808 @@
+From 76bc13aa12bd111f5da01e107f8d487b20b5a40c Mon Sep 17 00:00:00 2001
+From: "shanlong.li" <shanlong.li@starfivetech.com>
+Date: Thu, 8 Jun 2023 00:07:15 -0700
+Subject: [PATCH 064/116] driver: mailbox: Add mailbox driver
+
+Add mailbox driver.
+
+Signed-off-by: shanlong.li <shanlong.li@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/mailbox/Kconfig                 |  13 +
+ drivers/mailbox/Makefile                |   4 +
+ drivers/mailbox/starfive_mailbox-test.c | 407 ++++++++++++++++++++++++
+ drivers/mailbox/starfive_mailbox.c      | 347 ++++++++++++++++++++
+ 4 files changed, 771 insertions(+)
+ create mode 100644 drivers/mailbox/starfive_mailbox-test.c
+ create mode 100644 drivers/mailbox/starfive_mailbox.c
+
+--- a/drivers/mailbox/Kconfig
++++ b/drivers/mailbox/Kconfig
+@@ -295,4 +295,17 @@ config QCOM_IPCC
+         acts as an interrupt controller for receiving interrupts from clients.
+         Say Y here if you want to build this driver.
++config STARFIVE_MBOX
++      tristate "Platform Starfive Mailbox"
++      depends on OF
++      help
++        Say Y here if you want to build a platform specific variant RISCV
++        controller driver.
++
++config STARFIVE_MBOX_TEST
++      tristate "Starfive Mailbox Test Client"
++      depends on OF
++      depends on HAS_IOMEM
++      help
++        Test client to help with testing new Controller driver implementations.
+ endif
+--- a/drivers/mailbox/Makefile
++++ b/drivers/mailbox/Makefile
+@@ -62,3 +62,7 @@ obj-$(CONFIG_SPRD_MBOX)              += sprd-mailbox
+ obj-$(CONFIG_QCOM_IPCC)               += qcom-ipcc.o
+ obj-$(CONFIG_APPLE_MAILBOX)   += apple-mailbox.o
++
++obj-$(CONFIG_STARFIVE_MBOX)    += starfive_mailbox.o
++
++obj-$(CONFIG_STARFIVE_MBOX_TEST)    += starfive_mailbox-test.o
+--- /dev/null
++++ b/drivers/mailbox/starfive_mailbox-test.c
+@@ -0,0 +1,407 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2015 ST Microelectronics
++ *
++ * Author: Lee Jones <lee.jones@linaro.org>
++ */
++
++#include <linux/debugfs.h>
++#include <linux/err.h>
++#include <linux/fs.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/mailbox_client.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/sched/signal.h>
++
++#include <linux/mailbox_controller.h>
++
++#define MBOX_MAX_SIG_LEN      8
++#define MBOX_MAX_MSG_LEN      16
++#define MBOX_BYTES_PER_LINE   16
++#define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2)
++#define MBOX_HEXDUMP_MAX_LEN  (MBOX_HEXDUMP_LINE_LEN * (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))
++
++static bool mbox_data_ready;
++
++struct mbox_test_device {
++      struct device           *dev;
++      void __iomem            *tx_mmio;
++      void __iomem            *rx_mmio;
++      struct mbox_chan        *tx_channel;
++      struct mbox_chan        *rx_channel;
++      char                    *rx_buffer;
++      char                    *signal;
++      char                    *message;
++      spinlock_t              lock;
++      wait_queue_head_t       waitq;
++      struct fasync_struct    *async_queue;
++      struct dentry           *root_debugfs_dir;
++};
++
++static ssize_t mbox_test_signal_write(struct file *filp,
++              const char __user *userbuf,
++              size_t count, loff_t *ppos)
++{
++      struct mbox_test_device *tdev = filp->private_data;
++
++      if (!tdev->tx_channel) {
++              dev_err(tdev->dev, "Channel cannot do Tx\n");
++              return -EINVAL;
++      }
++
++      if (count > MBOX_MAX_SIG_LEN) {
++              dev_err(tdev->dev,
++                      "Signal length %zd greater than max allowed %d\n",
++                      count, MBOX_MAX_SIG_LEN);
++              return -EINVAL;
++      }
++
++      /* Only allocate memory if we need to */
++      if (!tdev->signal) {
++              tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL);
++              if (!tdev->signal)
++                      return -ENOMEM;
++      }
++
++      if (copy_from_user(tdev->signal, userbuf, count)) {
++              kfree(tdev->signal);
++              tdev->signal = NULL;
++              return -EFAULT;
++      }
++
++      return count;
++}
++
++static const struct file_operations mbox_test_signal_ops = {
++      .write = mbox_test_signal_write,
++      .open = simple_open,
++      .llseek = generic_file_llseek,
++};
++
++static int mbox_test_message_fasync(int fd, struct file *filp, int on)
++{
++      struct mbox_test_device *tdev = filp->private_data;
++
++      return fasync_helper(fd, filp, on, &tdev->async_queue);
++}
++
++static ssize_t mbox_test_message_write(struct file *filp,
++              const char __user *userbuf,
++              size_t count, loff_t *ppos)
++{
++      struct mbox_test_device *tdev = filp->private_data;
++      void *data;
++      int ret;
++
++      if (!tdev->tx_channel) {
++              dev_err(tdev->dev, "Channel cannot do Tx\n");
++              return -EINVAL;
++      }
++
++      if (count > MBOX_MAX_MSG_LEN) {
++              dev_err(tdev->dev,
++                      "Message length %zd greater than max allowed %d\n",
++                      count, MBOX_MAX_MSG_LEN);
++              return -EINVAL;
++      }
++
++      tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL);
++      if (!tdev->message)
++              return -ENOMEM;
++
++      ret = copy_from_user(tdev->message, userbuf, count);
++      if (ret) {
++              ret = -EFAULT;
++              goto out;
++      }
++
++      if (tdev->tx_mmio && tdev->signal) {
++              print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS,
++                      tdev->signal, MBOX_MAX_SIG_LEN);
++
++              data = tdev->signal;
++      } else
++              data = tdev->message;
++
++      print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS,
++              tdev->message, MBOX_MAX_MSG_LEN);
++
++      ret = mbox_send_message(tdev->tx_channel, data);
++      mbox_chan_txdone(tdev->tx_channel, ret);
++      if (ret < 0)
++              dev_err(tdev->dev, "Failed to send message via mailbox\n");
++
++out:
++      kfree(tdev->signal);
++      kfree(tdev->message);
++      tdev->signal = NULL;
++
++      return ret < 0 ? ret : count;
++}
++
++static bool mbox_test_message_data_ready(struct mbox_test_device *tdev)
++{
++      bool data_ready;
++      unsigned long flags;
++
++      spin_lock_irqsave(&tdev->lock, flags);
++      data_ready = mbox_data_ready;
++      spin_unlock_irqrestore(&tdev->lock, flags);
++
++      return data_ready;
++}
++
++static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf,
++              size_t count, loff_t *ppos)
++{
++      struct mbox_test_device *tdev = filp->private_data;
++      unsigned long flags;
++      char *touser, *ptr;
++      int ret;
++
++      touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL);
++      if (!touser)
++              return -ENOMEM;
++
++      if (!tdev->rx_channel) {
++              ret = snprintf(touser, 20, "<NO RX CAPABILITY>\n");
++              ret = simple_read_from_buffer(userbuf, count, ppos,
++                      touser, ret);
++              goto kfree_err;
++      }
++
++      do {
++              if (mbox_test_message_data_ready(tdev))
++                      break;
++
++              if (filp->f_flags & O_NONBLOCK) {
++                      ret = -EAGAIN;
++                      goto waitq_err;
++              }
++
++              if (signal_pending(current)) {
++                      ret = -ERESTARTSYS;
++                      goto waitq_err;
++              }
++              schedule();
++
++      } while (1);
++
++      spin_lock_irqsave(&tdev->lock, flags);
++
++      ptr = tdev->rx_buffer;
++
++      mbox_data_ready = false;
++
++      spin_unlock_irqrestore(&tdev->lock, flags);
++      if (copy_to_user((void __user *)userbuf, ptr, 4))
++              ret = -EFAULT;
++
++waitq_err:
++      __set_current_state(TASK_RUNNING);
++kfree_err:
++      kfree(touser);
++      return ret;
++}
++
++static __poll_t
++mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait)
++{
++      struct mbox_test_device *tdev = filp->private_data;
++
++      poll_wait(filp, &tdev->waitq, wait);
++
++      if (mbox_test_message_data_ready(tdev))
++              return EPOLLIN | EPOLLRDNORM;
++      return 0;
++}
++
++static const struct file_operations mbox_test_message_ops = {
++      .write = mbox_test_message_write,
++      .read = mbox_test_message_read,
++      .fasync = mbox_test_message_fasync,
++      .poll = mbox_test_message_poll,
++      .open = simple_open,
++      .llseek = generic_file_llseek,
++};
++
++static int mbox_test_add_debugfs(struct platform_device *pdev,
++              struct mbox_test_device *tdev)
++{
++      if (!debugfs_initialized())
++              return 0;
++
++      tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL);
++      if (!tdev->root_debugfs_dir) {
++              dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n");
++              return -EINVAL;
++      }
++
++      debugfs_create_file("message", 0600, tdev->root_debugfs_dir,
++              tdev, &mbox_test_message_ops);
++
++      debugfs_create_file("signal", 0200, tdev->root_debugfs_dir,
++              tdev, &mbox_test_signal_ops);
++
++      return 0;
++}
++
++static void mbox_test_receive_message(struct mbox_client *client, void *message)
++{
++      struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
++      unsigned long flags;
++
++      spin_lock_irqsave(&tdev->lock, flags);
++      if (tdev->rx_mmio) {
++              memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN);
++              print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS,
++                      tdev->rx_buffer, MBOX_MAX_MSG_LEN);
++      } else if (message) {
++              print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS,
++                      message, MBOX_MAX_MSG_LEN);
++              memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN);
++      }
++      mbox_data_ready = true;
++      spin_unlock_irqrestore(&tdev->lock, flags);
++}
++
++static void mbox_test_prepare_message(struct mbox_client *client, void *message)
++{
++      struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
++
++      if (tdev->tx_mmio) {
++              if (tdev->signal)
++                      memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN);
++              else
++                      memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN);
++      }
++}
++
++static struct mbox_chan *
++mbox_test_request_channel(struct platform_device *pdev, const char *name)
++{
++      struct mbox_client *client;
++      struct mbox_chan *channel;
++
++      client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
++      if (!client)
++              return ERR_PTR(-ENOMEM);
++
++      client->dev        = &pdev->dev;
++      client->rx_callback    = mbox_test_receive_message;
++      client->tx_prepare    = mbox_test_prepare_message;
++      client->tx_block    = false;
++      client->knows_txdone    = false;
++      client->tx_tout        = 500;
++
++      channel = mbox_request_channel_byname(client, name);
++      if (IS_ERR(channel)) {
++              dev_warn(&pdev->dev, "Failed to request %s channel\n", name);
++              return NULL;
++      }
++
++      return channel;
++}
++
++static int mbox_test_probe(struct platform_device *pdev)
++{
++      struct mbox_test_device *tdev;
++      struct resource *res;
++      resource_size_t size;
++      int ret;
++
++      tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
++      if (!tdev)
++              return -ENOMEM;
++
++      /* It's okay for MMIO to be NULL */
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
++      if (PTR_ERR(tdev->tx_mmio) == -EBUSY) {
++              /* if reserved area in SRAM, try just ioremap */
++              size = resource_size(res);
++              tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size);
++      } else if (IS_ERR(tdev->tx_mmio)) {
++              tdev->tx_mmio = NULL;
++      }
++
++      /* If specified, second reg entry is Rx MMIO */
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++      tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
++      if (PTR_ERR(tdev->rx_mmio) == -EBUSY) {
++              size = resource_size(res);
++              tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size);
++      } else if (IS_ERR(tdev->rx_mmio)) {
++              tdev->rx_mmio = tdev->tx_mmio;
++      }
++
++      tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
++      tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
++
++      if (!tdev->tx_channel && !tdev->rx_channel)
++              return -EPROBE_DEFER;
++
++      /* If Rx is not specified but has Rx MMIO, then Rx = Tx */
++      if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio))
++              tdev->rx_channel = tdev->tx_channel;
++
++      tdev->dev = &pdev->dev;
++      platform_set_drvdata(pdev, tdev);
++
++      spin_lock_init(&tdev->lock);
++
++      if (tdev->rx_channel) {
++              tdev->rx_buffer = devm_kzalloc(&pdev->dev,
++                      MBOX_MAX_MSG_LEN, GFP_KERNEL);
++              if (!tdev->rx_buffer)
++                      return -ENOMEM;
++      }
++
++      ret = mbox_test_add_debugfs(pdev, tdev);
++      if (ret)
++              return ret;
++
++      dev_info(&pdev->dev, "Successfully registered\n");
++
++      return 0;
++}
++
++static int mbox_test_remove(struct platform_device *pdev)
++{
++      struct mbox_test_device *tdev = platform_get_drvdata(pdev);
++
++      debugfs_remove_recursive(tdev->root_debugfs_dir);
++
++      if (tdev->tx_channel)
++              mbox_free_channel(tdev->tx_channel);
++      if (tdev->rx_channel)
++              mbox_free_channel(tdev->rx_channel);
++
++      return 0;
++}
++
++static const struct of_device_id mbox_test_match[] = {
++      { .compatible = "starfive,mailbox-test" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, mbox_test_match);
++
++static struct platform_driver mbox_test_driver = {
++      .driver = {
++              .name = "mailbox_test",
++              .of_match_table = mbox_test_match,
++      },
++      .probe  = mbox_test_probe,
++      .remove = mbox_test_remove,
++};
++module_platform_driver(mbox_test_driver);
++
++MODULE_DESCRIPTION("Generic Mailbox Testing Facility");
++MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/drivers/mailbox/starfive_mailbox.c
+@@ -0,0 +1,347 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * mailbox driver for StarFive JH7110 SoC
++ *
++ * Copyright (c) 2021 StarFive Technology Co., Ltd.
++ * Author: Shanlong Li <shanlong.li@starfivetech.com>
++ */
++
++#include <linux/bitops.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/mailbox_controller.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/pm_runtime.h>
++
++#include "mailbox.h"
++
++#define MBOX_CHAN_MAX         4
++
++#define MBOX_BASE(mbox, ch)   ((mbox)->base + ((ch) * 0x10))
++#define MBOX_IRQ_REG          0x00
++#define MBOX_SET_REG          0x04
++#define MBOX_CLR_REG          0x08
++#define MBOX_CMD_REG          0x0c
++#define MBC_PEND_SMRY         0x100
++
++typedef enum {
++      MAILBOX_CORE_U7 = 0,
++      MAILBOX_CORE_HIFI4,
++      MAILBOX_CORE_E2,
++      MAILBOX_CORE_RSVD0,
++      MAILBOX_CORE_NUM,
++} mailbox_core_t;
++
++struct mailbox_irq_name_c{
++      int id;
++      char name[16];
++};
++
++static const struct mailbox_irq_name_c irq_peer_name[MBOX_CHAN_MAX] = {
++      {MAILBOX_CORE_U7,    "u74_core"},
++      {MAILBOX_CORE_HIFI4, "hifi4_core"},
++      {MAILBOX_CORE_E2,    "e24_core"},
++      {MAILBOX_CORE_RSVD0, "" },
++};
++
++/**
++ * starfive mailbox channel information
++ *
++ * A channel can be used for TX or RX, it can trigger remote
++ * processor interrupt to notify remote processor and can receive
++ * interrupt if has incoming message.
++ *
++ * @dst_irq:    Interrupt vector for remote processor
++ * @core_id:    id for remote processor
++ */
++struct starfive_chan_info {
++      unsigned int dst_irq;
++      mailbox_core_t core_id;
++};
++
++/**
++ * starfive mailbox controller data
++ *
++ * Mailbox controller includes 4 channels and can allocate
++ * channel for message transferring.
++ *
++ * @dev:    Device to which it is attached
++ * @base:    Base address of the register mapping region
++ * @chan:    Representation of channels in mailbox controller
++ * @mchan:    Representation of channel info
++ * @controller:    Representation of a communication channel controller
++ */
++struct starfive_mbox {
++      struct device *dev;
++      void __iomem *base;
++      struct mbox_chan chan[MBOX_CHAN_MAX];
++      struct starfive_chan_info mchan[MBOX_CHAN_MAX];
++      struct mbox_controller controller;
++      struct clk *clk;
++      struct reset_control *rst_rresetn;
++};
++
++static struct starfive_mbox *to_starfive_mbox(struct mbox_controller *mbox)
++{
++      return container_of(mbox, struct starfive_mbox, controller);
++}
++
++static struct mbox_chan *
++starfive_of_mbox_index_xlate(struct mbox_controller *mbox,
++                      const struct of_phandle_args *sp)
++{
++      struct starfive_mbox *sbox;
++
++      int ind = sp->args[0];
++      int core_id = sp->args[1];
++
++      if (ind >= mbox->num_chans || core_id >= MAILBOX_CORE_NUM)
++              return ERR_PTR(-EINVAL);
++
++      sbox = to_starfive_mbox(mbox);
++
++      sbox->mchan[ind].core_id = core_id;
++
++      return &mbox->chans[ind];
++}
++
++static irqreturn_t starfive_rx_irq_handler(int irq, void *p)
++{
++      struct mbox_chan *chan = p;
++      unsigned long ch = (unsigned long)chan->con_priv;
++      struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++      void __iomem *base = MBOX_BASE(mbox, ch);
++      u32 val;
++
++      val = readl(base + MBOX_CMD_REG);
++      if (!val)
++              return IRQ_NONE;
++
++      mbox_chan_received_data(chan, (void *)&val);
++      writel(val, base + MBOX_CLR_REG);
++      return IRQ_HANDLED;
++}
++
++static int starfive_mbox_check_state(struct mbox_chan *chan)
++{
++      unsigned long ch = (unsigned long)chan->con_priv;
++      struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++      unsigned long irq_flag = IRQF_SHARED;
++      long ret = 0;
++
++      pm_runtime_get_sync(mbox->dev);
++      /* MAILBOX should be with IRQF_NO_SUSPEND set */
++      if (!mbox->dev->pm_domain)
++              irq_flag |= IRQF_NO_SUSPEND;
++
++      /* Mailbox is idle so directly bail out */
++      if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch))
++              return -EBUSY;
++
++      if (mbox->mchan[ch].dst_irq > 0) {
++              dev_dbg(mbox->dev, "%s: host IRQ = %d, ch:%ld", __func__, mbox->mchan[ch].dst_irq, ch);
++              ret = devm_request_irq(mbox->dev, mbox->mchan[ch].dst_irq, starfive_rx_irq_handler,
++                      irq_flag, irq_peer_name[ch].name, chan);
++              if (ret < 0)
++                      dev_err(mbox->dev, "request_irq %d failed\n", mbox->mchan[ch].dst_irq);
++      }
++
++      return ret;
++}
++
++static int starfive_mbox_startup(struct mbox_chan *chan)
++{
++      return starfive_mbox_check_state(chan);
++}
++
++static void starfive_mbox_shutdown(struct mbox_chan *chan)
++{
++      struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++      unsigned long ch = (unsigned long)chan->con_priv;
++      void __iomem *base = MBOX_BASE(mbox, ch);
++
++      writel(0x0, base + MBOX_IRQ_REG);
++      writel(0x0, base + MBOX_CLR_REG);
++
++      if (mbox->mchan[ch].dst_irq > 0)
++              devm_free_irq(mbox->dev, mbox->mchan[ch].dst_irq, chan);
++      pm_runtime_put_sync(mbox->dev);
++}
++
++static int starfive_mbox_send_data(struct mbox_chan *chan, void *msg)
++{
++      unsigned long ch = (unsigned long)chan->con_priv;
++      struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
++      struct starfive_chan_info *mchan = &mbox->mchan[ch];
++      void __iomem *base = MBOX_BASE(mbox, ch);
++      u32 *buf = msg;
++
++      /* Ensure channel is released */
++      if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) {
++              pr_debug("%s:%d. busy\n", __func__, __LINE__);
++              return -EBUSY;
++      }
++
++      /* Clear mask for destination interrupt */
++      writel(BIT(mchan->core_id), base + MBOX_IRQ_REG);
++
++      /* Fill message data */
++      writel(*buf, base + MBOX_SET_REG);
++      return 0;
++}
++
++static struct mbox_chan_ops starfive_mbox_ops = {
++      .startup = starfive_mbox_startup,
++      .send_data = starfive_mbox_send_data,
++      .shutdown = starfive_mbox_shutdown,
++};
++
++static const struct of_device_id starfive_mbox_of_match[] = {
++      { .compatible = "starfive,mail_box",},
++      {},
++};
++
++MODULE_DEVICE_TABLE(of, starfive_mbox_of_match);
++
++void starfive_mailbox_init(struct starfive_mbox *mbox)
++{
++      mbox->clk = devm_clk_get_optional(mbox->dev, "clk_apb");
++      if (IS_ERR(mbox->clk)) {
++              dev_err(mbox->dev, "failed to get mailbox\n");
++              return;
++      }
++
++      mbox->rst_rresetn = devm_reset_control_get_exclusive(mbox->dev, "mbx_rre");
++      if (IS_ERR(mbox->rst_rresetn)) {
++              dev_err(mbox->dev, "failed to get mailbox reset\n");
++              return;
++      }
++
++      clk_prepare_enable(mbox->clk);
++      reset_control_deassert(mbox->rst_rresetn);
++}
++
++static int starfive_mbox_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct starfive_mbox *mbox;
++      struct mbox_chan *chan;
++      struct resource *res;
++      unsigned long ch;
++      int err;
++
++      mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
++      if (!mbox)
++              return -ENOMEM;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      mbox->base = devm_ioremap_resource(dev, res);
++      mbox->dev = dev;
++
++      if (IS_ERR(mbox->base))
++              return PTR_ERR(mbox->base);
++
++      starfive_mailbox_init(mbox);
++
++      mbox->controller.dev = dev;
++      mbox->controller.chans = mbox->chan;
++      mbox->controller.num_chans = MBOX_CHAN_MAX;
++      mbox->controller.ops = &starfive_mbox_ops;
++      mbox->controller.of_xlate = starfive_of_mbox_index_xlate;
++      mbox->controller.txdone_irq = true;
++      mbox->controller.txdone_poll = false;
++
++      /* Initialize mailbox channel data */
++      chan = mbox->chan;
++      for (ch = 0; ch < MBOX_CHAN_MAX; ch++) {
++              mbox->mchan[ch].dst_irq = 0;
++              mbox->mchan[ch].core_id = (mailbox_core_t)ch;
++              chan[ch].con_priv = (void *)ch;
++      }
++      mbox->mchan[MAILBOX_CORE_HIFI4].dst_irq = platform_get_irq(pdev, 0);
++      mbox->mchan[MAILBOX_CORE_E2].dst_irq = platform_get_irq(pdev, 1);
++
++      err = mbox_controller_register(&mbox->controller);
++      if (err) {
++              dev_err(dev, "Failed to register mailbox %d\n", err);
++              return err;
++      }
++
++      platform_set_drvdata(pdev, mbox);
++      dev_info(dev, "Mailbox enabled\n");
++      pm_runtime_set_active(dev);
++      pm_runtime_enable(dev);
++
++      return 0;
++}
++
++static int starfive_mbox_remove(struct platform_device *pdev)
++{
++      struct starfive_mbox *mbox = platform_get_drvdata(pdev);
++
++      mbox_controller_unregister(&mbox->controller);
++      devm_clk_put(mbox->dev, mbox->clk);
++      pm_runtime_disable(mbox->dev);
++
++      return 0;
++}
++
++static int __maybe_unused starfive_mbox_suspend(struct device *dev)
++{
++      struct starfive_mbox *mbox = dev_get_drvdata(dev);
++
++      clk_disable_unprepare(mbox->clk);
++
++      return 0;
++}
++
++static int __maybe_unused starfive_mbox_resume(struct device *dev)
++{
++      struct starfive_mbox *mbox = dev_get_drvdata(dev);
++      int ret;
++
++      ret = clk_prepare_enable(mbox->clk);
++      if (ret)
++              dev_err(dev, "failed to enable clock\n");
++
++      return ret;
++}
++
++static const struct dev_pm_ops starfive_mbox_pm_ops = {
++      .suspend = starfive_mbox_suspend,
++      .resume = starfive_mbox_resume,
++      SET_RUNTIME_PM_OPS(starfive_mbox_suspend, starfive_mbox_resume, NULL)
++};
++static struct platform_driver starfive_mbox_driver = {
++      .probe  = starfive_mbox_probe,
++      .remove = starfive_mbox_remove,
++      .driver = {
++      .name = "mailbox",
++              .of_match_table = starfive_mbox_of_match,
++              .pm = &starfive_mbox_pm_ops,
++      },
++};
++
++static int __init starfive_mbox_init(void)
++{
++      return platform_driver_register(&starfive_mbox_driver);
++}
++core_initcall(starfive_mbox_init);
++
++static void __exit starfive_mbox_exit(void)
++{
++      platform_driver_unregister(&starfive_mbox_driver);
++}
++module_exit(starfive_mbox_exit);
++
++MODULE_DESCRIPTION("StarFive Mailbox Controller driver");
++MODULE_AUTHOR("Shanlong Li <shanlong.li@starfivetech.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch b/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch
new file mode 100644 (file)
index 0000000..0257cb0
--- /dev/null
@@ -0,0 +1,789 @@
+From 0f44bd6bec708782f38bba4d03deecf927d1c83d Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfivetech.com>
+Date: Fri, 9 Jun 2023 15:31:53 +0800
+Subject: [PATCH 065/116] driver: rtc: Add StarFive JH7110 rtc driver
+
+Add RTC driver and support for StarFive JH7110 SoC.
+
+Signed-off-by: ziv.xu <ziv.xu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/rtc/Kconfig        |   8 +
+ drivers/rtc/Makefile       |   1 +
+ drivers/rtc/rtc-starfive.c | 743 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 752 insertions(+)
+ create mode 100644 drivers/rtc/rtc-starfive.c
+
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -1327,6 +1327,14 @@ config RTC_DRV_NTXEC
+         embedded controller found in certain e-book readers designed by the
+         original design manufacturer Netronix.
++config RTC_DRV_STARFIVE
++      tristate "StarFive 32.768k-RTC"
++      depends on ARCH_STARFIVE
++      depends on OF
++      help
++        If you say Y here you will get support for the RTC found on
++        StarFive SOCS.
++
+ comment "on-CPU RTC drivers"
+ config RTC_DRV_ASM9260
+--- a/drivers/rtc/Makefile
++++ b/drivers/rtc/Makefile
+@@ -163,6 +163,7 @@ obj-$(CONFIG_RTC_DRV_SH)   += rtc-sh.o
+ obj-$(CONFIG_RTC_DRV_SNVS)    += rtc-snvs.o
+ obj-$(CONFIG_RTC_DRV_SPEAR)   += rtc-spear.o
+ obj-$(CONFIG_RTC_DRV_STARFIRE)        += rtc-starfire.o
++obj-$(CONFIG_RTC_DRV_STARFIVE)        += rtc-starfive.o
+ obj-$(CONFIG_RTC_DRV_STK17TA8)        += rtc-stk17ta8.o
+ obj-$(CONFIG_RTC_DRV_ST_LPC)  += rtc-st-lpc.o
+ obj-$(CONFIG_RTC_DRV_STM32)   += rtc-stm32.o
+--- /dev/null
++++ b/drivers/rtc/rtc-starfive.c
+@@ -0,0 +1,743 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * RTC driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2021 StarFive Technology Co., Ltd.
++ */
++
++#include <asm/delay.h>
++#include <linux/bcd.h>
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/completion.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/iopoll.h>
++#include <linux/platform_device.h>
++#include <linux/rtc.h>
++
++/* Registers */
++#define SFT_RTC_CFG           0x00
++#define SFT_RTC_SW_CAL_VALUE  0x04
++#define SFT_RTC_HW_CAL_CFG    0x08
++#define SFT_RTC_CMP_CFG               0x0C
++#define SFT_RTC_IRQ_EN                0x10
++#define SFT_RTC_IRQ_EVEVT     0x14
++#define SFT_RTC_IRQ_STATUS    0x18
++#define SFT_RTC_CAL_VALUE     0x24
++#define SFT_RTC_CFG_TIME      0x28
++#define SFT_RTC_CFG_DATE      0x2C
++#define SFT_RTC_ACT_TIME      0x34
++#define SFT_RTC_ACT_DATE      0x38
++#define SFT_RTC_TIME          0x3C
++#define SFT_RTC_DATE          0x40
++#define SFT_RTC_TIME_LATCH    0x44
++#define SFT_RTC_DATE_LATCH    0x48
++
++/* RTC_CFG */
++#define RTC_CFG_ENABLE_SHIFT  0  /* RW: RTC Enable. */
++#define RTC_CFG_CAL_EN_HW_SHIFT       1  /* RW: Enable of hardware calibretion. */
++#define RTC_CFG_CAL_SEL_SHIFT 2  /* RW: select the hw/sw calibretion mode.*/
++#define RTC_CFG_HOUR_MODE_SHIFT       3  /* RW: time hour mode. 24h|12h */
++
++/* RTC_SW_CAL_VALUE */
++#define RTC_SW_CAL_VALUE_MASK GENMASK(15, 0)
++#define RTC_SW_CAL_MAX                RTC_SW_CAL_VALUE_MASK
++#define RTC_SW_CAL_MIN                0
++#define RTC_TICKS_PER_SEC     32768           /* Number of ticks per second */
++#define RTC_PPB_MULT          1000000000LL    /* Multiplier for ppb conversions */
++
++/* RTC_HW_CAL_CFG */
++#define RTC_HW_CAL_REF_SEL_SHIFT      0
++#define RTC_HW_CAL_FRQ_SEL_SHIFT      1
++
++/* IRQ_EN/IRQ_EVEVT/IRQ_STATUS */
++#define RTC_IRQ_CAL_START     BIT(0)
++#define RTC_IRQ_CAL_FINISH    BIT(1)
++#define RTC_IRQ_CMP           BIT(2)
++#define RTC_IRQ_1SEC          BIT(3)
++#define RTC_IRQ_ALAEM         BIT(4)
++#define RTC_IRQ_EVT_UPDATE_PSE        BIT(31) /* WO: Enable of update time&&date, IRQ_EVEVT only */
++#define RTC_IRQ_ALL           (RTC_IRQ_CAL_START \
++                              | RTC_IRQ_CAL_FINISH \
++                              | RTC_IRQ_CMP \
++                              | RTC_IRQ_1SEC \
++                              | RTC_IRQ_ALAEM)
++
++/* CAL_VALUE */
++#define RTC_CAL_VALUE_MASK    GENMASK(15, 0)
++
++/* CFG_TIME/ACT_TIME/RTC_TIME */
++#define TIME_SEC_MASK         GENMASK(6, 0)
++#define TIME_MIN_MASK         GENMASK(13, 7)
++#define TIME_HOUR_MASK                GENMASK(20, 14)
++
++/* CFG_DATE/ACT_DATE/RTC_DATE */
++#define DATE_DAY_MASK         GENMASK(5, 0)
++#define DATE_MON_MASK         GENMASK(10, 6)
++#define DATE_YEAR_MASK                GENMASK(18, 11)
++
++#define INT_TIMEOUT_US                180
++
++enum RTC_HOUR_MODE {
++      RTC_HOUR_MODE_12H = 0,
++      RTC_HOUR_MODE_24H = 1
++};
++
++enum RTC_CAL_MODE {
++      RTC_CAL_MODE_SW = 0,
++      RTC_CAL_MODE_HW = 1
++};
++
++enum RTC_HW_CAL_REF_MODE {
++      RTC_CAL_CLK_REF = 0,
++      RTC_CAL_CLK_MARK = 1
++};
++
++static const unsigned long refclk_list[] = {
++      1000000,
++      2000000,
++      4000000,
++      5927000,
++      6000000,
++      7200000,
++      8000000,
++      10250000,
++      11059200,
++      12000000,
++      12288000,
++      13560000,
++      16000000,
++      19200000,
++      20000000,
++      22118000,
++      24000000,
++      24567000,
++      25000000,
++      26000000,
++      27000000,
++      30000000,
++      32000000,
++      33868800,
++      36000000,
++      36860000,
++      40000000,
++      44000000,
++      50000000,
++      54000000,
++      28224000,
++      28000000,
++};
++
++struct sft_rtc {
++      struct rtc_device *rtc_dev;
++      struct completion cal_done;
++      struct completion onesec_done;
++      struct clk *pclk;
++      struct clk *cal_clk;
++      struct reset_control *rst_array;
++      int hw_cal_map;
++      void __iomem *regs;
++      int rtc_irq;
++      int ms_pulse_irq;
++      int one_sec_pulse_irq;
++};
++
++static inline void sft_rtc_set_enabled(struct sft_rtc *srtc, bool enabled)
++{
++      u32 val;
++
++      if (enabled) {
++              val = readl(srtc->regs + SFT_RTC_CFG);
++              val |= BIT(RTC_CFG_ENABLE_SHIFT);
++              writel(val, srtc->regs + SFT_RTC_CFG);
++      } else {
++              val = readl(srtc->regs + SFT_RTC_CFG);
++              val &= ~BIT(RTC_CFG_ENABLE_SHIFT);
++              writel(val, srtc->regs + SFT_RTC_CFG);
++      }
++}
++
++static inline bool sft_rtc_get_enabled(struct sft_rtc *srtc)
++{
++      return !!(readl(srtc->regs + SFT_RTC_CFG) & BIT(RTC_CFG_ENABLE_SHIFT));
++}
++
++static inline void sft_rtc_set_mode(struct sft_rtc *srtc, enum RTC_HOUR_MODE mode)
++{
++      u32 val;
++
++      val = readl(srtc->regs + SFT_RTC_CFG);
++      val |= mode << RTC_CFG_HOUR_MODE_SHIFT;
++      writel(val, srtc->regs + SFT_RTC_CFG);
++}
++
++static inline int sft_rtc_irq_enable(struct sft_rtc *srtc, u32 irq, bool enable)
++{
++      u32 val;
++
++      if (!(irq & RTC_IRQ_ALL))
++              return -EINVAL;
++
++      if (enable) {
++              val = readl(srtc->regs + SFT_RTC_IRQ_EN);
++              val |= irq;
++              writel(val, srtc->regs + SFT_RTC_IRQ_EN);
++      } else {
++              val = readl(srtc->regs + SFT_RTC_IRQ_EN);
++              val &= ~irq;
++              writel(val, srtc->regs + SFT_RTC_IRQ_EN);
++      }
++      return 0;
++}
++
++static inline void
++sft_rtc_set_cal_hw_enable(struct sft_rtc *srtc, bool enable)
++{
++      u32 val;
++
++      if (enable) {
++              val = readl(srtc->regs + SFT_RTC_CFG);
++              val |= BIT(RTC_CFG_CAL_EN_HW_SHIFT);
++              writel(val, srtc->regs + SFT_RTC_CFG);
++      } else {
++              val = readl(srtc->regs + SFT_RTC_CFG);
++              val &= ~BIT(RTC_CFG_CAL_EN_HW_SHIFT);
++              writel(val, srtc->regs + SFT_RTC_CFG);
++      }
++}
++
++static inline void
++sft_rtc_set_cal_mode(struct sft_rtc *srtc, enum RTC_CAL_MODE mode)
++{
++      u32 val;
++
++      val = readl(srtc->regs + SFT_RTC_CFG);
++      val |= mode << RTC_CFG_CAL_SEL_SHIFT;
++      writel(val, srtc->regs + SFT_RTC_CFG);
++}
++
++static int sft_rtc_get_hw_calclk(struct device *dev, unsigned long freq)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(refclk_list); i++)
++              if (refclk_list[i] == freq)
++                      return i;
++
++      dev_err(dev, "refclk: %ldHz do not support.\n", freq);
++      return -EINVAL;
++}
++
++static inline void sft_rtc_reg2time(struct rtc_time *tm, u32 reg)
++{
++      tm->tm_hour = bcd2bin(FIELD_GET(TIME_HOUR_MASK, reg));
++      tm->tm_min = bcd2bin(FIELD_GET(TIME_MIN_MASK, reg));
++      tm->tm_sec = bcd2bin(FIELD_GET(TIME_SEC_MASK, reg));
++}
++
++static inline void sft_rtc_reg2date(struct rtc_time *tm, u32 reg)
++{
++      tm->tm_year = bcd2bin(FIELD_GET(DATE_YEAR_MASK, reg)) + 100;
++      tm->tm_mon = bcd2bin(FIELD_GET(DATE_MON_MASK, reg)) - 1;
++      tm->tm_mday = bcd2bin(FIELD_GET(DATE_DAY_MASK, reg));
++}
++
++static inline u32 sft_rtc_time2reg(struct rtc_time *tm)
++{
++      return  FIELD_PREP(TIME_HOUR_MASK, bin2bcd(tm->tm_hour)) |
++              FIELD_PREP(TIME_MIN_MASK, bin2bcd(tm->tm_min)) |
++              FIELD_PREP(TIME_SEC_MASK, bin2bcd(tm->tm_sec));
++}
++
++static inline u32 sft_rtc_date2reg(struct rtc_time *tm)
++{
++      return  FIELD_PREP(DATE_YEAR_MASK, bin2bcd(tm->tm_year - 100)) |
++              FIELD_PREP(DATE_MON_MASK, bin2bcd(tm->tm_mon + 1)) |
++              FIELD_PREP(DATE_DAY_MASK, bin2bcd(tm->tm_mday));
++}
++
++static inline void sft_rtc_update_pulse(struct sft_rtc *srtc)
++{
++      u32 val;
++
++      val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT);
++      val |= RTC_IRQ_EVT_UPDATE_PSE;
++      writel(val, srtc->regs + SFT_RTC_IRQ_EVEVT);
++}
++
++static irqreturn_t sft_rtc_irq_handler(int irq, void *data)
++{
++      struct sft_rtc *srtc = data;
++      struct timerqueue_node *next;
++      u32 irq_flags = 0;
++      u32 irq_mask = 0;
++      u32 val;
++      int ret = 0;
++
++      val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT);
++      if (val & RTC_IRQ_CAL_START)
++              irq_mask |= RTC_IRQ_CAL_START;
++
++      if (val & RTC_IRQ_CAL_FINISH) {
++              irq_mask |= RTC_IRQ_CAL_FINISH;
++              complete(&srtc->cal_done);
++      }
++
++      if (val & RTC_IRQ_CMP)
++              irq_mask |= RTC_IRQ_CMP;
++
++      if (val & RTC_IRQ_1SEC) {
++              irq_flags |= RTC_PF;
++              irq_mask |= RTC_IRQ_1SEC;
++              complete(&srtc->onesec_done);
++      }
++
++      if (val & RTC_IRQ_ALAEM) {
++              irq_flags |= RTC_AF;
++              irq_mask |= RTC_IRQ_ALAEM;
++
++              next = timerqueue_getnext(&srtc->rtc_dev->timerqueue);
++              if (next == &srtc->rtc_dev->aie_timer.node)
++                      dev_info(&srtc->rtc_dev->dev, "alarm expires");
++      }
++
++      writel(irq_mask, srtc->regs + SFT_RTC_IRQ_EVEVT);
++
++      /* Wait interrupt flag clear */
++      ret = readl_poll_timeout_atomic(srtc->regs + SFT_RTC_IRQ_EVEVT, val,
++                                      (val & irq_mask) == 0, 0, INT_TIMEOUT_US);
++      if (ret)
++              dev_warn(&srtc->rtc_dev->dev, "fail to clear rtc interrupt flag\n");
++
++      if (irq_flags)
++              rtc_update_irq(srtc->rtc_dev, 1, irq_flags | RTC_IRQF);
++
++      return IRQ_HANDLED;
++}
++
++static int sft_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      u32 val;
++      int irq_1sec_state_start, irq_1sec_state_end;
++
++      /* If the RTC is disabled, assume the values are invalid */
++      if (!sft_rtc_get_enabled(srtc))
++              return -EINVAL;
++
++      irq_1sec_state_start =
++              (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1;
++
++read_again:
++      val = readl(srtc->regs + SFT_RTC_TIME);
++      sft_rtc_reg2time(tm, val);
++
++      val = readl(srtc->regs + SFT_RTC_DATE);
++      sft_rtc_reg2date(tm, val);
++
++      if (irq_1sec_state_start == 0) {
++              irq_1sec_state_end =
++                      (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1;
++              if (irq_1sec_state_end == 1) {
++                      irq_1sec_state_start = 1;
++                      goto read_again;
++              }
++      }
++
++      return 0;
++}
++
++static int sft_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      u32 val;
++      int ret;
++
++      val = sft_rtc_time2reg(tm);
++      writel(val, srtc->regs + SFT_RTC_CFG_TIME);
++
++      val = sft_rtc_date2reg(tm);
++      writel(val, srtc->regs + SFT_RTC_CFG_DATE);
++
++      /* Update pulse */
++      sft_rtc_update_pulse(srtc);
++
++      /* Ensure that data is fully written */
++      ret = wait_for_completion_interruptible_timeout(&srtc->onesec_done,
++                                                      usecs_to_jiffies(120));
++      if (ret) {
++              dev_warn(dev,
++                       "rtc wait for completion interruptible timeout.\n");
++      }
++      return 0;
++}
++
++static int sft_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++
++      return sft_rtc_irq_enable(srtc, RTC_IRQ_ALAEM, enabled);
++}
++
++static int sft_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      u32 val;
++
++      val = readl(srtc->regs + SFT_RTC_ACT_TIME);
++      sft_rtc_reg2time(&alarm->time, val);
++
++      val = readl(srtc->regs + SFT_RTC_ACT_DATE);
++      sft_rtc_reg2date(&alarm->time, val);
++
++      return 0;
++}
++
++static int sft_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      u32 val;
++
++      sft_rtc_alarm_irq_enable(dev, 0);
++
++      val = sft_rtc_time2reg(&alarm->time);
++      writel(val, srtc->regs + SFT_RTC_ACT_TIME);
++
++      val = sft_rtc_date2reg(&alarm->time);
++      writel(val, srtc->regs + SFT_RTC_ACT_DATE);
++
++      sft_rtc_alarm_irq_enable(dev, alarm->enabled);
++
++      return 0;
++}
++
++static int sft_rtc_get_offset(struct device *dev, long *offset)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      s64 tmp;
++      u32 val;
++
++      val = readl(srtc->regs + SFT_RTC_CAL_VALUE)
++                      & RTC_SW_CAL_VALUE_MASK;
++      val += 1;
++      /*
++       * the adjust val range is [0x0000-0xffff],
++       * the default val is 0x7fff (32768-1),mapping offset=0 ;
++       */
++      tmp = (s64)val - RTC_TICKS_PER_SEC;
++      tmp *= RTC_PPB_MULT;
++      tmp = div_s64(tmp, RTC_TICKS_PER_SEC);
++
++      /* Offset value operates in negative way, so swap sign */
++      *offset = -tmp;
++
++      return 0;
++}
++
++static int sft_rtc_set_offset(struct device *dev, long offset)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      s64 tmp;
++      u32 val;
++
++      tmp = offset * RTC_TICKS_PER_SEC;
++      tmp = div_s64(tmp, RTC_PPB_MULT);
++
++      tmp = RTC_TICKS_PER_SEC - tmp;
++      tmp -= 1;
++      if (tmp > RTC_SW_CAL_MAX || tmp < RTC_SW_CAL_MIN) {
++              dev_err(dev, "offset is out of range.\n");
++              return -EINVAL;
++      }
++
++      val = tmp & RTC_SW_CAL_VALUE_MASK;
++      /* set software calibration value */
++      writel(val, srtc->regs + SFT_RTC_SW_CAL_VALUE);
++
++      /* set CFG_RTC-cal_sel to select calibretion by software. */
++      sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW);
++
++      return 0;
++}
++
++static __maybe_unused int
++sft_rtc_hw_adjustment(struct device *dev, unsigned int enable)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++      u32 val;
++
++      if (srtc->hw_cal_map <= 0) {
++              dev_err(dev, "fail to get cal-clock-freq.\n");
++              return -EFAULT;
++      }
++
++      if (enable) {
++              sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, true);
++
++              /* Set reference clock frequency value */
++              val = readl(srtc->regs + SFT_RTC_HW_CAL_CFG);
++              val |= (srtc->hw_cal_map << RTC_HW_CAL_FRQ_SEL_SHIFT);
++              writel(val, srtc->regs + SFT_RTC_HW_CAL_CFG);
++
++              /* Set CFG_RTC-cal_sel to select calibretion by hardware. */
++              sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_HW);
++
++              /* Set CFG_RTC-cal_en_hw to launch hardware calibretion.*/
++              sft_rtc_set_cal_hw_enable(srtc, true);
++
++              wait_for_completion_interruptible_timeout(&srtc->cal_done,
++                                                        usecs_to_jiffies(100));
++
++              sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, false);
++      } else {
++              sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW);
++              sft_rtc_set_cal_hw_enable(srtc, false);
++      }
++
++      return 0;
++}
++
++static int sft_rtc_get_cal_clk(struct device *dev, struct sft_rtc *srtc)
++{
++      struct device_node *np = dev->of_node;
++      unsigned long cal_clk_freq;
++      u32 freq;
++      int ret;
++
++      srtc->cal_clk = devm_clk_get(dev, "cal_clk");
++      if (IS_ERR(srtc->cal_clk))
++              return PTR_ERR(srtc->cal_clk);
++
++      clk_prepare_enable(srtc->cal_clk);
++
++      cal_clk_freq = clk_get_rate(srtc->cal_clk);
++      if (!cal_clk_freq) {
++              dev_warn(dev,
++                       "get rate failed, next try to get from dts.\n");
++              ret = of_property_read_u32(np, "rtc,cal-clock-freq", &freq);
++              if (!ret) {
++                      cal_clk_freq = (u64)freq;
++              } else {
++                      dev_err(dev,
++                              "Need rtc,cal-clock-freq define in dts.\n");
++                      goto err_disable_cal_clk;
++              }
++      }
++
++      srtc->hw_cal_map = sft_rtc_get_hw_calclk(dev, cal_clk_freq);
++      if (srtc->hw_cal_map < 0) {
++              ret = srtc->hw_cal_map;
++              goto err_disable_cal_clk;
++      }
++
++      return 0;
++
++err_disable_cal_clk:
++      clk_disable_unprepare(srtc->cal_clk);
++
++      return ret;
++}
++
++static int sft_rtc_get_irq(struct platform_device *pdev, struct sft_rtc *srtc)
++{
++      int ret;
++
++      srtc->rtc_irq = platform_get_irq_byname(pdev, "rtc");
++      if (srtc->rtc_irq < 0)
++              return -EINVAL;
++
++      ret = devm_request_irq(&pdev->dev, srtc->rtc_irq,
++                             sft_rtc_irq_handler, 0,
++                              KBUILD_MODNAME, srtc);
++      if (ret)
++              dev_err(&pdev->dev, "Failed to request interrupt, %d\n", ret);
++
++      return ret;
++}
++
++static const struct rtc_class_ops starfive_rtc_ops = {
++      .read_time              = sft_rtc_read_time,
++      .set_time               = sft_rtc_set_time,
++      .read_alarm             = sft_rtc_read_alarm,
++      .set_alarm              = sft_rtc_set_alarm,
++      .alarm_irq_enable       = sft_rtc_alarm_irq_enable,
++      .set_offset             = sft_rtc_set_offset,
++      .read_offset            = sft_rtc_get_offset,
++};
++
++static int sft_rtc_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct sft_rtc *srtc;
++      struct rtc_time tm;
++      struct irq_desc *desc;
++      int ret;
++
++      srtc = devm_kzalloc(dev, sizeof(*srtc), GFP_KERNEL);
++      if (!srtc)
++              return -ENOMEM;
++
++      srtc->regs = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(srtc->regs))
++              return PTR_ERR(srtc->regs);
++
++      srtc->pclk = devm_clk_get(dev, "pclk");
++      if (IS_ERR(srtc->pclk)) {
++              ret = PTR_ERR(srtc->pclk);
++              dev_err(dev,
++                      "Failed to retrieve the peripheral clock, %d\n", ret);
++              return ret;
++      }
++
++      srtc->rst_array = devm_reset_control_array_get_exclusive(dev);
++      if (IS_ERR(srtc->rst_array)) {
++              ret = PTR_ERR(srtc->rst_array);
++              dev_err(dev,
++                      "Failed to retrieve the rtc reset, %d\n", ret);
++              return ret;
++      }
++
++      init_completion(&srtc->cal_done);
++      init_completion(&srtc->onesec_done);
++
++      ret = clk_prepare_enable(srtc->pclk);
++      if (ret) {
++              dev_err(dev,
++                      "Failed to enable the peripheral clock, %d\n", ret);
++              return ret;
++      }
++
++      ret = sft_rtc_get_cal_clk(dev, srtc);
++      if (ret)
++              goto err_disable_pclk;
++
++      ret = reset_control_deassert(srtc->rst_array);
++      if (ret) {
++              dev_err(dev,
++                      "Failed to deassert rtc resets, %d\n", ret);
++              goto err_disable_cal_clk;
++      }
++
++      ret = sft_rtc_get_irq(pdev, srtc);
++      if (ret)
++              goto err_disable_cal_clk;
++
++      srtc->rtc_dev = devm_rtc_allocate_device(dev);
++      if (IS_ERR(srtc->rtc_dev))
++              return PTR_ERR(srtc->rtc_dev);
++
++      platform_set_drvdata(pdev, srtc);
++
++      /* The RTC supports 01.01.2001 - 31.12.2099 */
++      srtc->rtc_dev->range_min = mktime64(2001,  1,  1,  0,  0,  0);
++      srtc->rtc_dev->range_max = mktime64(2099, 12, 31, 23, 59, 59);
++
++      srtc->rtc_dev->ops = &starfive_rtc_ops;
++      device_init_wakeup(dev, true);
++
++      desc = irq_to_desc(srtc->rtc_irq);
++      irq_desc_get_chip(desc)->flags = IRQCHIP_SKIP_SET_WAKE;
++
++      /* Always use 24-hour mode and keep the RTC values */
++      sft_rtc_set_mode(srtc, RTC_HOUR_MODE_24H);
++
++      sft_rtc_set_enabled(srtc, true);
++
++      if (device_property_read_bool(dev, "rtc,hw-adjustment"))
++              sft_rtc_hw_adjustment(dev, true);
++
++      /*
++       * If rtc time is out of supported range, reset it to the minimum time.
++       * notice that, actual year = 1900 + tm.tm_year
++       *              actual month = 1 + tm.tm_mon
++       */
++      sft_rtc_read_time(dev, &tm);
++      if (tm.tm_year < 101 || tm.tm_year > 199 || tm.tm_mon < 0 || tm.tm_mon > 11 ||
++          tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 ||
++          tm.tm_min < 0 || tm.tm_min > 59 || tm.tm_sec < 0 || tm.tm_sec > 59) {
++              rtc_time64_to_tm(srtc->rtc_dev->range_min, &tm);
++              sft_rtc_set_time(dev, &tm);
++      }
++
++      ret = devm_rtc_register_device(srtc->rtc_dev);
++      if (ret)
++              goto err_disable_wakeup;
++
++      return 0;
++
++err_disable_wakeup:
++      device_init_wakeup(dev, false);
++
++err_disable_cal_clk:
++      clk_disable_unprepare(srtc->cal_clk);
++
++err_disable_pclk:
++      clk_disable_unprepare(srtc->pclk);
++
++      return ret;
++}
++
++static int sft_rtc_remove(struct platform_device *pdev)
++{
++      struct sft_rtc *srtc = platform_get_drvdata(pdev);
++
++      sft_rtc_alarm_irq_enable(&pdev->dev, 0);
++      device_init_wakeup(&pdev->dev, 0);
++
++      clk_disable_unprepare(srtc->pclk);
++      clk_disable_unprepare(srtc->cal_clk);
++
++      return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int sft_rtc_suspend(struct device *dev)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++
++      if (device_may_wakeup(dev))
++              enable_irq_wake(srtc->rtc_irq);
++
++      return 0;
++}
++
++static int sft_rtc_resume(struct device *dev)
++{
++      struct sft_rtc *srtc = dev_get_drvdata(dev);
++
++      if (device_may_wakeup(dev))
++              disable_irq_wake(srtc->rtc_irq);
++
++      return 0;
++}
++#endif
++
++static SIMPLE_DEV_PM_OPS(sft_rtc_pm_ops, sft_rtc_suspend, sft_rtc_resume);
++
++static const struct of_device_id sft_rtc_of_match[] = {
++      { .compatible = "starfive,jh7110-rtc" },
++      { },
++};
++MODULE_DEVICE_TABLE(of, sft_rtc_of_match);
++
++static struct platform_driver starfive_rtc_driver = {
++      .driver = {
++              .name = "starfive-rtc",
++              .of_match_table = sft_rtc_of_match,
++              .pm   = &sft_rtc_pm_ops,
++      },
++      .probe = sft_rtc_probe,
++      .remove = sft_rtc_remove,
++};
++module_platform_driver(starfive_rtc_driver);
++
++MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>");
++MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
++MODULE_DESCRIPTION("StarFive RTC driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:starfive-rtc");
diff --git a/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch b/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch
new file mode 100644 (file)
index 0000000..c88008a
--- /dev/null
@@ -0,0 +1,96 @@
+From 552114b8cbbd956ad8466261b5f11b059eba82ca Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Sun, 25 Jun 2023 09:40:29 +0800
+Subject: [PATCH 066/116] uart: 8250: Add dw auto flow ctrl support
+
+Add designeware 8250 auto flow ctrl support. Enable
+it by add auto-flow-control in dts.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+---
+ drivers/tty/serial/8250/8250_core.c |  2 ++
+ drivers/tty/serial/8250/8250_dw.c   |  3 +++
+ drivers/tty/serial/8250/8250_port.c | 14 +++++++++++++-
+ include/linux/serial_8250.h         |  1 +
+ include/uapi/linux/serial_core.h    |  2 ++
+ 5 files changed, 21 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/8250/8250_core.c
++++ b/drivers/tty/serial/8250/8250_core.c
+@@ -1129,6 +1129,8 @@ int serial8250_register_8250_port(const
+                       uart->dl_read = up->dl_read;
+               if (up->dl_write)
+                       uart->dl_write = up->dl_write;
++              if (up->probe)
++                      uart->probe = up->probe;
+               if (uart->port.type != PORT_8250_CIR) {
+                       if (serial8250_isa_config != NULL)
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -612,6 +612,9 @@ static int dw8250_probe(struct platform_
+               data->msr_mask_off |= UART_MSR_TERI;
+       }
++      if (device_property_read_bool(dev, "auto-flow-control"))
++              up->probe |= UART_PROBE_AFE;
++
+       /* If there is separate baudclk, get the rate from it. */
+       data->clk = devm_clk_get_optional(dev, "baudclk");
+       if (data->clk == NULL)
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -330,6 +330,14 @@ static const struct serial8250_config ua
+               .rxtrig_bytes   = {1, 8, 16, 30},
+               .flags          = UART_CAP_FIFO | UART_CAP_AFE,
+       },
++      [PORT_16550A_AFE] = {
++              .name           = "16550A_AFE",
++              .fifo_size      = 16,
++              .tx_loadsz      = 16,
++              .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
++              .rxtrig_bytes   = {1, 4, 8, 14},
++              .flags          = UART_CAP_FIFO | UART_CAP_AFE,
++      },
+ };
+ /* Uart divisor latch read */
+@@ -1143,6 +1151,11 @@ static void autoconfig_16550a(struct uar
+               up->port.type = PORT_U6_16550A;
+               up->capabilities |= UART_CAP_AFE;
+       }
++
++      if ((up->port.type == PORT_16550A) && (up->probe & UART_PROBE_AFE)) {
++              up->port.type = PORT_16550A_AFE;
++              up->capabilities |= UART_CAP_AFE;
++      }
+ }
+ /*
+@@ -2813,7 +2826,6 @@ serial8250_do_set_termios(struct uart_po
+               if (termios->c_cflag & CRTSCTS)
+                       up->mcr |= UART_MCR_AFE;
+       }
+-
+       /*
+        * Update the per-port timeout.
+        */
+--- a/include/linux/serial_8250.h
++++ b/include/linux/serial_8250.h
+@@ -141,6 +141,7 @@ struct uart_8250_port {
+       unsigned char           probe;
+       struct mctrl_gpios      *gpios;
+ #define UART_PROBE_RSA        (1 << 0)
++#define UART_PROBE_AFE  (1 << 1)
+       /*
+        * Some bits in registers are cleared on a read, so they must
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -245,4 +245,6 @@
+ /* Sunplus UART */
+ #define PORT_SUNPLUS  123
++#define PORT_16550A_AFE       124
++
+ #endif /* _UAPILINUX_SERIAL_CORE_H */
diff --git a/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch b/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch
new file mode 100644 (file)
index 0000000..18d735b
--- /dev/null
@@ -0,0 +1,29 @@
+From 6edee93a89254f30c3387c88231e7ecec06ba84a Mon Sep 17 00:00:00 2001
+From: "shanlong.li" <shanlong.li@starfivetech.com>
+Date: Mon, 10 Jul 2023 03:07:57 -0700
+Subject: [PATCH 067/116] driver:uart: fix up uart communicate fail
+
+fix up uart communicate fail
+
+Signed-off-by: shanlong.li <shanlong.li@starfivetech.com>
+---
+ drivers/tty/serial/8250/8250_dw.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -652,10 +652,10 @@ static int dw8250_probe(struct platform_
+       if (err)
+               return err;
+-      data->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
+-      if (IS_ERR(data->rst))
+-              return PTR_ERR(data->rst);
+-
++      data->rst = devm_reset_control_array_get_exclusive(dev);
++      if (IS_ERR(data->rst)) {
++              err = PTR_ERR(data->rst);
++      }
+       reset_control_deassert(data->rst);
+       err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst);
diff --git a/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch b/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch
new file mode 100644 (file)
index 0000000..56efebd
--- /dev/null
@@ -0,0 +1,32 @@
+From 777d288f03a0b350f6c2d4367b01a80d9f25cd6e Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Wed, 20 Sep 2023 17:19:59 +0800
+Subject: [PATCH 068/116] uart: 8250: add reset operation in runtime PM
+
+add reset operation in runtime PM
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+---
+ drivers/tty/serial/8250/8250_dw.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -745,6 +745,8 @@ static int dw8250_runtime_suspend(struct
+ {
+       struct dw8250_data *data = dev_get_drvdata(dev);
++      reset_control_assert(data->rst);
++
+       clk_disable_unprepare(data->clk);
+       clk_disable_unprepare(data->pclk);
+@@ -760,6 +762,8 @@ static int dw8250_runtime_resume(struct
+       clk_prepare_enable(data->clk);
++      reset_control_deassert(data->rst);
++
+       return 0;
+ }
diff --git a/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch b/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch
new file mode 100644 (file)
index 0000000..eb46a96
--- /dev/null
@@ -0,0 +1,113 @@
+From 5eda2331a252436756fb40861f01a7a38b1502c7 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Thu, 15 Jun 2023 20:14:22 +0800
+Subject: [PATCH 069/116] dt-bindings: CAN: Add StarFive CAN module
+
+Add documentation to describe StarFive CAN engine.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+---
+ .../devicetree/bindings/net/can/ipms-can.yaml | 97 +++++++++++++++++++
+ 1 file changed, 97 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/can/ipms-can.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/can/ipms-can.yaml
+@@ -0,0 +1,97 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++#$id: http://devicetree.org/schemas/net/can/ipms-can.yaml#
++#$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IPMS CAN/CANFD controller Device Tree Bindings
++
++properties:
++  compatible:
++    const:ipms,can
++
++  reg:
++    maxItems: 1
++    items:
++      - description:CAN controller registers
++
++  interrupts:
++    maxItems: 1
++
++  clocks:
++    minItems: 1
++    items:
++      - description:apb_clk clock
++      - description:core_clk clock
++      - description:timer_clk clock
++
++  clock-names:
++    minItems: 1
++    items:
++      - const:apb_clk
++      - const:core_clk
++      - const:timer_clk
++  resets:
++   minItems: 1
++    items:
++      - description:apb_clk reset
++      - description:core_clk reset
++      - description:timer_clk reset
++  reset-names:
++    minItems: 1
++    items:
++      - const:rst_apb
++      - const:rst_core
++      - const:rst_timer
++  starfive,sys-syscon:
++    format:
++    starfive,sys-syscon = <&arg0 arg1 arg2 arg3>
++    description:
++      arg0:arg0 is sys_syscon.
++      arg1:arg1 is syscon register offset, used to enable can2.0/canfd function, can0 is 0x10, can1 is 0x88.
++      arg2:arg2 is used to enable the register shift of the can2.0/canfd function, can0 is 0x3, can1 is 0x12.
++      arg3:arg3 is used to enable the register mask of the can2.0/canfd function, can0 is 0x8, can1 is 0x40000
++
++  syscon,can_or_canfd:
++    description:
++    IPMS CAN-CTRL core is a serial communications controller that performs serial communication according to the CAN protocol.
++    This CAN bus interface uses the basic CAN principle and meets all constraints of the CAN-specification 2.0B active.
++    Furthermore this CAN core can be configured to meet the specification of CAN with flexible data rate CAN FD.
++    When syscon,can_or_canfd is set to 0, use CAN2.0B.
++    when syscon,can_or_canfd is set to 1, use CAN FD.
++required:
++  - compatible
++  - reg
++  - interrupts
++  - clocks
++  - clock-names
++  - resets
++  - reset-names
++  - starfive,sys-syscon
++  - syscon,can_or_canfd
++additionalProperties:false
++
++examples:
++  - |
++    can0: can@130d0000{
++    compatible = "ipms,can";
++    reg = <0x0 0x130d0000 0x0 0x1000>;
++    interrupts = <112>;
++    interrupt-parent = <&plic>;
++    clocks = <&clkgen JH7110_CAN0_CTRL_CLK_APB>,
++             <&clkgen JH7110_CAN0_CTRL_CLK_CAN>,
++             <&clkgen JH7110_CAN0_CTRL_CLK_TIMER>;
++    clock-names = "apb_clk",
++                  "core_clk",
++                  "timer_clk";
++    resets = <&rstgen RSTN_U0_CAN_CTRL_APB>,
++             <&rstgen RSTN_U0_CAN_CTRL_CORE>,
++             <&rstgen RSTN_U0_CAN_CTRL_TIMER>;
++    reset-names = "rst_apb",
++                  "rst_core",
++                  "rst_timer";
++    starfive,sys-syscon = <&sys_syscon, 0x10 0x3 0x8>;
++    syscon,can_or_canfd = <0>;
++    };
++
++...
diff --git a/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch b/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch
new file mode 100644 (file)
index 0000000..1bb41dd
--- /dev/null
@@ -0,0 +1,1317 @@
+From b1fbe15b87be654b1b280a76ec1470917d79f720 Mon Sep 17 00:00:00 2001
+From: William Qiu <william.qiu@starfivetech.com>
+Date: Thu, 15 Jun 2023 20:15:25 +0800
+Subject: [PATCH 070/116] CAN: starfive - Add CAN engine support
+
+Adding device probe StarFive CAN module.
+
+Signed-off-by: William Qiu <william.qiu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/net/can/Kconfig      |    5 +
+ drivers/net/can/Makefile     |    1 +
+ drivers/net/can/ipms_canfd.c | 1275 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 1281 insertions(+)
+ create mode 100644 drivers/net/can/ipms_canfd.c
+
+--- a/drivers/net/can/Kconfig
++++ b/drivers/net/can/Kconfig
+@@ -214,6 +214,11 @@ config CAN_XILINXCAN
+         Xilinx CAN driver. This driver supports both soft AXI CAN IP and
+         Zynq CANPS IP.
++config IPMS_CAN
++      tristate "IPMS CAN"
++      help
++        IPMS CANFD driver. This driver supports IPMS CANFD IP.
++
+ source "drivers/net/can/c_can/Kconfig"
+ source "drivers/net/can/cc770/Kconfig"
+ source "drivers/net/can/ctucanfd/Kconfig"
+--- a/drivers/net/can/Makefile
++++ b/drivers/net/can/Makefile
+@@ -31,5 +31,6 @@ obj-$(CONFIG_CAN_SJA1000)    += sja1000/
+ obj-$(CONFIG_CAN_SUN4I)               += sun4i_can.o
+ obj-$(CONFIG_CAN_TI_HECC)     += ti_hecc.o
+ obj-$(CONFIG_CAN_XILINXCAN)   += xilinx_can.o
++obj-$(CONFIG_IPMS_CAN)                += ipms_canfd.o
+ subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
+--- /dev/null
++++ b/drivers/net/can/ipms_canfd.c
+@@ -0,0 +1,1275 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * StarFive Controller Area Network Host Controller Driver
++ *
++ * Copyright (c) 2022 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/can/dev.h>
++#include <linux/can/error.h>
++#include <linux/pm_runtime.h>
++#include <linux/of_device.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regmap.h>
++
++#define DRIVER_NAME "ipms_canfd"
++
++/* CAN registers set */
++enum canfd_device_reg {
++      CANFD_RUBF_OFFSET           =   0x00,   /* Receive Buffer Registers 0x00-0x4f */
++      CANFD_RUBF_ID_OFFSET        =   0x00,
++      CANFD_RBUF_CTL_OFFSET       =   0x04,
++      CANFD_RBUF_DATA_OFFSET      =   0x08,
++      CANFD_TBUF_OFFSET           =   0x50,   /* Transmit Buffer Registers 0x50-0x97 */
++      CANFD_TBUF_ID_OFFSET        =   0x50,
++      CANFD_TBUF_CTL_OFFSET       =   0x54,
++      CANFD_TBUF_DATA_OFFSET      =   0x58,
++      CANFD_TTS_OFFSET            =   0x98,   /* Transmission Time Stamp 0x98-0x9f */
++      CANFD_CFG_STAT_OFFSET       =   0xa0,
++      CANFD_TCMD_OFFSET           =   0xa1,
++      CANFD_TCTRL_OFFSET          =   0xa2,
++      CANFD_RCTRL_OFFSET          =   0xa3,
++      CANFD_RTIE_OFFSET           =   0xa4,
++      CANFD_RTIF_OFFSET           =   0xa5,
++      CANFD_ERRINT_OFFSET         =   0xa6,
++      CANFD_LIMIT_OFFSET          =   0xa7,
++      CANFD_S_SEG_1_OFFSET        =   0xa8,
++      CANFD_S_SEG_2_OFFSET        =   0xa9,
++      CANFD_S_SJW_OFFSET          =   0xaa,
++      CANFD_S_PRESC_OFFSET        =   0xab,
++      CANFD_F_SEG_1_OFFSET        =   0xac,
++      CANFD_F_SEG_2_OFFSET        =   0xad,
++      CANFD_F_SJW_OFFSET          =   0xae,
++      CANFD_F_PRESC_OFFSET        =   0xaf,
++      CANFD_EALCAP_OFFSET         =   0xb0,
++      CANFD_RECNT_OFFSET          =   0xb2,
++      CANFD_TECNT_OFFSET          =   0xb3,
++};
++
++enum canfd_reg_bitchange {
++      CAN_FD_SET_RST_MASK         =   0x80,   /* Set Reset Bit */
++      CAN_FD_OFF_RST_MASK         =   0x7f,   /* Reset Off Bit */
++      CAN_FD_SET_FULLCAN_MASK     =   0x10,   /* set TTTBM as 1->full TTCAN mode */
++      CAN_FD_OFF_FULLCAN_MASK     =   0xef,   /* set TTTBM as 0->separate PTB and STB mode */
++      CAN_FD_SET_FIFO_MASK        =   0x20,   /* set TSMODE as 1->FIFO mode */
++      CAN_FD_OFF_FIFO_MASK        =   0xdf,   /* set TSMODE as 0->Priority mode */
++      CAN_FD_SET_TSONE_MASK       =   0x04,
++      CAN_FD_OFF_TSONE_MASK       =   0xfb,
++      CAN_FD_SET_TSALL_MASK       =   0x02,
++      CAN_FD_OFF_TSALL_MASK       =   0xfd,
++      CAN_FD_LBMEMOD_MASK         =   0x40,   /* set loop back mode, external */
++      CAN_FD_LBMIMOD_MASK         =   0x20,   /* set loopback internal mode */
++      CAN_FD_SET_BUSOFF_MASK      =   0x01,
++      CAN_FD_OFF_BUSOFF_MASK      =   0xfe,
++      CAN_FD_SET_TTSEN_MASK       =   0x80,   /* set ttsen, tts update enable */
++      CAN_FD_SET_BRS_MASK         =   0x10,   /* can fd Bit Rate Switch mask */
++      CAN_FD_OFF_BRS_MASK         =   0xef,
++      CAN_FD_SET_EDL_MASK         =   0x20,   /* Extended Data Length */
++      CAN_FD_OFF_EDL_MASK         =   0xdf,
++      CAN_FD_SET_DLC_MASK         =   0x0f,
++      CAN_FD_SET_TENEXT_MASK      =   0x40,
++      CAN_FD_SET_IDE_MASK         =   0x80,
++      CAN_FD_OFF_IDE_MASK         =   0x7f,
++      CAN_FD_SET_RTR_MASK         =   0x40,
++      CAN_FD_OFF_RTR_MASK         =   0xbf,
++      CAN_FD_INTR_ALL_MASK        =   0xff,   /* all interrupts enable mask */
++      CAN_FD_SET_RIE_MASK         =   0x80,
++      CAN_FD_OFF_RIE_MASK         =   0x7f,
++      CAN_FD_SET_RFIE_MASK        =   0x20,
++      CAN_FD_OFF_RFIE_MASK        =   0xdf,
++      CAN_FD_SET_RAFIE_MASK       =   0x10,
++      CAN_FD_OFF_RAFIE_MASK       =   0xef,
++      CAN_FD_SET_EIE_MASK         =   0x02,
++      CAN_FD_OFF_EIE_MASK         =   0xfd,
++      CAN_FD_TASCTIVE_MASK        =   0x02,
++      CAN_FD_RASCTIVE_MASK        =   0x04,
++      CAN_FD_SET_TBSEL_MASK       =   0x80,   /* message writen in STB */
++      CAN_FD_OFF_TBSEL_MASK       =   0x7f,   /* message writen in PTB */
++      CAN_FD_SET_STBY_MASK        =   0x20,
++      CAN_FD_OFF_STBY_MASK        =   0xdf,
++      CAN_FD_SET_TPE_MASK         =   0x10,   /* Transmit primary enable */
++      CAN_FD_SET_TPA_MASK         =   0x08,
++      CAN_FD_SET_SACK_MASK        =   0x80,
++      CAN_FD_SET_RREL_MASK        =   0x10,
++      CAN_FD_RSTAT_NOT_EMPTY_MASK =   0x03,
++      CAN_FD_SET_RIF_MASK         =   0x80,
++      CAN_FD_OFF_RIF_MASK         =   0x7f,
++      CAN_FD_SET_RAFIF_MASK       =   0x10,
++      CAN_FD_SET_RFIF_MASK        =   0x20,
++      CAN_FD_SET_TPIF_MASK        =   0x08,   /* Transmission Primary Interrupt Flag */
++      CAN_FD_SET_TSIF_MASK        =   0x04,
++      CAN_FD_SET_EIF_MASK         =   0x02,
++      CAN_FD_SET_AIF_MASK         =   0x01,
++      CAN_FD_SET_EWARN_MASK       =   0x80,
++      CAN_FD_SET_EPASS_MASK       =   0x40,
++      CAN_FD_SET_EPIE_MASK        =   0x20,
++      CAN_FD_SET_EPIF_MASK        =   0x10,
++      CAN_FD_SET_ALIE_MASK        =   0x08,
++      CAN_FD_SET_ALIF_MASK        =   0x04,
++      CAN_FD_SET_BEIE_MASK        =   0x02,
++      CAN_FD_SET_BEIF_MASK        =   0x01,
++      CAN_FD_OFF_EPIE_MASK        =   0xdf,
++      CAN_FD_OFF_BEIE_MASK        =   0xfd,
++      CAN_FD_SET_AFWL_MASK        =   0x40,
++      CAN_FD_SET_EWL_MASK         =   0x0b,
++      CAN_FD_SET_KOER_MASK        =   0xe0,
++      CAN_FD_SET_BIT_ERROR_MASK   =   0x20,
++      CAN_FD_SET_FORM_ERROR_MASK  =   0x40,
++      CAN_FD_SET_STUFF_ERROR_MASK =   0x60,
++      CAN_FD_SET_ACK_ERROR_MASK   =   0x80,
++      CAN_FD_SET_CRC_ERROR_MASK   =   0xa0,
++      CAN_FD_SET_OTH_ERROR_MASK   =   0xc0,
++};
++
++/* seg1,seg2,sjw,prescaler all have 8 bits */
++#define BITS_OF_BITTIMING_REG         8
++
++/* in can_bittiming strucure every field has 32 bits---->u32 */
++#define FBITS_IN_BITTIMING_STR                32
++#define SEG_1_SHIFT                   0
++#define SEG_2_SHIFT                   8
++#define SJW_SHIFT                     16
++#define PRESC_SHIFT                   24
++
++/* TTSEN bit used for 32 bit register read or write */
++#define TTSEN_8_32_SHIFT              24
++#define RTR_32_8_SHIFT                        24
++
++/* transmit mode */
++#define XMIT_FULL                     0
++#define XMIT_SEP_FIFO                 1
++#define XMIT_SEP_PRIO                 2
++#define XMIT_PTB_MODE                 3
++
++enum  IPMS_CAN_TYPE {
++      IPMS_CAN_TYPY_CAN       = 0,
++      IPMS_CAN_TYPE_CANFD,
++};
++
++struct ipms_canfd_priv {
++      struct can_priv can;
++      struct napi_struct napi;
++      struct device *dev;
++      struct regmap *reg_syscon;
++      void __iomem *reg_base;
++      u32 (*read_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg);
++      void (*write_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg, u32 val);
++      struct clk *can_clk;
++      u32 tx_mode;
++      struct reset_control *resets;
++      struct clk_bulk_data *clks;
++      int nr_clks;
++      u32 can_or_canfd;
++};
++
++static struct can_bittiming_const canfd_bittiming_const = {
++      .name = DRIVER_NAME,
++      .tseg1_min = 2,
++      .tseg1_max = 16,
++      .tseg2_min = 2,
++      .tseg2_max = 8,
++      .sjw_max = 4,
++      .brp_min = 1,
++      .brp_max = 512,
++      .brp_inc = 1,
++
++};
++
++static struct can_bittiming_const canfd_data_bittiming_const = {
++      .name = DRIVER_NAME,
++      .tseg1_min = 1,
++      .tseg1_max = 16,
++      .tseg2_min = 2,
++      .tseg2_max = 8,
++      .sjw_max = 8,
++      .brp_min = 1,
++      .brp_max = 512,
++      .brp_inc = 1,
++};
++
++static void canfd_write_reg_le(const struct ipms_canfd_priv *priv,
++                              enum canfd_device_reg reg, u32 val)
++{
++      iowrite32(val, priv->reg_base + reg);
++}
++
++static u32 canfd_read_reg_le(const struct ipms_canfd_priv *priv,
++                              enum canfd_device_reg reg)
++{
++      return ioread32(priv->reg_base + reg);
++}
++
++static inline unsigned char can_ioread8(const void  *addr)
++{
++      void  *addr_down;
++      union val {
++              u8 val_8[4];
++              u32 val_32;
++      } val;
++      u32 offset = 0;
++
++      addr_down = (void  *)ALIGN_DOWN((unsigned long)addr, 4);
++      offset = addr - addr_down;
++      val.val_32 = ioread32(addr_down);
++      return val.val_8[offset];
++}
++
++static inline void can_iowrite8(unsigned char value, void  *addr)
++{
++      void  *addr_down;
++      union val {
++              u8 val_8[4];
++              u32 val_32;
++      } val;
++      u8 offset = 0;
++
++      addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4);
++      offset = addr - addr_down;
++      val.val_32 = ioread32(addr_down);
++      val.val_8[offset] = value;
++      iowrite32(val.val_32, addr_down);
++}
++
++static void canfd_reigister_set_bit(const struct ipms_canfd_priv *priv,
++                                      enum canfd_device_reg reg,
++                                      enum canfd_reg_bitchange set_mask)
++{
++      void  *addr_down;
++      union val {
++              u8 val_8[4];
++              u32 val_32;
++      } val;
++      u8 offset = 0;
++
++      addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
++      offset = (priv->reg_base + reg) - addr_down;
++      val.val_32 = ioread32(addr_down);
++      val.val_8[offset] |= set_mask;
++      iowrite32(val.val_32, addr_down);
++}
++
++static void canfd_reigister_off_bit(const struct ipms_canfd_priv *priv,
++                                      enum canfd_device_reg reg,
++                                      enum canfd_reg_bitchange set_mask)
++{
++      void  *addr_down;
++      union val {
++              u8 val_8[4];
++              u32 val_32;
++      } val;
++      u8 offset = 0;
++
++      addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4);
++      offset = (priv->reg_base + reg) - addr_down;
++      val.val_32 = ioread32(addr_down);
++      val.val_8[offset] &= set_mask;
++      iowrite32(val.val_32, addr_down);
++}
++
++static int canfd_device_driver_bittime_configuration(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      struct can_bittiming *bt = &priv->can.bittiming;
++      struct can_bittiming *dbt = &priv->can.data_bittiming;
++      u32 reset_test, bittiming_temp, dat_bittiming;
++
++      reset_test = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++      if (!(reset_test & CAN_FD_SET_RST_MASK)) {
++              netdev_alert(ndev, "Not in reset mode, cannot set bit timing\n");
++              return -EPERM;
++      }
++
++      bittiming_temp = ((bt->phase_seg1 + bt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
++                       ((bt->phase_seg2 - 1) << SEG_2_SHIFT) |
++                       ((bt->sjw - 1) << SJW_SHIFT) |
++                       ((bt->brp - 1) << PRESC_SHIFT);
++
++      /* Check the bittime parameter */
++      if ((((int)(bt->phase_seg1 + bt->prop_seg + 1) - 2) < 0) ||
++              (((int)(bt->phase_seg2) - 1) < 0) ||
++              (((int)(bt->sjw) - 1) < 0) ||
++              (((int)(bt->brp) - 1) < 0))
++              return -EINVAL;
++
++      priv->write_reg(priv, CANFD_S_SEG_1_OFFSET, bittiming_temp);
++
++      if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) {
++              dat_bittiming = ((dbt->phase_seg1 + dbt->prop_seg + 1 - 2) << SEG_1_SHIFT) |
++                              ((dbt->phase_seg2 - 1) << SEG_2_SHIFT) |
++                              ((dbt->sjw - 1) << SJW_SHIFT) |
++                              ((dbt->brp - 1) << PRESC_SHIFT);
++
++              if ((((int)(dbt->phase_seg1 + dbt->prop_seg + 1) - 2) < 0) ||
++                      (((int)(dbt->phase_seg2) - 1) < 0) ||
++                      (((int)(dbt->sjw) - 1) < 0) ||
++                      (((int)(dbt->brp) - 1) < 0))
++                      return -EINVAL;
++
++              priv->write_reg(priv, CANFD_F_SEG_1_OFFSET, dat_bittiming);
++      }
++
++      canfd_reigister_off_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_OFF_RST_MASK);
++
++      netdev_dbg(ndev, "Slow bit rate: %08x\n", priv->read_reg(priv, CANFD_S_SEG_1_OFFSET));
++      netdev_dbg(ndev, "Fast bit rate: %08x\n", priv->read_reg(priv, CANFD_F_SEG_1_OFFSET));
++
++      return 0;
++}
++
++int canfd_get_freebuffer(struct ipms_canfd_priv *priv)
++{
++      /* Get next transmit buffer */
++      canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_TENEXT_MASK);
++
++      if (can_ioread8(priv->reg_base + CANFD_TCTRL_OFFSET) & CAN_FD_SET_TENEXT_MASK)
++              return -1;
++
++      return 0;
++}
++
++static void canfd_tx_interrupt(struct net_device *ndev, u8 isr)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++      /* wait till transmission of the PTB or STB finished */
++      while (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) {
++              if (isr & CAN_FD_SET_TPIF_MASK)
++                      canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TPIF_MASK);
++
++              if (isr & CAN_FD_SET_TSIF_MASK)
++                      canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TSIF_MASK);
++
++              isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET);
++      }
++      netif_wake_queue(ndev);
++}
++
++static int can_rx(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      struct net_device_stats *stats = &ndev->stats;
++      struct can_frame *cf;
++      struct sk_buff *skb;
++      u32 can_id;
++      u8  dlc, control, rx_status;
++
++      rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++
++      if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK))
++              return 0;
++      control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++      can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET);
++      dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK;
++
++      skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
++      if (!skb) {
++              stats->rx_dropped++;
++              return 0;
++      }
++      cf->can_dlc = can_cc_dlc2len(dlc);
++
++      /* change the CANFD id into socketcan id format */
++      if (control & CAN_FD_SET_IDE_MASK) {
++              cf->can_id = can_id;
++              cf->can_id |= CAN_EFF_FLAG;
++      } else {
++              cf->can_id = can_id;
++              cf->can_id &= (~CAN_EFF_FLAG);
++      }
++
++      if (control & CAN_FD_SET_RTR_MASK)
++              cf->can_id |= CAN_RTR_FLAG;
++
++      if (!(control & CAN_FD_SET_RTR_MASK)) {
++              *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET);
++              *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4);
++      }
++
++      canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK);
++      stats->rx_bytes += can_fd_dlc2len(cf->can_dlc);
++      stats->rx_packets++;
++      netif_receive_skb(skb);
++
++      return 1;
++}
++
++static int canfd_rx(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      struct net_device_stats *stats = &ndev->stats;
++      struct canfd_frame *cf;
++      struct sk_buff *skb;
++      u32 can_id;
++      u8  dlc, control, rx_status;
++      int i;
++
++      rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++
++      if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK))
++              return 0;
++      control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++      can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET);
++      dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK;
++
++      if (control & CAN_FD_SET_EDL_MASK)
++              /* allocate sk_buffer for canfd frame */
++              skb = alloc_canfd_skb(ndev, &cf);
++      else
++              /* allocate sk_buffer for can frame */
++              skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
++
++      if (!skb) {
++              stats->rx_dropped++;
++              return 0;
++      }
++
++      /* change the CANFD or CAN2.0 data into socketcan data format */
++      if (control & CAN_FD_SET_EDL_MASK)
++              cf->len = can_fd_dlc2len(dlc);
++      else
++              cf->len = can_cc_dlc2len(dlc);
++
++      /* change the CANFD id into socketcan id format */
++      if (control & CAN_FD_SET_EDL_MASK) {
++              cf->can_id = can_id;
++              if (control & CAN_FD_SET_IDE_MASK)
++                      cf->can_id |= CAN_EFF_FLAG;
++              else
++                      cf->can_id &= (~CAN_EFF_FLAG);
++      } else {
++              cf->can_id = can_id;
++              if (control & CAN_FD_SET_IDE_MASK)
++                      cf->can_id |= CAN_EFF_FLAG;
++              else
++                      cf->can_id &= (~CAN_EFF_FLAG);
++
++              if (control & CAN_FD_SET_RTR_MASK)
++                      cf->can_id |= CAN_RTR_FLAG;
++      }
++
++      /* CANFD frames handed over to SKB */
++      if (control & CAN_FD_SET_EDL_MASK) {
++              for (i = 0; i < cf->len; i += 4)
++                      *((u32 *)(cf->data + i)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + i);
++      } else {
++              /* skb reads the received datas, if the RTR bit not set */
++              if (!(control & CAN_FD_SET_RTR_MASK)) {
++                      *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET);
++                      *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4);
++              }
++      }
++
++      canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK);
++
++      stats->rx_bytes += cf->len;
++      stats->rx_packets++;
++      netif_receive_skb(skb);
++
++      return 1;
++}
++
++static int canfd_rx_poll(struct napi_struct *napi, int quota)
++{
++      struct net_device *ndev = napi->dev;
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      int work_done = 0;
++      u8 rx_status = 0, control = 0;
++
++      control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++      rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++
++      /* clear receive interrupt and deal with all the received frames */
++      while ((rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK) && (work_done < quota)) {
++              (control & CAN_FD_SET_EDL_MASK) ? (work_done += canfd_rx(ndev)) : (work_done += can_rx(ndev));
++
++              control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET);
++              rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET);
++      }
++      napi_complete(napi);
++      canfd_reigister_set_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_SET_RIE_MASK);
++      return work_done;
++}
++
++static void canfd_rxfull_interrupt(struct net_device *ndev, u8 isr)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++      if (isr & CAN_FD_SET_RAFIF_MASK)
++              canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RAFIF_MASK);
++
++      if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK))
++              canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET,
++                                      (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK));
++}
++
++static int set_canfd_xmit_mode(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++      switch (priv->tx_mode) {
++      case XMIT_FULL:
++              canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FULLCAN_MASK);
++              break;
++      case XMIT_SEP_FIFO:
++              canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK);
++              canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FIFO_MASK);
++              canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK);
++              break;
++      case XMIT_SEP_PRIO:
++              canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK);
++              canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FIFO_MASK);
++              canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK);
++              break;
++      case XMIT_PTB_MODE:
++              canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_TBSEL_MASK);
++              break;
++      default:
++              break;
++      }
++      return 0;
++}
++
++static netdev_tx_t canfd_driver_start_xmit(struct sk_buff *skb, struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      struct canfd_frame *cf = (struct canfd_frame *)skb->data;
++      struct net_device_stats *stats = &ndev->stats;
++      u32 ttsen, id, ctl, addr_off;
++      int i;
++
++      priv->tx_mode = XMIT_PTB_MODE;
++
++      if (can_dropped_invalid_skb(ndev, skb))
++              return NETDEV_TX_OK;
++
++      switch (priv->tx_mode) {
++      case XMIT_FULL:
++              return NETDEV_TX_BUSY;
++      case XMIT_PTB_MODE:
++              set_canfd_xmit_mode(ndev);
++              canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_STBY_MASK);
++
++              if (cf->can_id & CAN_EFF_FLAG) {
++                      id = (cf->can_id & CAN_EFF_MASK);
++                      ttsen = 0 << TTSEN_8_32_SHIFT;
++                      id |= ttsen;
++              } else {
++                      id = (cf->can_id & CAN_SFF_MASK);
++                      ttsen = 0 << TTSEN_8_32_SHIFT;
++                      id |= ttsen;
++              }
++
++              ctl = can_fd_len2dlc(cf->len);
++
++              /* transmit can fd frame */
++              if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) {
++                      if (can_is_canfd_skb(skb)) {
++                              if (cf->can_id & CAN_EFF_FLAG)
++                                      ctl |= CAN_FD_SET_IDE_MASK;
++                              else
++                                      ctl &= CAN_FD_OFF_IDE_MASK;
++
++                              if (cf->flags & CANFD_BRS)
++                                      ctl |= CAN_FD_SET_BRS_MASK;
++
++                              ctl |= CAN_FD_SET_EDL_MASK;
++
++                              addr_off = CANFD_TBUF_DATA_OFFSET;
++
++                              for (i = 0; i < cf->len; i += 4) {
++                                      priv->write_reg(priv, addr_off,
++                                                      *((u32 *)(cf->data + i)));
++                                      addr_off += 4;
++                              }
++                      } else {
++                              ctl &= CAN_FD_OFF_EDL_MASK;
++                              ctl &= CAN_FD_OFF_BRS_MASK;
++
++                              if (cf->can_id & CAN_EFF_FLAG)
++                                      ctl |= CAN_FD_SET_IDE_MASK;
++                              else
++                                      ctl &= CAN_FD_OFF_IDE_MASK;
++
++                              if (cf->can_id & CAN_RTR_FLAG) {
++                                      ctl |= CAN_FD_SET_RTR_MASK;
++                                      priv->write_reg(priv,
++                                              CANFD_TBUF_ID_OFFSET, id);
++                                      priv->write_reg(priv,
++                                              CANFD_TBUF_CTL_OFFSET, ctl);
++                              } else {
++                                      ctl &= CAN_FD_OFF_RTR_MASK;
++                                      addr_off = CANFD_TBUF_DATA_OFFSET;
++                                      priv->write_reg(priv, addr_off,
++                                                      *((u32 *)(cf->data + 0)));
++                                      priv->write_reg(priv, addr_off + 4,
++                                                      *((u32 *)(cf->data + 4)));
++                              }
++                      }
++                      priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id);
++                      priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl);
++                      addr_off = CANFD_TBUF_DATA_OFFSET;
++              } else {
++                      ctl &= CAN_FD_OFF_EDL_MASK;
++                      ctl &= CAN_FD_OFF_BRS_MASK;
++
++                      if (cf->can_id & CAN_EFF_FLAG)
++                              ctl |= CAN_FD_SET_IDE_MASK;
++                      else
++                              ctl &= CAN_FD_OFF_IDE_MASK;
++
++                      if (cf->can_id & CAN_RTR_FLAG) {
++                              ctl |= CAN_FD_SET_RTR_MASK;
++                              priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id);
++                              priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl);
++                      } else {
++                              ctl &= CAN_FD_OFF_RTR_MASK;
++                              priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id);
++                              priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl);
++                              addr_off = CANFD_TBUF_DATA_OFFSET;
++                              priv->write_reg(priv, addr_off,
++                                              *((u32 *)(cf->data + 0)));
++                              priv->write_reg(priv, addr_off + 4,
++                                              *((u32 *)(cf->data + 4)));
++                      }
++              }
++              canfd_reigister_set_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TPE_MASK);
++              stats->tx_bytes += cf->len;
++              break;
++      default:
++              break;
++      }
++
++      /*Due to cache blocking, we need call dev_kfree_skb() here to free the socket
++      buffer and return NETDEV_TX_OK */
++      dev_kfree_skb(skb);
++
++      return NETDEV_TX_OK;
++}
++
++static int set_reset_mode(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      u8 ret;
++
++      ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++      ret |= CAN_FD_SET_RST_MASK;
++      can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++      return 0;
++}
++
++static void canfd_driver_stop(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      int ret;
++
++      ret = set_reset_mode(ndev);
++      if (ret)
++              netdev_err(ndev, "Mode Resetting Failed!\n");
++
++      priv->can.state = CAN_STATE_STOPPED;
++}
++
++static int canfd_driver_close(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++      netif_stop_queue(ndev);
++      napi_disable(&priv->napi);
++      canfd_driver_stop(ndev);
++
++      free_irq(ndev->irq, ndev);
++      close_candev(ndev);
++
++      pm_runtime_put(priv->dev);
++
++      return 0;
++}
++
++static enum can_state get_of_chip_status(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      u8 can_stat, eir;
++
++      can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++      eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET);
++
++      if (can_stat & CAN_FD_SET_BUSOFF_MASK)
++              return CAN_STATE_BUS_OFF;
++
++      if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK))
++              return CAN_STATE_ERROR_PASSIVE;
++
++      if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK))
++              return CAN_STATE_ERROR_WARNING;
++
++      if (~(eir & CAN_FD_SET_EPASS_MASK))
++              return CAN_STATE_ERROR_ACTIVE;
++
++      return CAN_STATE_ERROR_ACTIVE;
++}
++
++static void canfd_error_interrupt(struct net_device *ndev, u8 isr, u8 eir)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      struct net_device_stats *stats = &ndev->stats;
++      struct can_frame *cf;
++      struct sk_buff *skb;
++      u8 koer, recnt = 0, tecnt = 0, can_stat = 0;
++
++      skb = alloc_can_err_skb(ndev, &cf);
++
++      koer = can_ioread8(priv->reg_base + CANFD_EALCAP_OFFSET) & CAN_FD_SET_KOER_MASK;
++      recnt = can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET);
++      tecnt = can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET);
++
++      /*Read can status*/
++      can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++      /* Bus off --->active error mode */
++      if ((isr & CAN_FD_SET_EIF_MASK) && priv->can.state == CAN_STATE_BUS_OFF)
++              priv->can.state = get_of_chip_status(ndev);
++
++      /* State selection */
++      if (can_stat & CAN_FD_SET_BUSOFF_MASK) {
++              priv->can.state = get_of_chip_status(ndev);
++              priv->can.can_stats.bus_off++;
++              canfd_reigister_set_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_SET_BUSOFF_MASK);
++              can_bus_off(ndev);
++              if (skb)
++                      cf->can_id |= CAN_ERR_BUSOFF;
++
++      } else if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK)) {
++              priv->can.state = get_of_chip_status(ndev);
++              priv->can.can_stats.error_passive++;
++              if (skb) {
++                      cf->can_id |= CAN_ERR_CRTL;
++                      cf->data[1] |= (recnt > 127) ? CAN_ERR_CRTL_RX_PASSIVE : 0;
++                      cf->data[1] |= (tecnt > 127) ? CAN_ERR_CRTL_TX_PASSIVE : 0;
++                      cf->data[6] = tecnt;
++                      cf->data[7] = recnt;
++              }
++      } else if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK)) {
++              priv->can.state = get_of_chip_status(ndev);
++              priv->can.can_stats.error_warning++;
++              if (skb) {
++                      cf->can_id |= CAN_ERR_CRTL;
++                      cf->data[1] |= (recnt > 95) ? CAN_ERR_CRTL_RX_WARNING : 0;
++                      cf->data[1] |= (tecnt > 95) ? CAN_ERR_CRTL_TX_WARNING : 0;
++                      cf->data[6] = tecnt;
++                      cf->data[7] = recnt;
++              }
++      }
++
++      /* Check for in protocol defined error interrupt */
++      if (eir & CAN_FD_SET_BEIF_MASK) {
++              if (skb)
++                      cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
++
++              /* bit error interrupt */
++              if (koer == CAN_FD_SET_BIT_ERROR_MASK) {
++                      stats->tx_errors++;
++                      if (skb) {
++                              cf->can_id |= CAN_ERR_PROT;
++                              cf->data[2] = CAN_ERR_PROT_BIT;
++                      }
++              }
++              /* format error interrupt */
++              if (koer == CAN_FD_SET_FORM_ERROR_MASK) {
++                      stats->rx_errors++;
++                      if (skb) {
++                              cf->can_id |= CAN_ERR_PROT;
++                              cf->data[2] = CAN_ERR_PROT_FORM;
++                      }
++              }
++              /* stuffing error interrupt */
++              if (koer == CAN_FD_SET_STUFF_ERROR_MASK) {
++                      stats->rx_errors++;
++                      if (skb) {
++                              cf->can_id |= CAN_ERR_PROT;
++                              cf->data[3] = CAN_ERR_PROT_STUFF;
++                      }
++              }
++              /* ack error interrupt */
++              if (koer == CAN_FD_SET_ACK_ERROR_MASK) {
++                      stats->tx_errors++;
++                      if (skb) {
++                              cf->can_id |= CAN_ERR_PROT;
++                              cf->data[2] = CAN_ERR_PROT_LOC_ACK;
++                      }
++              }
++              /* crc error interrupt */
++              if (koer == CAN_FD_SET_CRC_ERROR_MASK) {
++                      stats->rx_errors++;
++                      if (skb) {
++                              cf->can_id |= CAN_ERR_PROT;
++                              cf->data[2] = CAN_ERR_PROT_LOC_CRC_SEQ;
++                      }
++              }
++              priv->can.can_stats.bus_error++;
++      }
++      if (skb) {
++              stats->rx_packets++;
++              stats->rx_bytes += cf->can_dlc;
++              netif_rx(skb);
++      }
++
++      netdev_dbg(ndev, "Recnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET));
++      netdev_dbg(ndev, "Tecnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET));
++}
++
++static irqreturn_t canfd_interrupt(int irq, void *dev_id)
++{
++      struct net_device *ndev = (struct net_device *)dev_id;
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      u8 isr, eir;
++      u8 isr_handled = 0, eir_handled = 0;
++
++      /* read the value of interrupt status register */
++      isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET);
++
++      /* read the value of error interrupt register */
++      eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET);
++
++      /* Check for Tx interrupt and Processing it */
++      if (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) {
++              canfd_tx_interrupt(ndev, isr);
++              isr_handled |= (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK);
++      }
++      if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)) {
++              canfd_rxfull_interrupt(ndev, isr);
++              isr_handled |= (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK);
++      }
++      /* Check Rx interrupt and Processing the receive interrupt routine */
++      if (isr & CAN_FD_SET_RIF_MASK) {
++              canfd_reigister_off_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_OFF_RIE_MASK);
++              canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RIF_MASK);
++
++              napi_schedule(&priv->napi);
++              isr_handled |= CAN_FD_SET_RIF_MASK;
++      }
++      if ((isr & CAN_FD_SET_EIF_MASK) | (eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK))) {
++              /* reset EPIF and BEIF. Reset EIF */
++              canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET,
++                                      eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK));
++              canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET,
++                                      isr & CAN_FD_SET_EIF_MASK);
++
++              canfd_error_interrupt(ndev, isr, eir);
++
++              isr_handled |= CAN_FD_SET_EIF_MASK;
++              eir_handled |= (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK);
++      }
++      if ((isr_handled == 0) && (eir_handled == 0)) {
++              netdev_err(ndev, "Unhandled interrupt!\n");
++              return IRQ_NONE;
++      }
++
++      return IRQ_HANDLED;
++}
++
++static int canfd_chip_start(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      int err;
++      u8 ret;
++
++      err = set_reset_mode(ndev);
++      if (err) {
++              netdev_err(ndev, "Mode Resetting Failed!\n");
++              return err;
++      }
++
++      err = canfd_device_driver_bittime_configuration(ndev);
++      if (err) {
++              netdev_err(ndev, "Bittime Setting Failed!\n");
++              return err;
++      }
++
++      /* Set Almost Full Warning Limit */
++      canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_AFWL_MASK);
++
++      /* Programmable Error Warning Limit = (EWL+1)*8. Set EWL=11->Error Warning=96 */
++      canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_EWL_MASK);
++
++      /* Interrupts enable */
++      can_iowrite8(CAN_FD_INTR_ALL_MASK, priv->reg_base + CANFD_RTIE_OFFSET);
++
++      /* Error Interrupts enable(Error Passive and Bus Error) */
++      canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET, CAN_FD_SET_EPIE_MASK);
++
++      ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++      /* Check whether it is loopback mode or normal mode */
++      if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
++              ret |= CAN_FD_LBMIMOD_MASK;
++      } else {
++              ret &= ~CAN_FD_LBMEMOD_MASK;
++              ret &= ~CAN_FD_LBMIMOD_MASK;
++      }
++
++      can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET);
++
++      priv->can.state = CAN_STATE_ERROR_ACTIVE;
++
++      return 0;
++}
++
++static int  canfd_do_set_mode(struct net_device *ndev, enum can_mode mode)
++{
++      int ret;
++
++      switch (mode) {
++      case CAN_MODE_START:
++              ret = canfd_chip_start(ndev);
++              if (ret) {
++                      netdev_err(ndev, "Could Not Start CAN device !!\n");
++                      return ret;
++              }
++              netif_wake_queue(ndev);
++              break;
++      default:
++              ret = -EOPNOTSUPP;
++              break;
++      }
++
++      return ret;
++}
++
++static int canfd_driver_open(struct net_device *ndev)
++{
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      int ret;
++
++      ret = pm_runtime_get_sync(priv->dev);
++      if (ret < 0) {
++              netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
++                         __func__, ret);
++              goto err;
++      }
++
++      /* Set chip into reset mode */
++      ret = set_reset_mode(ndev);
++      if (ret) {
++              netdev_err(ndev, "Mode Resetting Failed!\n");
++              return ret;
++      }
++
++      /* Common open */
++      ret = open_candev(ndev);
++      if (ret)
++              return ret;
++
++      /* Register interrupt handler */
++      ret = request_irq(ndev->irq, canfd_interrupt, IRQF_SHARED, ndev->name, ndev);
++      if (ret) {
++              netdev_err(ndev, "Request_irq err: %d\n", ret);
++              goto exit_irq;
++      }
++
++      ret = canfd_chip_start(ndev);
++      if (ret) {
++              netdev_err(ndev, "Could Not Start CAN device !\n");
++              goto exit_can_start;
++      }
++
++      napi_enable(&priv->napi);
++      netif_start_queue(ndev);
++
++      return 0;
++
++exit_can_start:
++      free_irq(ndev->irq, ndev);
++err:
++      pm_runtime_put(priv->dev);
++exit_irq:
++      close_candev(ndev);
++      return ret;
++}
++
++static int canfd_control_parse_dt(struct ipms_canfd_priv *priv)
++{
++      struct of_phandle_args args;
++      u32 syscon_mask, syscon_shift;
++      u32 can_or_canfd;
++      u32 syscon_offset, regval;
++      int ret;
++
++      ret = of_parse_phandle_with_fixed_args(priv->dev->of_node,
++                                              "starfive,sys-syscon", 3, 0, &args);
++      if (ret) {
++              dev_err(priv->dev, "Failed to parse starfive,sys-syscon\n");
++              return -EINVAL;
++      }
++
++      priv->reg_syscon = syscon_node_to_regmap(args.np);
++      of_node_put(args.np);
++      if (IS_ERR(priv->reg_syscon))
++              return PTR_ERR(priv->reg_syscon);
++
++      syscon_offset = args.args[0];
++      syscon_shift  = args.args[1];
++      syscon_mask   = args.args[2];
++
++      ret = device_property_read_u32(priv->dev, "syscon,can_or_canfd", &can_or_canfd);
++      if (ret)
++              goto exit_parse;
++
++      priv->can_or_canfd = can_or_canfd;
++
++      /* enable can2.0/canfd function */
++      regval = can_or_canfd << syscon_shift;
++      ret = regmap_update_bits(priv->reg_syscon, syscon_offset, syscon_mask, regval);
++      if (ret)
++              return ret;
++      return 0;
++exit_parse:
++      return ret;
++}
++
++static const struct net_device_ops canfd_netdev_ops = {
++      .ndo_open = canfd_driver_open,
++      .ndo_stop = canfd_driver_close,
++      .ndo_start_xmit = canfd_driver_start_xmit,
++      .ndo_change_mtu = can_change_mtu,
++};
++
++static int canfd_driver_probe(struct platform_device *pdev)
++{
++      struct net_device *ndev;
++      struct ipms_canfd_priv *priv;
++      void __iomem *addr;
++      int ret;
++      u32 frq;
++
++      addr = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(addr)) {
++              ret = PTR_ERR(addr);
++              goto exit;
++      }
++
++      ndev = alloc_candev(sizeof(struct ipms_canfd_priv), 1);
++      if (!ndev) {
++              ret = -ENOMEM;
++              goto exit;
++      }
++
++      priv = netdev_priv(ndev);
++      priv->dev = &pdev->dev;
++
++      ret = canfd_control_parse_dt(priv);
++      if (ret)
++              goto free_exit;
++
++      priv->nr_clks = devm_clk_bulk_get_all(priv->dev, &priv->clks);
++      if (priv->nr_clks < 0) {
++              dev_err(priv->dev, "Failed to get can clocks\n");
++              ret = -ENODEV;
++              goto free_exit;
++      }
++
++      ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks);
++      if (ret) {
++              dev_err(priv->dev, "Failed to enable clocks\n");
++              goto free_exit;
++      }
++
++      priv->resets = devm_reset_control_array_get_exclusive(priv->dev);
++      if (IS_ERR(priv->resets)) {
++              ret = PTR_ERR(priv->resets);
++              dev_err(priv->dev, "Failed to get can resets");
++              goto clk_exit;
++      }
++
++      ret = reset_control_deassert(priv->resets);
++      if (ret)
++              goto clk_exit;
++      priv->can.bittiming_const = &canfd_bittiming_const;
++      priv->can.data_bittiming_const = &canfd_data_bittiming_const;
++      priv->can.do_set_mode = canfd_do_set_mode;
++
++      /* in user space the execution mode can be chosen */
++      if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD)
++              priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_FD;
++      else
++              priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK;
++      priv->reg_base = addr;
++      priv->write_reg = canfd_write_reg_le;
++      priv->read_reg = canfd_read_reg_le;
++
++      pm_runtime_enable(&pdev->dev);
++
++      priv->can_clk = devm_clk_get(&pdev->dev, "core_clk");
++      if (IS_ERR(priv->can_clk)) {
++              dev_err(&pdev->dev, "Device clock not found.\n");
++              ret = PTR_ERR(priv->can_clk);
++              goto reset_exit;
++      }
++
++      device_property_read_u32(priv->dev, "frequency", &frq);
++      clk_set_rate(priv->can_clk, frq);
++
++      priv->can.clock.freq = clk_get_rate(priv->can_clk);
++      ndev->irq = platform_get_irq(pdev, 0);
++
++      /* we support local echo */
++      ndev->flags |= IFF_ECHO;
++      ndev->netdev_ops = &canfd_netdev_ops;
++
++      platform_set_drvdata(pdev, ndev);
++      SET_NETDEV_DEV(ndev, &pdev->dev);
++
++      netif_napi_add(ndev, &priv->napi, canfd_rx_poll);
++      ret = register_candev(ndev);
++      if (ret) {
++              dev_err(&pdev->dev, "Fail to register failed (err=%d)\n", ret);
++              goto reset_exit;
++      }
++
++      dev_dbg(&pdev->dev, "Driver registered: regs=%p, irp=%d, clock=%d\n",
++              priv->reg_base, ndev->irq, priv->can.clock.freq);
++
++      return 0;
++
++reset_exit:
++      reset_control_assert(priv->resets);
++clk_exit:
++      clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
++free_exit:
++      free_candev(ndev);
++exit:
++      return ret;
++}
++
++static int canfd_driver_remove(struct platform_device *pdev)
++{
++      struct net_device *ndev = platform_get_drvdata(pdev);
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++      reset_control_assert(priv->resets);
++      clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
++      pm_runtime_disable(&pdev->dev);
++
++      unregister_candev(ndev);
++      netif_napi_del(&priv->napi);
++      free_candev(ndev);
++
++      return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int __maybe_unused canfd_suspend(struct device *dev)
++{
++      struct net_device *ndev = dev_get_drvdata(dev);
++
++      if (netif_running(ndev)) {
++              netif_stop_queue(ndev);
++              netif_device_detach(ndev);
++              canfd_driver_stop(ndev);
++      }
++
++      return pm_runtime_force_suspend(dev);
++}
++
++static int __maybe_unused canfd_resume(struct device *dev)
++{
++      struct net_device *ndev = dev_get_drvdata(dev);
++      int ret;
++
++      ret = pm_runtime_force_resume(dev);
++      if (ret) {
++              dev_err(dev, "pm_runtime_force_resume failed on resume\n");
++              return ret;
++      }
++
++      if (netif_running(ndev)) {
++              ret = canfd_chip_start(ndev);
++              if (ret) {
++                      dev_err(dev, "canfd_chip_start failed on resume\n");
++                      return ret;
++              }
++
++              netif_device_attach(ndev);
++              netif_start_queue(ndev);
++      }
++
++      return 0;
++}
++#endif
++
++#ifdef CONFIG_PM
++static int canfd_runtime_suspend(struct device *dev)
++{
++      struct net_device *ndev = dev_get_drvdata(dev);
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++
++      reset_control_assert(priv->resets);
++      clk_bulk_disable_unprepare(priv->nr_clks, priv->clks);
++
++      return 0;
++}
++
++static int canfd_runtime_resume(struct device *dev)
++{
++      struct net_device *ndev = dev_get_drvdata(dev);
++      struct ipms_canfd_priv *priv = netdev_priv(ndev);
++      int ret;
++
++      ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks);
++      if (ret) {
++              dev_err(dev, "Failed to  prepare_enable clk\n");
++              return ret;
++      }
++
++      ret = reset_control_deassert(priv->resets);
++      if (ret) {
++              dev_err(dev, "Failed to deassert reset\n");
++              return ret;
++      }
++
++      return 0;
++}
++#endif
++
++static const struct dev_pm_ops canfd_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(canfd_suspend, canfd_resume)
++      SET_RUNTIME_PM_OPS(canfd_runtime_suspend,
++                         canfd_runtime_resume, NULL)
++};
++
++static const struct of_device_id canfd_of_match[] = {
++      { .compatible = "ipms,can" },
++      { }
++};
++MODULE_DEVICE_TABLE(of, canfd_of_match);
++
++static struct platform_driver can_driver = {
++      .probe          = canfd_driver_probe,
++      .remove         = canfd_driver_remove,
++      .driver = {
++              .name  = DRIVER_NAME,
++              .pm    = &canfd_pm_ops,
++              .of_match_table = canfd_of_match,
++      },
++};
++
++module_platform_driver(can_driver);
++
++MODULE_DESCRIPTION("ipms can controller driver for StarFive jh7110 SoC");
++MODULE_AUTHOR("William Qiu<william.qiu@starfivetech.com");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0071-regulator-starfive-jh7110-Add-regulator-support-for-.patch b/target/linux/starfive/patches-6.6/0071-regulator-starfive-jh7110-Add-regulator-support-for-.patch
new file mode 100644 (file)
index 0000000..cf1e623
--- /dev/null
@@ -0,0 +1,204 @@
+From b12213d474966fbf47e7afa36a6655b5b241cf36 Mon Sep 17 00:00:00 2001
+From: "Kevin.xie" <kevin.xie@starfivetech.com>
+Date: Fri, 9 Jun 2023 14:57:13 +0800
+Subject: [PATCH 071/116] regulator: starfive-jh7110: Add regulator support for
+ JH7110 A type EVB.
+
+Add 7 regulators base on regulator framework for
+JH7110 evb HW design.
+
+Signed-off-by: Kevin.xie <kevin.xie@starfivetech.com>
+---
+ drivers/regulator/Kconfig                     |  10 ++
+ drivers/regulator/Makefile                    |   1 +
+ drivers/regulator/starfive-jh7110-regulator.c | 126 ++++++++++++++++++
+ include/linux/regulator/jh7110.h              |  24 ++++
+ 4 files changed, 161 insertions(+)
+ create mode 100644 drivers/regulator/starfive-jh7110-regulator.c
+ create mode 100644 include/linux/regulator/jh7110.h
+
+--- a/drivers/regulator/Kconfig
++++ b/drivers/regulator/Kconfig
+@@ -1335,6 +1335,16 @@ config REGULATOR_SM5703
+         This driver provides support for voltage regulators of SM5703
+         multi-function device.
++config REGULATOR_STARFIVE_JH7110
++      tristate "Starfive JH7110 PMIC"
++      depends on I2C
++      select REGMAP_I2C
++      help
++        Say y here to select this option to enable the power regulator of
++        Starfive JH7110 PMIC.
++        This driver supports the control of different power rails of device
++        through regulator interface.
++
+ config REGULATOR_STM32_BOOSTER
+       tristate "STMicroelectronics STM32 BOOSTER"
+       depends on ARCH_STM32 || COMPILE_TEST
+--- a/drivers/regulator/Makefile
++++ b/drivers/regulator/Makefile
+@@ -156,6 +156,7 @@ obj-$(CONFIG_REGULATOR_SC2731) += sc2731
+ obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
+ obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o
+ obj-$(CONFIG_REGULATOR_SM5703) += sm5703-regulator.o
++obj-$(CONFIG_REGULATOR_STARFIVE_JH7110) += starfive-jh7110-regulator.o
+ obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o
+ obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+ obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
+--- /dev/null
++++ b/drivers/regulator/starfive-jh7110-regulator.c
+@@ -0,0 +1,126 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2022 Starfive Technology Co., Ltd.
++ * Author: Mason Huo <mason.huo@starfivetech.com>
++ */
++
++#include <linux/err.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/regulator/driver.h>
++#include <linux/regulator/machine.h>
++#include <linux/regulator/of_regulator.h>
++#include <linux/regulator/jh7110.h>
++#include <linux/slab.h>
++
++#define JH7110_PM_POWER_SW_0          0x80
++#define JH7110_PM_POWER_SW_1          0x81
++#define ENABLE_MASK(id)                       BIT(id)
++
++
++static const struct regmap_config jh7110_regmap_config = {
++      .reg_bits = 8,
++      .val_bits = 8,
++      .max_register = JH7110_PM_POWER_SW_1,
++      .cache_type = REGCACHE_FLAT,
++};
++
++static const struct regulator_ops jh7110_ldo_ops = {
++      .enable = regulator_enable_regmap,
++      .disable = regulator_disable_regmap,
++      .is_enabled = regulator_is_enabled_regmap,
++};
++
++#define JH7110_LDO(_id, _name, en_reg, en_mask) \
++{\
++      .name = (_name),\
++      .ops = &jh7110_ldo_ops,\
++      .of_match = of_match_ptr(_name),\
++      .regulators_node = of_match_ptr("regulators"),\
++      .type = REGULATOR_VOLTAGE,\
++      .id = JH7110_ID_##_id,\
++      .owner = THIS_MODULE,\
++      .enable_reg = JH7110_PM_POWER_SW_##en_reg,\
++      .enable_mask = ENABLE_MASK(en_mask),\
++}
++
++static const struct regulator_desc jh7110_regulators[] = {
++      JH7110_LDO(LDO_REG1, "hdmi_1p8", 0, 0),
++      JH7110_LDO(LDO_REG2, "mipitx_1p8", 0, 1),
++      JH7110_LDO(LDO_REG3, "mipirx_1p8", 0, 2),
++      JH7110_LDO(LDO_REG4, "hdmi_0p9", 0, 3),
++      JH7110_LDO(LDO_REG5, "mipitx_0p9", 0, 4),
++      JH7110_LDO(LDO_REG6, "mipirx_0p9", 0, 5),
++      JH7110_LDO(LDO_REG7, "sdio_vdd", 1, 0),
++};
++
++static int jh7110_i2c_probe(struct i2c_client *i2c)
++{
++      struct regulator_config config = { };
++      struct regulator_dev *rdev;
++      struct regulator_init_data *init_data;
++      struct regmap *regmap;
++      int i, ret;
++
++      regmap = devm_regmap_init_i2c(i2c, &jh7110_regmap_config);
++      if (IS_ERR(regmap)) {
++              ret = PTR_ERR(regmap);
++              dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
++                      ret);
++              return ret;
++      }
++
++      init_data = of_get_regulator_init_data(&i2c->dev, i2c->dev.of_node, NULL);
++      if (!init_data)
++              return -ENOMEM;
++      config.init_data = init_data;
++
++      for (i = 0; i < JH7110_MAX_REGULATORS; i++) {
++              config.dev = &i2c->dev;
++              config.regmap = regmap;
++
++              rdev = devm_regulator_register(&i2c->dev,
++                      &jh7110_regulators[i], &config);
++              if (IS_ERR(rdev)) {
++                      dev_err(&i2c->dev,
++                              "Failed to register JH7110 regulator\n");
++                      return PTR_ERR(rdev);
++              }
++      }
++
++      return 0;
++}
++
++static const struct i2c_device_id jh7110_i2c_id[] = {
++      {"jh7110_evb_reg", 0},
++      {},
++};
++MODULE_DEVICE_TABLE(i2c, jh7110_i2c_id);
++
++#ifdef CONFIG_OF
++static const struct of_device_id jh7110_dt_ids[] = {
++      { .compatible = "starfive,jh7110-evb-regulator",
++        .data = &jh7110_i2c_id[0] },
++      {},
++};
++MODULE_DEVICE_TABLE(of, jh7110_dt_ids);
++#endif
++
++static struct i2c_driver jh7110_regulator_driver = {
++      .driver = {
++              .name = "jh7110-evb-regulator",
++              .of_match_table = of_match_ptr(jh7110_dt_ids),
++      },
++      .probe = jh7110_i2c_probe,
++      .id_table = jh7110_i2c_id,
++};
++
++module_i2c_driver(jh7110_regulator_driver);
++
++MODULE_AUTHOR("Mason Huo <mason.huo@starfivetech.com>");
++MODULE_DESCRIPTION("Regulator device driver for Starfive JH7110");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/include/linux/regulator/jh7110.h
+@@ -0,0 +1,24 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2022 Starfive Technology Co., Ltd.
++ * Author: Mason Huo <mason.huo@starfivetech.com>
++ */
++
++#ifndef __LINUX_REGULATOR_JH7110_H
++#define __LINUX_REGULATOR_JH7110_H
++
++#define JH7110_MAX_REGULATORS 7
++
++
++enum jh7110_reg_id {
++      JH7110_ID_LDO_REG1 = 0,
++      JH7110_ID_LDO_REG2,
++      JH7110_ID_LDO_REG3,
++      JH7110_ID_LDO_REG4,
++      JH7110_ID_LDO_REG5,
++      JH7110_ID_LDO_REG6,
++      JH7110_ID_LDO_REG7,
++};
++
++
++#endif /* __LINUX_REGULATOR_JH7110_H */
diff --git a/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch b/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch
new file mode 100644 (file)
index 0000000..d602df9
--- /dev/null
@@ -0,0 +1,40 @@
+From f0b4cffe4d1813305f783d208f260747ecc56c50 Mon Sep 17 00:00:00 2001
+From: "Kevin.xie" <kevin.xie@starfivetech.com>
+Date: Thu, 24 Nov 2022 16:59:12 +0800
+Subject: [PATCH 072/116] drivers: nvme: Add precheck and delay for CQE pending
+ status.
+
+To workaroud the NVMe I/O timeout problem in bootup S10udev case
+which caused by the CQE update lantancy.
+
+Signed-off-by: Kevin.xie <kevin.xie@starfivetech.com>
+---
+ drivers/nvme/host/pci.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -28,6 +28,7 @@
+ #include <linux/io-64-nonatomic-hi-lo.h>
+ #include <linux/sed-opal.h>
+ #include <linux/pci-p2pdma.h>
++#include <linux/delay.h>
+ #include "trace.h"
+ #include "nvme.h"
+@@ -1062,6 +1063,15 @@ static inline int nvme_poll_cq(struct nv
+ {
+       int found = 0;
++      /*
++       * In some cases, such as udev trigger, cqe status may update
++       * a little bit later than MSI, which cause an irq handle missing.
++       * To workaound, here we will prefetch the status first, and wait
++       * 1us if we get nothing.
++       */
++      if (!nvme_cqe_pending(nvmeq))
++              udelay(1);
++
+       while (nvme_cqe_pending(nvmeq)) {
+               found++;
+               /*
diff --git a/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch b/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch
new file mode 100644 (file)
index 0000000..fff01c4
--- /dev/null
@@ -0,0 +1,93 @@
+From eb294df4b9fab46bc5dbf676edf51e28e06d1968 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?=
+ <joao.mario@tecnico.ulisboa.pt>
+Date: Tue, 16 Nov 2021 15:48:09 +0000
+Subject: [PATCH 073/116] RISC-V: Create unique identification for SoC PMU
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The SBI PMU platform driver did not provide any identification for
+perf events matching. This patch introduces a new sysfs file inside the
+platform device (soc:pmu/id) for pmu identification.
+
+The identification is a 64-bit value generated as:
+[63-32]: mvendorid;
+[31]: marchid[MSB];
+[30-16]: marchid[15-0];
+[15-0]: mimpid[15MSBs];
+
+The CSRs are detailed in the RISC-V privileged spec [1].
+The marchid is split in MSB + 15LSBs, due to the MSB being used for
+open-source architecture identification.
+
+[1] https://github.com/riscv/riscv-isa-manual
+
+Signed-off-by: João Mário Domingos <joao.mario@tecnico.ulisboa.pt>
+---
+ drivers/perf/riscv_pmu_sbi.c | 47 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+--- a/drivers/perf/riscv_pmu_sbi.c
++++ b/drivers/perf/riscv_pmu_sbi.c
+@@ -1019,6 +1019,46 @@ static struct ctl_table sbi_pmu_sysctl_t
+       { }
+ };
++static uint64_t pmu_sbi_get_pmu_id(void)
++{
++      union sbi_pmu_id {
++              uint64_t value;
++              struct {
++                      uint16_t imp:16;
++                      uint16_t arch:16;
++                      uint32_t vendor:32;
++              };
++      } pmuid;
++
++      pmuid.value = 0;
++      pmuid.vendor = (uint32_t) sbi_get_mvendorid();
++      pmuid.arch = (sbi_get_marchid() >> (63 - 15) & (1 << 15)) | (sbi_get_marchid() & 0x7FFF);
++      pmuid.imp = (sbi_get_mimpid() >> 16);
++
++      return pmuid.value;
++}
++
++static ssize_t pmu_sbi_id_show(struct device *dev,
++              struct device_attribute *attr, char *buf)
++{
++      int len;
++
++      len = sprintf(buf, "0x%llx\n", pmu_sbi_get_pmu_id());
++      if (len <= 0)
++              dev_err(dev, "mydrv: Invalid sprintf len: %dn", len);
++
++      return len;
++}
++
++static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, pmu_sbi_id_show, 0);
++
++static struct attribute *pmu_sbi_attrs[] = {
++      &dev_attr_id.attr,
++      NULL
++};
++
++ATTRIBUTE_GROUPS(pmu_sbi);
++
+ static int pmu_sbi_device_probe(struct platform_device *pdev)
+ {
+       struct riscv_pmu *pmu = NULL;
+@@ -1067,6 +1107,13 @@ static int pmu_sbi_device_probe(struct p
+       pmu->event_unmapped = pmu_sbi_event_unmapped;
+       pmu->csr_index = pmu_sbi_csr_index;
++      ret = sysfs_create_group(&pdev->dev.kobj, &pmu_sbi_group);
++      if (ret) {
++              dev_err(&pdev->dev, "sysfs creation failed\n");
++              return ret;
++      }
++      pdev->dev.groups = pmu_sbi_groups;
++
+       ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
+       if (ret)
+               return ret;
diff --git a/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch b/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch
new file mode 100644 (file)
index 0000000..177b3b3
--- /dev/null
@@ -0,0 +1,55 @@
+From 1dc069ffadf4ce7817a716f9df2f480254e9b01d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?=
+ <joao.mario@tecnico.ulisboa.pt>
+Date: Tue, 16 Nov 2021 15:48:10 +0000
+Subject: [PATCH 074/116] RISC-V: Support CPUID for risc-v in perf
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch creates the header.c file for the risc-v architecture and introduces support for
+PMU identification through sysfs.
+It is now possible to configure pmu-events in risc-v.
+
+Depends on patch [1], that introduces the id sysfs file.
+
+Signed-off-by: João Mário Domingos <joao.mario@tecnico.ulisboa.pt>
+Signed-off-by: minda.chen <minda.chen@starfivetech.com>
+---
+ drivers/perf/riscv_pmu.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/perf/riscv_pmu.c
++++ b/drivers/perf/riscv_pmu.c
+@@ -18,6 +18,23 @@
+ #include <asm/sbi.h>
++PMU_FORMAT_ATTR(event, "config:0-63");
++
++static struct attribute *riscv_arch_formats_attr[] = {
++      &format_attr_event.attr,
++      NULL,
++};
++
++static struct attribute_group riscv_pmu_format_group = {
++      .name = "format",
++      .attrs = riscv_arch_formats_attr,
++};
++
++static const struct attribute_group *riscv_pmu_attr_groups[] = {
++      &riscv_pmu_format_group,
++      NULL,
++};
++
+ static bool riscv_perf_user_access(struct perf_event *event)
+ {
+       return ((event->attr.type == PERF_TYPE_HARDWARE) ||
+@@ -410,6 +427,7 @@ struct riscv_pmu *riscv_pmu_alloc(void)
+                       cpuc->events[i] = NULL;
+       }
+       pmu->pmu = (struct pmu) {
++              .attr_groups    = riscv_pmu_attr_groups,
+               .event_init     = riscv_pmu_event_init,
+               .event_mapped   = riscv_pmu_event_mapped,
+               .event_unmapped = riscv_pmu_event_unmapped,
diff --git a/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch b/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch
new file mode 100644 (file)
index 0000000..01bb4f1
--- /dev/null
@@ -0,0 +1,42 @@
+From 3e6ea12dda276c01a756764fcafa315b19860c33 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?=
+ <joao.mario@tecnico.ulisboa.pt>
+Date: Tue, 16 Nov 2021 15:48:11 +0000
+Subject: [PATCH 075/116] RISC-V: Added generic pmu-events mapfile
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The pmu-events now supports custom events for RISC-V, plus the cycle,
+time and instret events were defined.
+
+Signed-off-by: João Mário Domingos <joao.mario@tecnico.ulisboa.pt>
+---
+ .../pmu-events/arch/riscv/riscv-generic.json  | 20 +++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+ create mode 100644 tools/perf/pmu-events/arch/riscv/riscv-generic.json
+
+--- /dev/null
++++ b/tools/perf/pmu-events/arch/riscv/riscv-generic.json
+@@ -0,0 +1,20 @@
++[
++  {
++    "PublicDescription": "CPU Cycles",
++    "EventCode": "0x00",
++    "EventName": "riscv_cycles",
++    "BriefDescription": "CPU cycles RISC-V generic counter"
++  },
++  {
++    "PublicDescription": "CPU Time",
++      "EventCode": "0x01",
++      "EventName": "riscv_time",
++      "BriefDescription": "CPU time RISC-V generic counter"
++  },
++  {
++    "PublicDescription": "CPU Instructions",
++      "EventCode": "0x02",
++      "EventName": "riscv_instret",
++      "BriefDescription": "CPU retired instructions RISC-V generic counter"
++  }
++]
+\ No newline at end of file
diff --git a/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch b/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch
new file mode 100644 (file)
index 0000000..152a60c
--- /dev/null
@@ -0,0 +1,30 @@
+From 30e0cdcf9e05faa65ecde4ed8b70039568fdb660 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Thu, 2 Mar 2023 17:16:01 +0800
+Subject: [PATCH 076/116] perf: sbi: disable cpu hotplug callback.
+
+register cpu hotplug callback will cause dhrystone
+and coremark benchmark reduce the scores. this CPU
+hotplug ops will do in sbi cpu/on and off. So disable
+this no side effect.
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/perf/riscv_pmu_sbi.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/perf/riscv_pmu_sbi.c
++++ b/drivers/perf/riscv_pmu_sbi.c
+@@ -1114,9 +1114,11 @@ static int pmu_sbi_device_probe(struct p
+       }
+       pdev->dev.groups = pmu_sbi_groups;
++#ifndef CONFIG_ARCH_STARFIVE
+       ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
+       if (ret)
+               return ret;
++#endif
+       ret = riscv_pm_pmu_register(pmu);
+       if (ret)
diff --git a/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch b/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch
new file mode 100644 (file)
index 0000000..cdc6e4e
--- /dev/null
@@ -0,0 +1,24 @@
+From fc4b5c7c27e1b56b1f848e50511c4fd081b1b6c5 Mon Sep 17 00:00:00 2001
+From: Walker Chen <walker.chen@starfivetech.com>
+Date: Mon, 12 Jun 2023 21:21:45 +0800
+Subject: [PATCH 077/116] dmaengine: dw-axi-dmac: Drop unused print message
+
+Removed printing information which is not related to StarFive
+platform.
+
+Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -523,7 +523,7 @@ static void dw_axi_dma_set_hw_channel(st
+       unsigned long reg_value, val;
+       if (!chip->apb_regs) {
+-              dev_err(chip->dev, "apb_regs not initialized\n");
++              dev_dbg(chip->dev, "apb_regs not initialized\n");
+               return;
+       }
diff --git a/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch b/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch
new file mode 100644 (file)
index 0000000..2a82897
--- /dev/null
@@ -0,0 +1,4748 @@
+From cd2254c6be9441ebacaa35693ecb5ce116b90622 Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Fri, 16 Jun 2023 16:27:46 +0800
+Subject: [PATCH 079/116] ASoC: codecs: Add AC108 Codec driver
+
+Add AC108 Codec driver and AC101 driver for AC10x.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/codecs/Kconfig      |    5 +
+ sound/soc/codecs/Makefile     |    2 +
+ sound/soc/codecs/ac101.c      | 1716 +++++++++++++++++++++++++++++++++
+ sound/soc/codecs/ac101_regs.h |  431 +++++++++
+ sound/soc/codecs/ac108.c      | 1622 +++++++++++++++++++++++++++++++
+ sound/soc/codecs/ac108.h      |  749 ++++++++++++++
+ sound/soc/codecs/ac10x.h      |  152 +++
+ 7 files changed, 4677 insertions(+)
+ create mode 100644 sound/soc/codecs/ac101.c
+ create mode 100644 sound/soc/codecs/ac101_regs.h
+ create mode 100644 sound/soc/codecs/ac108.c
+ create mode 100644 sound/soc/codecs/ac108.h
+ create mode 100644 sound/soc/codecs/ac10x.h
+
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS
+       depends on COMPILE_TEST
+       imply SND_SOC_88PM860X
+       imply SND_SOC_AB8500_CODEC
++      imply SND_SOC_AC108
+       imply SND_SOC_AC97_CODEC
+       imply SND_SOC_AD1836
+       imply SND_SOC_AD193X_SPI
+@@ -397,6 +398,10 @@ config SND_SOC_AB8500_CODEC
+       tristate
+       depends on ABX500_CORE
++config SND_SOC_AC108
++      tristate "AC108"
++      depends on I2C
++
+ config SND_SOC_AC97_CODEC
+       tristate "Build generic ASoC AC97 CODEC driver"
+       select SND_AC97_CODEC
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -2,6 +2,7 @@
+ snd-soc-88pm860x-objs := 88pm860x-codec.o
+ snd-soc-ab8500-codec-objs := ab8500-codec.o
+ snd-soc-ac97-objs := ac97.o
++snd-soc-ac108-objs := ac108.o ac101.o
+ snd-soc-ad1836-objs := ad1836.o
+ snd-soc-ad193x-objs := ad193x.o
+ snd-soc-ad193x-spi-objs := ad193x-spi.o
+@@ -386,6 +387,7 @@ snd-soc-simple-mux-objs := simple-mux.o
+ obj-$(CONFIG_SND_SOC_88PM860X)        += snd-soc-88pm860x.o
+ obj-$(CONFIG_SND_SOC_AB8500_CODEC)    += snd-soc-ab8500-codec.o
++obj-$(CONFIG_SND_SOC_AC108)           += snd-soc-ac108.o
+ obj-$(CONFIG_SND_SOC_AC97_CODEC)      += snd-soc-ac97.o
+ obj-$(CONFIG_SND_SOC_AD1836)  += snd-soc-ad1836.o
+ obj-$(CONFIG_SND_SOC_AD193X)  += snd-soc-ad193x.o
+--- /dev/null
++++ b/sound/soc/codecs/ac101.c
+@@ -0,0 +1,1716 @@
++/*
++ * ac101.c
++ *
++ * (C) Copyright 2017-2018
++ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
++ *
++ * PeterYang <linsheng.yang@seeed.cc>
++ *
++ * (C) Copyright 2014-2017
++ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
++ *
++ * huangxin <huangxin@Reuuimllatech.com>
++ * liushaohua <liushaohua@allwinnertech.com>
++ *
++ * X-Powers AC101 codec driver
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/input.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <sound/tlv.h>
++#include <linux/workqueue.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include "ac101_regs.h"
++#include "ac10x.h"
++
++/* #undef AC101_DEBG
++ * use 'make DEBUG=1' to enable debugging
++ */
++
++/*
++ * *** To sync channels ***
++ *
++ * 1. disable clock in codec   hw_params()
++ * 2. clear   fifo  in bcm2835 hw_params()
++ * 3. clear   fifo  in bcm2385 prepare()
++ * 4. enable  RX    in bcm2835 trigger()
++ * 5. enable  clock in machine trigger()
++ */
++
++/*Default initialize configuration*/
++static bool speaker_double_used = 1;
++static int double_speaker_val = 0x1B;
++static int single_speaker_val = 0x19;
++static int headset_val                = 0x3B;
++static int mainmic_val                = 0x4;
++static int headsetmic_val     = 0x4;
++static bool dmic_used         = 0;
++static int adc_digital_val    = 0xb0b0;
++static bool drc_used          = false;
++
++#define AC101_RATES  (SNDRV_PCM_RATE_8000_96000 &             \
++              ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \
++              SNDRV_PCM_RATE_88200))
++#define AC101_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | \
++                      SNDRV_PCM_FMTBIT_S24_LE |*/     \
++                      SNDRV_PCM_FMTBIT_S32_LE | \
++                      0)
++
++static struct ac10x_priv* static_ac10x;
++
++
++int ac101_read(struct snd_soc_codec *codec, unsigned reg) {
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int r, v = 0;
++
++      if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) {
++              dev_err(codec->dev, "read reg %02X fail\n",
++                       reg);
++              return r;
++      }
++      return v;
++}
++
++int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) {
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int v;
++
++      v = regmap_write(ac10x->regmap101, reg, val);
++      return v;
++}
++
++int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg,
++                      unsigned mask, unsigned value
++) {
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int v;
++
++      v = regmap_update_bits(ac10x->regmap101, reg, mask, value);
++      return v;
++}
++
++
++
++#ifdef CONFIG_AC101_SWITCH_DETECT
++/******************************************************************************/
++/********************************switch****************************************/
++/******************************************************************************/
++#define KEY_HEADSETHOOK         226           /* key define */
++#define HEADSET_FILTER_CNT    (10)
++
++/*
++ * switch_hw_config:config the 53 codec register
++ */
++static void switch_hw_config(struct snd_soc_codec *codec)
++{
++      int r;
++
++      AC101_DBG();
++
++      /*HMIC/MMIC BIAS voltage level select:2.5v*/
++      ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<<BIASVOLTAGE), (0xf<<BIASVOLTAGE));
++      /*debounce when Key down or keyup*/
++      ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_M), (0x0<<HMIC_M));
++      /*debounce when earphone plugin or pullout*/
++      ac101_update_bits(codec, HMIC_CTRL1, (0xf<<HMIC_N), (0x0<<HMIC_N));
++      /*Down Sample Setting Select: Downby 4,32Hz*/
++      ac101_update_bits(codec, HMIC_CTRL2, (0x3<<HMIC_SAMPLE_SELECT),
++                        (0x02<<HMIC_SAMPLE_SELECT));
++      /*Hmic_th2 for detecting Keydown or Keyup.*/
++      ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH2), (0x8<<HMIC_TH2));
++      /*Hmic_th1[4:0],detecting eraphone plugin or pullout*/
++      ac101_update_bits(codec, HMIC_CTRL2, (0x1f<<HMIC_TH1), (0x1<<HMIC_TH1));
++      /*Headset microphone BIAS working mode: when HBIASEN = 1 */
++      ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASMOD), (0x1<<HBIASMOD));
++      /*Headset microphone BIAS Enable*/
++      ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0x1<<HBIASEN));
++      /*Headset microphone BIAS Current sensor & ADC Enable*/
++      ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0x1<<HBIASADCEN));
++      /*Earphone Plugin/out Irq Enable*/
++      ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PULLOUT_IRQ), (0x1<<HMIC_PULLOUT_IRQ));
++      ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_PLUGIN_IRQ), (0x1<<HMIC_PLUGIN_IRQ));
++
++      /*Hmic KeyUp/key down Irq Enable*/
++      ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYDOWN_IRQ), (0x1<<HMIC_KEYDOWN_IRQ));
++      ac101_update_bits(codec, HMIC_CTRL1, (0x1<<HMIC_KEYUP_IRQ), (0x1<<HMIC_KEYUP_IRQ));
++
++      /*headphone calibration clock frequency select*/
++      ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
++
++      /*clear hmic interrupt */
++      r = HMIC_PEND_ALL;
++      ac101_write(codec, HMIC_STS, r);
++
++      return;
++}
++
++/*
++ * switch_status_update: update the switch state.
++ */
++static void switch_status_update(struct ac10x_priv *ac10x)
++{
++      AC101_DBG("ac10x->state:%d\n", ac10x->state);
++
++    input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state);
++    input_sync(ac10x->inpdev);
++    return;
++}
++
++/*
++ * work_cb_clear_irq: clear audiocodec pending and Record the interrupt.
++ */
++static void work_cb_clear_irq(struct work_struct *work)
++{
++      int reg_val = 0;
++      struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_clear_irq);
++      struct snd_soc_codec *codec = ac10x->codec;
++
++      ac10x->irq_cntr++;
++
++      reg_val = ac101_read(codec, HMIC_STS);
++      if (BIT(HMIC_PULLOUT_PEND) & reg_val) {
++              ac10x->pullout_cntr++;
++              AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr);
++      }
++
++      reg_val |= HMIC_PEND_ALL;
++      ac101_write(codec, HMIC_STS, reg_val);
++
++      reg_val = ac101_read(codec, HMIC_STS);
++      if ((reg_val & HMIC_PEND_ALL) != 0){
++              reg_val |= HMIC_PEND_ALL;
++              ac101_write(codec, HMIC_STS, reg_val);
++      }
++
++      if (cancel_work_sync(&ac10x->work_switch) != 0) {
++              ac10x->irq_cntr--;
++      }
++
++      if (0 == schedule_work(&ac10x->work_switch)) {
++              ac10x->irq_cntr--;
++              AC101_DBG("[work_cb_clear_irq] add work struct failed!\n");
++      }
++}
++
++enum {
++      HBIAS_LEVEL_1 = 0x02,
++      HBIAS_LEVEL_2 = 0x0B,
++      HBIAS_LEVEL_3 = 0x13,
++      HBIAS_LEVEL_4 = 0x17,
++      HBIAS_LEVEL_5 = 0x19,
++};
++
++static int __ac101_get_hmic_data(struct snd_soc_codec *codec) {
++      #ifdef AC101_DEBG
++      static long counter;
++      #endif
++      int r, d;
++
++      d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS));
++
++      r = 0x1 << HMIC_DATA_PEND;
++      ac101_write(codec, HMIC_STS, r);
++
++      /* prevent i2c accessing too frequently */
++      usleep_range(1500, 3000);
++
++      AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d);
++      return d;
++}
++
++/*
++ * work_cb_earphone_switch: judge the status of the headphone
++ */
++static void work_cb_earphone_switch(struct work_struct *work)
++{
++      struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_switch);
++      struct snd_soc_codec *codec = ac10x->codec;
++
++      static int hook_flag1 = 0, hook_flag2 = 0;
++      static int KEY_VOLUME_FLAG = 0;
++
++      unsigned filter_buf = 0;
++      int filt_index = 0;
++      int t = 0;
++
++      ac10x->irq_cntr--;
++
++      /* read HMIC_DATA */
++      t = __ac101_get_hmic_data(codec);
++
++      if ((t >= HBIAS_LEVEL_2) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
++              t = __ac101_get_hmic_data(codec);
++
++              if (t >= HBIAS_LEVEL_5){
++                      msleep(150);
++                      t = __ac101_get_hmic_data(codec);
++                      if (((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1 - 1) || t >= HBIAS_LEVEL_5)
++                      && (ac10x->pullout_cntr == 0)) {
++                              input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1);
++                              input_sync(ac10x->inpdev);
++
++                              AC101_DBG("KEY_HEADSETHOOK1\n");
++
++                              if (hook_flag1 != hook_flag2)
++                                      hook_flag1 = hook_flag2 = 0;
++                              hook_flag1++;
++                      }
++                      if (ac10x->pullout_cntr)
++                              ac10x->pullout_cntr--;
++              } else if (t >= HBIAS_LEVEL_4) {
++                      msleep(80);
++                      t = __ac101_get_hmic_data(codec);
++                      if (t < HBIAS_LEVEL_5 && t >= HBIAS_LEVEL_4 && (ac10x->pullout_cntr == 0)) {
++                              KEY_VOLUME_FLAG = 1;
++                              input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 1);
++                              input_sync(ac10x->inpdev);
++                              input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0);
++                              input_sync(ac10x->inpdev);
++
++                              AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t);
++                      }
++                      if (ac10x->pullout_cntr)
++                              ac10x->pullout_cntr--;
++              } else if (t >= HBIAS_LEVEL_3){
++                      msleep(80);
++                      t = __ac101_get_hmic_data(codec);
++                      if (t < HBIAS_LEVEL_4 && t >= HBIAS_LEVEL_3 && (ac10x->pullout_cntr == 0)) {
++                              KEY_VOLUME_FLAG = 1;
++                              input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 1);
++                              input_sync(ac10x->inpdev);
++                              input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0);
++                              input_sync(ac10x->inpdev);
++                              AC101_DBG("KEY_VOLUMEDOWN\n");
++                      }
++                      if (ac10x->pullout_cntr)
++                              ac10x->pullout_cntr--;
++              }
++      } else if ((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) &&
++                 (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) {
++              t = __ac101_get_hmic_data(codec);
++              if (t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) {
++                      if (KEY_VOLUME_FLAG) {
++                              KEY_VOLUME_FLAG = 0;
++                      }
++                      if (hook_flag1 == (++hook_flag2)) {
++                              hook_flag1 = hook_flag2 = 0;
++                              input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0);
++                              input_sync(ac10x->inpdev);
++
++                              AC101_DBG("KEY_HEADSETHOOK0\n");
++                      }
++              }
++      } else {
++              while (ac10x->irq_cntr == 0 && ac10x->irq != 0) {
++                      msleep(20);
++
++                      t = __ac101_get_hmic_data(codec);
++
++                      if (filt_index <= HEADSET_FILTER_CNT) {
++                              if (filt_index++ == 0) {
++                                      filter_buf = t;
++                              } else if (filter_buf != t) {
++                                      filt_index = 0;
++                              }
++                              continue;
++                      }
++
++                      filt_index = 0;
++                      if (filter_buf >= HBIAS_LEVEL_2) {
++                              ac10x->mode = THREE_HEADPHONE_PLUGIN;
++                              ac10x->state = 2;
++                      } else if (filter_buf >= HBIAS_LEVEL_1 - 1) {
++                              ac10x->mode = FOUR_HEADPHONE_PLUGIN;
++                              ac10x->state = 1;
++                      } else {
++                              ac10x->mode = HEADPHONE_IDLE;
++                              ac10x->state = 0;
++                      }
++                      switch_status_update(ac10x);
++                      ac10x->pullout_cntr = 0;
++                      break;
++              }
++      }
++}
++
++/*
++ * audio_hmic_irq:  the interrupt handlers
++ */
++static irqreturn_t audio_hmic_irq(int irq, void *para)
++{
++      struct ac10x_priv *ac10x = (struct ac10x_priv *)para;
++      if (ac10x == NULL) {
++              return -EINVAL;
++      }
++
++      if (0 == schedule_work(&ac10x->work_clear_irq)){
++              AC101_DBG("[audio_hmic_irq] work already in queue_codec_irq, adding failed!\n");
++      }
++      return IRQ_HANDLED;
++}
++
++static int ac101_switch_probe(struct ac10x_priv *ac10x) {
++      struct i2c_client *i2c = ac10x->i2c101;
++      long ret;
++
++      ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN);
++      if (IS_ERR(ac10x->gpiod_irq)) {
++              ac10x->gpiod_irq = NULL;
++              dev_err(&i2c->dev, "failed get switch-irq in device tree\n");
++              goto _err_irq;
++      }
++
++      gpiod_direction_input(ac10x->gpiod_irq);
++
++      ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq);
++      if (IS_ERR_VALUE(ac10x->irq)) {
++              pr_info("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq);
++              ac10x->irq = 0;
++              goto _err_irq;
++      }
++
++      /* request irq, set irq type to falling edge trigger */
++      ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq,
++                             IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x);
++      if (IS_ERR_VALUE(ret)) {
++              pr_info("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret);
++              goto _err_irq;
++      }
++
++      ac10x->mode = HEADPHONE_IDLE;
++      ac10x->state = -1;
++
++      /*use for judge the state of switch*/
++      INIT_WORK(&ac10x->work_switch, work_cb_earphone_switch);
++      INIT_WORK(&ac10x->work_clear_irq, work_cb_clear_irq);
++
++      /********************create input device************************/
++      ac10x->inpdev = devm_input_allocate_device(ac10x->codec->dev);
++      if (!ac10x->inpdev) {
++              AC101_DBG("input_allocate_device: not enough memory for input device\n");
++              ret = -ENOMEM;
++              goto _err_input_allocate_device;
++      }
++
++      ac10x->inpdev->name          = "seed-voicecard-headset";
++      ac10x->inpdev->phys          = dev_name(ac10x->codec->dev);
++      ac10x->inpdev->id.bustype    = BUS_I2C;
++      ac10x->inpdev->dev.parent    = ac10x->codec->dev;
++      input_set_drvdata(ac10x->inpdev, ac10x->codec);
++
++      ac10x->inpdev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SW);
++
++      set_bit(KEY_HEADSETHOOK, ac10x->inpdev->keybit);
++      set_bit(KEY_VOLUMEUP,    ac10x->inpdev->keybit);
++      set_bit(KEY_VOLUMEDOWN,  ac10x->inpdev->keybit);
++      input_set_capability(ac10x->inpdev, EV_SW, SW_HEADPHONE_INSERT);
++
++      ret = input_register_device(ac10x->inpdev);
++      if (ret) {
++              AC101_DBG("input_register_device: input_register_device failed\n");
++              goto _err_input_register_device;
++      }
++
++      /* the first headset state checking */
++      switch_hw_config(ac10x->codec);
++      ac10x->irq_cntr = 1;
++      schedule_work(&ac10x->work_switch);
++
++      return 0;
++
++_err_input_register_device:
++_err_input_allocate_device:
++
++      if (ac10x->irq) {
++              devm_free_irq(&i2c->dev, ac10x->irq, ac10x);
++              ac10x->irq = 0;
++      }
++_err_irq:
++      return ret;
++}
++/******************************************************************************/
++/********************************switch****************************************/
++/******************************************************************************/
++#endif
++
++
++
++void drc_config(struct snd_soc_codec *codec)
++{
++      int reg_val;
++      reg_val = ac101_read(codec, 0xa3);
++      reg_val &= ~(0x7ff<<0);
++      reg_val |= 1<<0;
++      ac101_write(codec, 0xa3, reg_val);
++      ac101_write(codec, 0xa4, 0x2baf);
++
++      reg_val = ac101_read(codec, 0xa5);
++      reg_val &= ~(0x7ff<<0);
++      reg_val |= 1<<0;
++      ac101_write(codec, 0xa5, reg_val);
++      ac101_write(codec, 0xa6, 0x2baf);
++
++      reg_val = ac101_read(codec, 0xa7);
++      reg_val &= ~(0x7ff<<0);
++      ac101_write(codec, 0xa7, reg_val);
++      ac101_write(codec, 0xa8, 0x44a);
++
++      reg_val = ac101_read(codec, 0xa9);
++      reg_val &= ~(0x7ff<<0);
++      ac101_write(codec, 0xa9, reg_val);
++      ac101_write(codec, 0xaa, 0x1e06);
++
++      reg_val = ac101_read(codec, 0xab);
++      reg_val &= ~(0x7ff<<0);
++      reg_val |= (0x352<<0);
++      ac101_write(codec, 0xab, reg_val);
++      ac101_write(codec, 0xac, 0x6910);
++
++      reg_val = ac101_read(codec, 0xad);
++      reg_val &= ~(0x7ff<<0);
++      reg_val |= (0x77a<<0);
++      ac101_write(codec, 0xad, reg_val);
++      ac101_write(codec, 0xae, 0xaaaa);
++
++      reg_val = ac101_read(codec, 0xaf);
++      reg_val &= ~(0x7ff<<0);
++      reg_val |= (0x2de<<0);
++      ac101_write(codec, 0xaf, reg_val);
++      ac101_write(codec, 0xb0, 0xc982);
++
++      ac101_write(codec, 0x16, 0x9f9f);
++
++}
++
++void drc_enable(struct snd_soc_codec *codec,bool on)
++{
++      int reg_val;
++      if (on) {
++              ac101_write(codec, 0xb5, 0xA080);
++              reg_val = ac101_read(codec, MOD_CLK_ENA);
++              reg_val |= (0x1<<6);
++              ac101_write(codec, MOD_CLK_ENA, reg_val);
++              reg_val = ac101_read(codec, MOD_RST_CTRL);
++              reg_val |= (0x1<<6);
++              ac101_write(codec, MOD_RST_CTRL, reg_val);
++
++              reg_val = ac101_read(codec, 0xa0);
++              reg_val |= (0x7<<0);
++              ac101_write(codec, 0xa0, reg_val);
++      } else {
++              ac101_write(codec, 0xb5, 0x0);
++              reg_val = ac101_read(codec, MOD_CLK_ENA);
++              reg_val &= ~(0x1<<6);
++              ac101_write(codec, MOD_CLK_ENA, reg_val);
++              reg_val = ac101_read(codec, MOD_RST_CTRL);
++              reg_val &= ~(0x1<<6);
++              ac101_write(codec, MOD_RST_CTRL, reg_val);
++
++              reg_val = ac101_read(codec, 0xa0);
++              reg_val &= ~(0x7<<0);
++              ac101_write(codec, 0xa0, reg_val);
++      }
++}
++
++void set_configuration(struct snd_soc_codec *codec)
++{
++      if (speaker_double_used) {
++              ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL),
++                                (double_speaker_val<<SPK_VOL));
++      } else {
++              ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<<SPK_VOL),
++                                (single_speaker_val<<SPK_VOL));
++      }
++      ac101_update_bits(codec, HPOUT_CTRL, (0x3f<<HP_VOL), (headset_val<<HP_VOL));
++      ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC1G), (mainmic_val<<ADC_MIC1G));
++      ac101_update_bits(codec, ADC_SRCBST_CTRL, (0x7<<ADC_MIC2G), (headsetmic_val<<ADC_MIC2G));
++      if (dmic_used) {
++              ac101_write(codec, ADC_VOL_CTRL, adc_digital_val);
++      }
++      if (drc_used) {
++              drc_config(codec);
++      }
++      /*headphone calibration clock frequency select*/
++      ac101_update_bits(codec, SPKOUT_CTRL, (0x7<<HPCALICKS), (0x7<<HPCALICKS));
++
++      /* I2S1 DAC Timeslot 0 data <- I2S1 DAC channel 0 */
++      // "AIF1IN0L Mux" <= "AIF1DACL"
++      // "AIF1IN0R Mux" <= "AIF1DACR"
++      ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0L_SRC, 0x0 << AIF1_DA0L_SRC);
++      ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_SRC, 0x0 << AIF1_DA0R_SRC);
++      /* Timeslot 0 Left & Right Channel enable */
++      ac101_update_bits(codec, AIF1_DACDAT_CTRL, 0x3 << AIF1_DA0R_ENA, 0x3 << AIF1_DA0R_ENA);
++
++      /* DAC Digital Mixer Source Select <- I2S1 DA0 */
++      // "DACL Mixer" += "AIF1IN0L Mux"
++      // "DACR Mixer" += "AIF1IN0R Mux"
++      ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACL_MXR_ADCL, 0x8 << DACL_MXR_ADCL);
++      ac101_update_bits(codec, DAC_MXR_SRC, 0xF << DACR_MXR_ADCR, 0x8 << DACR_MXR_ADCR);
++      /* Internal DAC Analog Left & Right Channel enable */
++      ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << DACALEN, 0x3 << DACALEN);
++
++      /* Output Mixer Source Select */
++      // "Left Output Mixer"  += "DACL Mixer"
++      // "Right Output Mixer" += "DACR Mixer"
++      ac101_update_bits(codec, OMIXER_SR, 0x1 << LMIXMUTEDACL, 0x1 << LMIXMUTEDACL);
++      ac101_update_bits(codec, OMIXER_SR, 0x1 << RMIXMUTEDACR, 0x1 << RMIXMUTEDACR);
++      /* Left & Right Analog Output Mixer enable */
++      ac101_update_bits(codec, OMIXER_DACA_CTRL, 0x3 << LMIXEN, 0x3 << LMIXEN);
++
++      /* Headphone Ouput Control */ 
++      // "HP_R Mux" <= "DACR Mixer"
++      // "HP_L Mux" <= "DACL Mixer"
++      ac101_update_bits(codec, HPOUT_CTRL, 0x1 << LHPS, 0x0 << LHPS);
++      ac101_update_bits(codec, HPOUT_CTRL, 0x1 << RHPS, 0x0 << RHPS);
++
++      /* Speaker Output Control */
++      // "SPK_L Mux" <= "SPK_LR Adder"
++      // "SPK_R Mux" <= "SPK_LR Adder"
++      ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPKS) | (0x1 << RSPKS),
++                        (0x1 << LSPKS) | (0x1 << RSPKS));
++      /* Enable Left & Right Speaker */
++      ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
++                        (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
++      return;
++}
++
++static int late_enable_dac(struct snd_soc_codec* codec, int event) {
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      mutex_lock(&ac10x->dac_mutex);
++      switch (event) {
++      case SND_SOC_DAPM_PRE_PMU:
++              AC101_DBG();
++              if (ac10x->dac_enable == 0){
++                      /*enable dac module clk*/
++                      ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG),
++                                        (0x1<<MOD_CLK_DAC_DIG));
++                      ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG),
++                                        (0x1<<MOD_RESET_DAC_DIG));
++                      ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x1<<ENDA));
++                      ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x1<<ENHPF));
++              }
++              ac10x->dac_enable++;
++              break;
++      case SND_SOC_DAPM_POST_PMD:
++              if (ac10x->dac_enable != 0){
++                      ac10x->dac_enable = 0;
++
++                      ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENHPF),(0x0<<ENHPF));
++                      ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<<ENDA), (0x0<<ENDA));
++                      /*disable dac module clk*/
++                      ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_DAC_DIG),
++                                        (0x0<<MOD_CLK_DAC_DIG));
++                      ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_DAC_DIG),
++                                        (0x0<<MOD_RESET_DAC_DIG));
++              }
++              break;
++      }
++      mutex_unlock(&ac10x->dac_mutex);
++      return 0;
++}
++
++static int ac101_headphone_event(struct snd_soc_codec* codec, int event) {
++      switch (event) {
++      case SND_SOC_DAPM_POST_PMU:
++              /*open*/
++              AC101_DBG("post:open\n");
++              ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
++                                (0xf<<HPOUTPUTENABLE));
++              msleep(10);
++              ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x1<<HPPA_EN));
++              ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x3<<LHPPA_MUTE));
++              break;
++      case SND_SOC_DAPM_PRE_PMD:
++              /*close*/
++              AC101_DBG("pre:close\n");
++              ac101_update_bits(codec, HPOUT_CTRL, (0x3<<LHPPA_MUTE), (0x0<<LHPPA_MUTE));
++              msleep(10);
++              ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
++                                (0x0<<HPOUTPUTENABLE));
++              ac101_update_bits(codec, HPOUT_CTRL, (0x1<<HPPA_EN), (0x0<<HPPA_EN));
++              break;
++      }
++      return 0;
++}
++
++static int ac101_sysclk_started(void) {
++      int reg_val;
++
++      reg_val = ac101_read(static_ac10x->codec, SYSCLK_CTRL);
++      return (reg_val & (0x1<<SYSCLK_ENA));
++}
++
++static int ac101_aif1clk(struct snd_soc_codec* codec, int event, int quick) {
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int ret = 0;
++
++      switch (event) {
++      case SND_SOC_DAPM_PRE_PMU:
++              if (ac10x->aif1_clken == 0){
++                      ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),
++                                              (0x1<<AIF1CLK_ENA));
++                      if(!quick || _MASTER_MULTI_CODEC != _MASTER_AC101) {
++                              /* enable aif1clk & sysclk */
++                              ret = ret || ac101_update_bits(codec, MOD_CLK_ENA,
++                                                             (0x1<<MOD_CLK_AIF1),
++                                                             (0x1<<MOD_CLK_AIF1));
++                              ret = ret || ac101_update_bits(codec, MOD_RST_CTRL,
++                                                             (0x1<<MOD_RESET_AIF1),
++                                                             (0x1<<MOD_RESET_AIF1));
++                      }
++                      ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA),
++                                                     (0x1<<SYSCLK_ENA));
++
++                      if (ret) {
++                              AC101_DBG("start sysclk failed\n");
++                      } else {
++                              AC101_DBG("hw sysclk enable\n");
++                              ac10x->aif1_clken++;
++                      }
++              }
++              break;
++      case SND_SOC_DAPM_POST_PMD:
++              if (ac10x->aif1_clken != 0) {
++                      /* disable aif1clk & sysclk */
++                      ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<AIF1CLK_ENA),
++                                              (0x0<<AIF1CLK_ENA));
++                      ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, (0x1<<MOD_CLK_AIF1),
++                                                     (0x0<<MOD_CLK_AIF1));
++                      ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1),
++                                                     (0x0<<MOD_RESET_AIF1));
++                      ret = ret || ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<SYSCLK_ENA),
++                                                     (0x0<<SYSCLK_ENA));
++
++                      if (ret) {
++                              AC101_DBG("stop sysclk failed\n");
++                      } else {
++                              AC101_DBG("hw sysclk disable\n");
++                              ac10x->aif1_clken = 0;
++                      }
++                      break;
++              }
++      }
++
++      AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU,
++                SND_SOC_DAPM_POST_PMD);
++
++      return ret;
++}
++
++/**
++ * snd_ac101_get_volsw - single mixer get callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to get the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac101_get_volsw(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_value *ucontrol
++){
++      struct soc_mixer_control *mc =
++              (struct soc_mixer_control *)kcontrol->private_value;
++      unsigned int val, mask = (1 << fls(mc->max)) - 1;
++      unsigned int invert = mc->invert;
++      int ret;
++
++      if ((ret = ac101_read(static_ac10x->codec, mc->reg)) < 0)
++              return ret;
++
++      val = (ret >> mc->shift) & mask;
++      ucontrol->value.integer.value[0] = val - mc->min;
++      if (invert) {
++              ucontrol->value.integer.value[0] =
++                      mc->max - ucontrol->value.integer.value[0];
++      }
++
++      if (snd_soc_volsw_is_stereo(mc)) {
++              val = (ret >> mc->rshift) & mask;
++              ucontrol->value.integer.value[1] = val - mc->min;
++              if (invert) {
++                      ucontrol->value.integer.value[1] =
++                              mc->max - ucontrol->value.integer.value[1];
++              }
++      }
++      return 0;
++}
++
++/**
++ * snd_ac101_put_volsw - single mixer put callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to set the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac101_put_volsw(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_value *ucontrol
++){
++      struct soc_mixer_control *mc =
++              (struct soc_mixer_control *)kcontrol->private_value;
++      unsigned int sign_bit = mc->sign_bit;
++      unsigned int val, mask = (1 << fls(mc->max)) - 1;
++      unsigned int invert = mc->invert;
++      int ret;
++
++      if (sign_bit)
++              mask = BIT(sign_bit + 1) - 1;
++
++      val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
++      if (invert) {
++              val = mc->max - val;
++      }
++
++      ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->shift, val << mc->shift);
++
++      if (! snd_soc_volsw_is_stereo(mc)) {
++              return ret;
++      }
++      val = ((ucontrol->value.integer.value[1] + mc->min) & mask);
++      if (invert) {
++              val = mc->max - val;
++      }
++
++      ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->rshift,
++                              val << mc->rshift);
++      return ret;
++}
++
++
++static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0);
++static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0);
++static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0);
++static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0);
++static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0);
++
++static struct snd_kcontrol_new ac101_controls[] = {
++      /*DAC*/
++      SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv),
++      SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN,
++                     0xf, 0, dac_mix_vol_tlv),
++      SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv),
++      SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv),
++      SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv),
++};
++
++/* PLL divisors */
++struct pll_div {
++      unsigned int pll_in;
++      unsigned int pll_out;
++      int m;
++      int n_i;
++      int n_f;
++};
++
++struct aif1_fs {
++      unsigned samp_rate;
++      int bclk_div;
++      int srbit;
++      #define _SERIES_24_576K         0
++      #define _SERIES_22_579K         1
++      int series;
++};
++
++struct kv_map {
++      int val;
++      int bit;
++};
++
++/*
++ * Note : pll code from original tdm/i2s driver.
++ * freq_out = freq_in * N/(M*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2;
++ *            N_i[0,1023], N_f_factor[0,7], m[1,64]=REG_VAL[1-63,0]
++ */
++static const struct pll_div codec_pll_div[] = {
++      {128000,   _FREQ_22_579K,  1, 529, 1},
++      {192000,   _FREQ_22_579K,  1, 352, 4},
++      {256000,   _FREQ_22_579K,  1, 264, 3},
++      {384000,   _FREQ_22_579K,  1, 176, 2}, /*((176+2*0.2)*6000000)/(38*(2*1+1))*/
++      {1411200,  _FREQ_22_579K,  1,  48, 0},
++      {2822400,  _FREQ_22_579K,  1,  24, 0}, /* accurate, 11025 * 256 */
++      {5644800,  _FREQ_22_579K,  1,  12, 0}, /* accurate, 22050 * 256 */
++      {6000000,  _FREQ_22_579K, 38, 429, 0}, /*((429+0*0.2)*6000000)/(38*(2*1+1))*/
++      {11289600, _FREQ_22_579K,  1,   6, 0}, /* accurate, 44100 * 256 */
++      {13000000, _FREQ_22_579K, 19,  99, 0},
++      {19200000, _FREQ_22_579K, 25,  88, 1},
++      {24000000, _FREQ_22_579K, 63, 177, 4}, /* 22577778 Hz */
++
++      {128000,   _FREQ_24_576K,  1, 576, 0},
++      {192000,   _FREQ_24_576K,  1, 384, 0},
++      {256000,   _FREQ_24_576K,  1, 288, 0},
++      {384000,   _FREQ_24_576K,  1, 192, 0},
++      {2048000,  _FREQ_24_576K,  1,  36, 0}, /* accurate,  8000 * 256 */
++      {3072000,  _FREQ_24_576K,  1,  24, 0}, /* accurate, 12000 * 256 */
++      {4096000,  _FREQ_24_576K,  1,  18, 0}, /* accurate, 16000 * 256 */
++      {6000000,  _FREQ_24_576K, 25, 307, 1},
++      {6144000,  _FREQ_24_576K,  4,  48, 0}, /* accurate, 24000 * 256 */
++      {12288000, _FREQ_24_576K,  8,  48, 0}, /* accurate, 48000 * 256 */
++      {13000000, _FREQ_24_576K, 42, 238, 1},
++      {19200000, _FREQ_24_576K, 25,  96, 0},
++      {24000000, _FREQ_24_576K, 25,  76, 4}, /* accurate */
++
++      {_FREQ_22_579K, _FREQ_22_579K,  8,  24, 0}, /* accurate, 88200 * 256 */
++      {_FREQ_24_576K, _FREQ_24_576K,  8,  24, 0}, /* accurate, 96000 * 256 */
++};
++
++static const struct aif1_fs codec_aif1_fs[] = {
++      {8000, 12, 0},
++      {11025, 8, 1, _SERIES_22_579K},
++      {12000, 8, 2},
++      {16000, 6, 3},
++      {22050, 4, 4, _SERIES_22_579K},
++      {24000, 4, 5},
++      /* {32000, 3, 6}, dividing by 3 is not support */
++      {44100, 2, 7, _SERIES_22_579K},
++      {48000, 2, 8},
++      {96000, 1, 9},
++};
++
++static const struct kv_map codec_aif1_lrck[] = {
++      {16, 0},
++      {32, 1},
++      {64, 2},
++      {128, 3},
++      {256, 4},
++};
++
++static const struct kv_map codec_aif1_wsize[] = {
++      {8, 0},
++      {16, 1},
++      {20, 2},
++      {24, 3},
++      {32, 3},
++};
++
++static const unsigned ac101_bclkdivs[] = {
++        1,   2,   4,   6,
++        8,  12,  16,  24,
++       32,  48,  64,  96,
++      128, 192,   0,   0,
++};
++
++static int ac101_aif_play(struct ac10x_priv* ac10x) {
++      struct snd_soc_codec * codec = ac10x->codec;
++
++      late_enable_dac(codec, SND_SOC_DAPM_PRE_PMU);
++      ac101_headphone_event(codec, SND_SOC_DAPM_POST_PMU);
++      if (drc_used) {
++              drc_enable(codec, 1);
++      }
++
++      /* Enable Left & Right Speaker */
++      ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
++                        (0x1 << LSPK_EN) | (0x1 << RSPK_EN));
++      if (ac10x->gpiod_spk_amp_gate) {
++              gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1);
++      }
++      return 0;
++}
++
++static void ac10x_work_aif_play(struct work_struct *work) {
++      struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work);
++
++      ac101_aif_play(ac10x);
++      return;
++}
++
++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute)
++{
++      struct snd_soc_codec *codec = codec_dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      AC101_DBG("mute=%d\n",  mute);
++
++      ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0);
++
++      if (!mute) {
++              #if _MASTER_MULTI_CODEC != _MASTER_AC101
++              /* enable global clock */
++              ac10x->aif1_clken = 0;
++              ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
++              ac101_aif_play(ac10x);
++              #else
++              schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50));
++              #endif
++      } else {
++              #if _MASTER_MULTI_CODEC == _MASTER_AC101
++              cancel_delayed_work_sync(&ac10x->dlywork);
++              #endif
++
++              if (ac10x->gpiod_spk_amp_gate) {
++                      gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0);
++              }
++              /* Disable Left & Right Speaker */
++              ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN),
++                                (0x0 << LSPK_EN) | (0x0 << RSPK_EN));
++              if (drc_used) {
++                      drc_enable(codec, 0);
++              }
++              ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD);
++              late_enable_dac(codec, SND_SOC_DAPM_POST_PMD);
++
++              #if _MASTER_MULTI_CODEC != _MASTER_AC101
++              ac10x->aif1_clken = 1;
++              ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
++              #endif
++      }
++      return 0;
++}
++
++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai)
++{
++      struct snd_soc_codec *codec = codec_dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n", 
++              snd_pcm_stream_str(substream),
++              codec_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active,
++              codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active,
++              snd_soc_dai_active(codec_dai));
++
++      if (!snd_soc_dai_active(codec_dai)) {
++              ac10x->aif1_clken = 1;
++              ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
++      } else {
++              ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0);
++      }
++}
++
++static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source,
++                      unsigned int freq_in, unsigned int freq_out)
++{
++      struct snd_soc_codec *codec = codec_dai->codec;
++      int i, m, n_i, n_f;
++
++      AC101_DBG("pll_id:%d\n",  pll_id);
++
++      /* clear volatile reserved bits*/
++      ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0);
++
++      /* select aif1 clk srouce from mclk1 */
++      ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x0<<AIF1CLK_SRC));
++      /* disable pll */
++      ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (0<<PLL_EN));
++
++      if (!freq_out)
++              return 0;
++      if ((freq_in < 128000) || (freq_in > _FREQ_24_576K)) {
++              return -EINVAL;
++      } else if ((freq_in == _FREQ_24_576K) || (freq_in == _FREQ_22_579K)) {
++              if (pll_id == AC101_MCLK1) {
++                      /*select aif1 clk source from mclk1*/
++                      ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC),
++                                        (0x0<<AIF1CLK_SRC));
++                      return 0;
++              }
++      }
++
++      switch (pll_id) {
++      case AC101_MCLK1:
++              /*pll source from MCLK1*/
++              ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x0<<PLLCLK_SRC));
++              break;
++      case AC101_BCLK1:
++              /*pll source from BCLK1*/
++              ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<PLLCLK_SRC), (0x2<<PLLCLK_SRC));
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      /* freq_out = freq_in * n/(m*(2k+1)) , k=1,N=N_i+N_f */
++      for (i = m = n_i = n_f = 0; i < ARRAY_SIZE(codec_pll_div); i++) {
++              if ((codec_pll_div[i].pll_in == freq_in) &&
++                  (codec_pll_div[i].pll_out == freq_out)) {
++                      m   = codec_pll_div[i].m;
++                      n_i = codec_pll_div[i].n_i;
++                      n_f = codec_pll_div[i].n_f;
++                      break;
++              }
++      }
++      /* config pll m */
++      if (m  == 64) m = 0;
++      ac101_update_bits(codec, PLL_CTRL1, (0x3f<<PLL_POSTDIV_M), (m<<PLL_POSTDIV_M));
++      /* config pll n */
++      ac101_update_bits(codec, PLL_CTRL2, (0x3ff<<PLL_PREDIV_NI), (n_i<<PLL_PREDIV_NI));
++      ac101_update_bits(codec, PLL_CTRL2, (0x7<<PLL_POSTDIV_NF), (n_f<<PLL_POSTDIV_NF));
++      /* enable pll */
++      ac101_update_bits(codec, PLL_CTRL2, (0x1<<PLL_EN), (1<<PLL_EN));
++      ac101_update_bits(codec, SYSCLK_CTRL, (0x1<<PLLCLK_ENA),  (0x1<<PLLCLK_ENA));
++      ac101_update_bits(codec, SYSCLK_CTRL, (0x3<<AIF1CLK_SRC), (0x3<<AIF1CLK_SRC));
++
++      return 0;
++}
++
++int ac101_hw_params(struct snd_pcm_substream *substream,
++      struct snd_pcm_hw_params *params,
++      struct snd_soc_dai *codec_dai)
++{
++      int i = 0;
++      int AIF_CLK_CTRL = AIF1_CLK_CTRL;
++      int aif1_word_size = 24;
++      int aif1_slot_size = 32;
++      int aif1_lrck_div;
++      struct snd_soc_codec *codec = codec_dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int reg_val, freq_out;
++      unsigned channels;
++
++      AC101_DBG("+++\n");
++
++      if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) {
++              /* not configure hw_param twice if stream is playback, tell the caller it's started */
++              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++                      return 1;
++              }
++      }
++
++      /* get channels count & slot size */
++      channels = params_channels(params);
++
++      switch (params_format(params)) {
++      case SNDRV_PCM_FORMAT_S24_LE:
++      case SNDRV_PCM_FORMAT_S32_LE:
++              aif1_slot_size = 32;
++              break;
++      case SNDRV_PCM_FORMAT_S16_LE:
++      default:
++              aif1_slot_size = 16;
++              break;
++      }
++
++      /* set LRCK/BCLK ratio */
++      aif1_lrck_div = aif1_slot_size * channels;
++      for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) {
++              if (codec_aif1_lrck[i].val == aif1_lrck_div) {
++                      break;
++              }
++      }
++      ac101_update_bits(codec, AIF_CLK_CTRL, (0x7 << AIF1_LRCK_DIV),
++                        codec_aif1_lrck[i].bit << AIF1_LRCK_DIV);
++
++      /* set PLL output freq */
++      freq_out = _FREQ_24_576K;
++      for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) {
++              if (codec_aif1_fs[i].samp_rate == params_rate(params)) {
++                      if (codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && dmic_used &&
++                          codec_aif1_fs[i].samp_rate == 44100) {
++                              ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS),
++                                                (0x4<<AIF1_FS));
++                      } else {
++                              ac101_update_bits(codec, AIF_SR_CTRL, (0xf<<AIF1_FS),
++                                                ((codec_aif1_fs[i].srbit)<<AIF1_FS));
++                      }
++                      if (codec_aif1_fs[i].series == _SERIES_22_579K)
++                              freq_out = _FREQ_22_579K;
++                      break;
++              }
++      }
++
++      /* set I2S word size */
++      for (i = 0; i < ARRAY_SIZE(codec_aif1_wsize); i++) {
++              if (codec_aif1_wsize[i].val == aif1_word_size) {
++                      break;
++              }
++      }
++      ac101_update_bits(codec, AIF_CLK_CTRL, (0x3<<AIF1_WORK_SIZ),
++                        ((codec_aif1_wsize[i].bit)<<AIF1_WORK_SIZ));
++
++      /* set TDM slot size */
++      if ((reg_val = codec_aif1_wsize[i].bit) > 2) reg_val = 2;
++      ac101_update_bits(codec, AIF1_ADCDAT_CTRL, 0x3 << AIF1_SLOT_SIZ, reg_val << AIF1_SLOT_SIZ);
++
++      /* setting pll if it's master mode */
++      reg_val = ac101_read(codec, AIF_CLK_CTRL);
++      if ((reg_val & (0x1 << AIF1_MSTR_MOD)) == 0) {
++              unsigned bclkdiv;
++
++              ac101_set_pll(codec_dai, AC101_MCLK1, 0, ac10x->sysclk, freq_out);
++
++              bclkdiv = freq_out / (aif1_lrck_div * params_rate(params));
++              for (i = 0; i < ARRAY_SIZE(ac101_bclkdivs) - 1; i++) {
++                      if (ac101_bclkdivs[i] >= bclkdiv) {
++                              break;
++                      }
++              }
++              ac101_update_bits(codec, AIF_CLK_CTRL, (0xf<<AIF1_BCLK_DIV), i<<AIF1_BCLK_DIV);
++      } else {
++              /* set pll clock source to BCLK if slave mode */
++              ac101_set_pll(codec_dai, AC101_BCLK1, 0, aif1_lrck_div * params_rate(params),
++                            freq_out);
++      }
++
++      #if _MASTER_MULTI_CODEC == _MASTER_AC101
++      /* Master mode, to clear cpu_dai fifos, disable output bclk & lrck */
++      ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0);
++      #endif
++
++      AC101_DBG("rate: %d , channels: %d , samp_res: %d",
++              params_rate(params), channels, aif1_slot_size);
++
++      AC101_DBG("---\n");
++      return 0;
++}
++
++int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
++{
++      int reg_val;
++      int AIF_CLK_CTRL = AIF1_CLK_CTRL;
++      struct snd_soc_codec *codec = codec_dai->codec;
++
++      AC101_DBG();
++
++      /*
++       *      master or slave selection
++       *      0 = Master mode
++       *      1 = Slave mode
++       */
++      reg_val = ac101_read(codec, AIF_CLK_CTRL);
++      reg_val &= ~(0x1<<AIF1_MSTR_MOD);
++      switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++      case SND_SOC_DAIFMT_CBM_CFM:   /* codec clk & frm master, ap is slave*/
++              #if _MASTER_MULTI_CODEC == _MASTER_AC101
++              pr_warn("AC101 as Master\n");
++              reg_val |= (0x0<<AIF1_MSTR_MOD);
++              break;
++              #else
++              pr_warn("AC108 as Master\n");
++              #endif
++      case SND_SOC_DAIFMT_CBS_CFS:   /* codec clk & frm slave, ap is master*/
++              pr_warn("AC101 as Slave\n");
++              reg_val |= (0x1<<AIF1_MSTR_MOD);
++              break;
++      default:
++              pr_err("unknwon master/slave format\n");
++              return -EINVAL;
++      }
++
++      /*
++       * Enable TDM mode
++       */
++      reg_val |=  (0x1 << AIF1_TDMM_ENA);
++      ac101_write(codec, AIF_CLK_CTRL, reg_val);
++
++      /* i2s mode selection */
++      reg_val = ac101_read(codec, AIF_CLK_CTRL);
++      reg_val&=~(3<<AIF1_DATA_FMT);
++      switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK){
++      case SND_SOC_DAIFMT_I2S:        /* I2S1 mode */
++              reg_val |= (0x0<<AIF1_DATA_FMT);
++              break;
++      case SND_SOC_DAIFMT_RIGHT_J:    /* Right Justified mode */
++              reg_val |= (0x2<<AIF1_DATA_FMT);
++              break;
++      case SND_SOC_DAIFMT_LEFT_J:     /* Left Justified mode */
++              reg_val |= (0x1<<AIF1_DATA_FMT);
++              break;
++      case SND_SOC_DAIFMT_DSP_A:      /* L reg_val msb after FRM LRC */
++              reg_val |= (0x3<<AIF1_DATA_FMT);
++              break;
++      case SND_SOC_DAIFMT_DSP_B:
++              /* TODO: data offset set to 0 */
++              reg_val |= (0x3<<AIF1_DATA_FMT);
++              break;
++      default:
++              pr_err("%s, line:%d\n", __func__, __LINE__);
++              return -EINVAL;
++      }
++      ac101_write(codec, AIF_CLK_CTRL, reg_val);
++
++      /* DAI signal inversions */
++      reg_val = ac101_read(codec, AIF_CLK_CTRL);
++      switch(fmt & SND_SOC_DAIFMT_INV_MASK){
++      case SND_SOC_DAIFMT_NB_NF:     /* normal bit clock + nor frame */
++              reg_val &= ~(0x1<<AIF1_LRCK_INV);
++              reg_val &= ~(0x1<<AIF1_BCLK_INV);
++              break;
++      case SND_SOC_DAIFMT_NB_IF:     /* normal bclk + inv frm */
++              reg_val |= (0x1<<AIF1_LRCK_INV);
++              reg_val &= ~(0x1<<AIF1_BCLK_INV);
++              break;
++      case SND_SOC_DAIFMT_IB_NF:     /* invert bclk + nor frm */
++              reg_val &= ~(0x1<<AIF1_LRCK_INV);
++              reg_val |= (0x1<<AIF1_BCLK_INV);
++              break;
++      case SND_SOC_DAIFMT_IB_IF:     /* invert bclk + inv frm */
++              reg_val |= (0x1<<AIF1_LRCK_INV);
++              reg_val |= (0x1<<AIF1_BCLK_INV);
++              break;
++      }
++      ac101_write(codec, AIF_CLK_CTRL, reg_val);
++
++      return 0;
++}
++
++int ac101_audio_startup(struct snd_pcm_substream *substream,
++      struct snd_soc_dai *codec_dai)
++{
++      // struct snd_soc_codec *codec = codec_dai->codec;
++
++      AC101_DBG("\n\n\n");
++
++      if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
++      }
++      return 0;
++}
++
++int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
++                struct snd_soc_dai *dai)
++{
++      struct snd_soc_codec *codec = dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int ret = 0;
++
++      AC101_DBG("stream=%s  cmd=%d\n",
++              snd_pcm_stream_str(substream),
++              cmd);
++
++      switch (cmd) {
++      case SNDRV_PCM_TRIGGER_START:
++      case SNDRV_PCM_TRIGGER_RESUME:
++      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++              #if _MASTER_MULTI_CODEC == _MASTER_AC101
++              if (ac10x->aif1_clken == 0){
++                      /*
++                       * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable'
++                       * Or else the two AC108 chips lost the sync.
++                       */
++                      ret = 0;
++                      ret = ret || ac101_update_bits(codec, MOD_CLK_ENA,
++                                                     (0x1<<MOD_CLK_AIF1), (0x1<<MOD_CLK_AIF1));
++                      ret = ret || ac101_update_bits(codec, MOD_RST_CTRL, (0x1<<MOD_RESET_AIF1),
++                                                     (0x1<<MOD_RESET_AIF1));
++              }
++              #endif
++              break;
++      case SNDRV_PCM_TRIGGER_STOP:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++              break;
++      default:
++              ret = -EINVAL;
++      }
++      return ret;
++}
++
++#if 0
++static int ac101_set_dai_sysclk(struct snd_soc_dai *codec_dai,
++                                int clk_id, unsigned int freq, int dir)
++{
++      struct snd_soc_codec *codec = codec_dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      AC101_DBG("id=%d freq=%d, dir=%d\n", 
++              clk_id, freq, dir);
++
++      ac10x->sysclk = freq;
++
++      return 0;
++}
++
++static const struct snd_soc_dai_ops ac101_aif1_dai_ops = {
++      //.startup      = ac101_audio_startup,
++      //.shutdown     = ac101_aif_shutdown,
++      //.set_sysclk   = ac101_set_dai_sysclk,
++      //.set_pll      = ac101_set_pll,
++      //.set_fmt      = ac101_set_dai_fmt,
++      //.hw_params    = ac101_hw_params,
++      //.trigger      = ac101_trigger,
++      //.digital_mute = ac101_aif_mute,
++};
++
++static struct snd_soc_dai_driver ac101_dai[] = {
++      {
++              .name = "ac10x-aif1",
++              .id = AIF1_CLK,
++              .playback = {
++                      .stream_name = "Playback",
++                      .channels_min = 1,
++                      .channels_max = 8,
++                      .rates = AC101_RATES,
++                      .formats = AC101_FORMATS,
++              },
++              #if 0
++              .capture = {
++                      .stream_name = "Capture",
++                      .channels_min = 1,
++                      .channels_max = 8,
++                      .rates = AC101_RATES,
++                      .formats = AC101_FORMATS,
++              },
++              #endif
++              .ops = &ac101_aif1_dai_ops,
++      }
++};
++#endif
++
++static void codec_resume_work(struct work_struct *work)
++{
++      struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume);
++      struct snd_soc_codec *codec = ac10x->codec;
++
++      AC101_DBG("+++\n");
++
++      set_configuration(codec);
++      if (drc_used) {
++              drc_config(codec);
++      }
++      /*enable this bit to prevent leakage from ldoin*/
++      ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
++
++      AC101_DBG("---\n");
++      return;
++}
++
++int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
++{
++      switch (level) {
++      case SND_SOC_BIAS_ON:
++              AC101_DBG("SND_SOC_BIAS_ON\n");
++              break;
++      case SND_SOC_BIAS_PREPARE:
++              AC101_DBG("SND_SOC_BIAS_PREPARE\n");
++              break;
++      case SND_SOC_BIAS_STANDBY:
++              AC101_DBG("SND_SOC_BIAS_STANDBY\n");
++              #ifdef CONFIG_AC101_SWITCH_DETECT
++              switch_hw_config(codec);
++              #endif
++              break;
++      case SND_SOC_BIAS_OFF:
++              #ifdef CONFIG_AC101_SWITCH_DETECT
++              ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASEN), (0<<HBIASEN));
++              ac101_update_bits(codec, ADC_APC_CTRL, (0x1<<HBIASADCEN), (0<<HBIASADCEN));
++              #endif
++              ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<<HPOUTPUTENABLE),
++                                (0<<HPOUTPUTENABLE));
++              ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0<<OSCEN));
++              AC101_DBG("SND_SOC_BIAS_OFF\n");
++              break;
++      }
++      snd_soc_codec_get_dapm(codec)->bias_level = level;
++      return 0;
++}
++
++int ac101_codec_probe(struct snd_soc_codec *codec)
++{
++      int ret = 0;
++      struct ac10x_priv *ac10x;
++
++      ac10x = dev_get_drvdata(codec->dev);
++      if (ac10x == NULL) {
++              AC101_DBG("not set client data!\n");
++              return -ENOMEM;
++      }
++      ac10x->codec = codec;
++
++      INIT_DELAYED_WORK(&ac10x->dlywork, ac10x_work_aif_play);
++      INIT_WORK(&ac10x->codec_resume, codec_resume_work);
++      ac10x->dac_enable = 0;
++      ac10x->aif1_clken = 0;
++      mutex_init(&ac10x->dac_mutex);
++
++      set_configuration(ac10x->codec);
++
++      /*enable this bit to prevent leakage from ldoin*/
++      ac101_update_bits(codec, ADDA_TUNE3, (0x1<<OSCEN), (0x1<<OSCEN));
++      ac101_write(codec, DAC_VOL_CTRL, 0);
++
++      /* customized get/put inteface */
++      for (ret = 0; ret < ARRAY_SIZE(ac101_controls); ret++) {
++              struct snd_kcontrol_new* skn = &ac101_controls[ret];
++
++              skn->get = snd_ac101_get_volsw;
++              skn->put = snd_ac101_put_volsw;
++      }
++      ret = snd_soc_add_codec_controls(codec, ac101_controls, ARRAY_SIZE(ac101_controls));
++      if (ret) {
++              pr_err("[ac10x] Failed to register audio mode control, "
++                              "will continue without it.\n");
++      }
++
++      #ifdef CONFIG_AC101_SWITCH_DETECT
++      ret = ac101_switch_probe(ac10x);
++      if (ret) {
++              // not care the switch return value
++      }
++      #endif
++
++      return 0;
++}
++
++/* power down chip */
++int ac101_codec_remove(struct snd_soc_codec *codec)
++{
++      #ifdef CONFIG_AC101_SWITCH_DETECT
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      if (ac10x->irq) {
++              devm_free_irq(codec->dev, ac10x->irq, ac10x);
++              ac10x->irq = 0;
++      }
++
++      if (cancel_work_sync(&ac10x->work_switch) != 0) {
++      }
++
++      if (cancel_work_sync(&ac10x->work_clear_irq) != 0) {
++      }
++
++      if (ac10x->inpdev) {
++              input_unregister_device(ac10x->inpdev);
++              ac10x->inpdev = NULL;
++      }
++      #endif
++
++      return 0;
++}
++
++int ac101_codec_suspend(struct snd_soc_codec *codec)
++{
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      AC101_DBG("[codec]:suspend\n");
++      regcache_cache_only(ac10x->regmap101, true);
++      return 0;
++}
++
++int ac101_codec_resume(struct snd_soc_codec *codec)
++{
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int ret;
++
++      AC101_DBG("[codec]:resume");
++
++      /* Sync reg_cache with the hardware */
++      regcache_cache_only(ac10x->regmap101, false);
++      ret = regcache_sync(ac10x->regmap101);
++      if (ret != 0) {
++              dev_err(codec->dev, "Failed to sync register cache: %d\n", ret);
++              regcache_cache_only(ac10x->regmap101, true);
++              return ret;
++      }
++
++      #ifdef CONFIG_AC101_SWITCH_DETECT
++      ac10x->mode = HEADPHONE_IDLE;
++      ac10x->state = -1;
++      #endif
++
++      ac101_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
++      schedule_work(&ac10x->codec_resume);
++      return 0;
++}
++
++/***************************************************************************/
++static ssize_t ac101_debug_store(struct device *dev,
++      struct device_attribute *attr, const char *buf, size_t count)
++{
++      struct ac10x_priv *ac10x = dev_get_drvdata(dev);
++      int val = 0, flag = 0;
++      u16 value_w, value_r;
++      u8 reg, num, i=0;
++
++      val = simple_strtol(buf, NULL, 16);
++      flag = (val >> 24) & 0xF;
++      if (flag) {
++              reg = (val >> 16) & 0xFF;
++              value_w =  val & 0xFFFF;
++              ac101_write(ac10x->codec, reg, value_w);
++              printk("write 0x%x to reg:0x%x\n", value_w, reg);
++      } else {
++              reg = (val >> 8) & 0xFF;
++              num = val & 0xff;
++              printk("\n");
++              printk("read:start add:0x%x,count:0x%x\n", reg, num);
++
++              regcache_cache_bypass(ac10x->regmap101, true);
++              do {
++                      value_r = ac101_read(ac10x->codec, reg);
++                      printk("0x%x: 0x%04x ", reg++, value_r);
++                      if (++i % 4 == 0 || i == num)
++                              printk("\n");
++              } while (i < num);
++              regcache_cache_bypass(ac10x->regmap101, false);
++      }
++      return count;
++}
++static ssize_t ac101_debug_show(struct device *dev,
++      struct device_attribute *attr, char *buf)
++{
++      printk("echo flag|reg|val > ac10x\n");
++      printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n");
++      printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n");
++      return 0;
++}
++static DEVICE_ATTR(ac10x, 0644, ac101_debug_show, ac101_debug_store);
++
++static struct attribute *audio_debug_attrs[] = {
++      &dev_attr_ac10x.attr,
++      NULL,
++};
++
++static struct attribute_group audio_debug_attr_group = {
++      .name   = "ac101_debug",
++      .attrs  = audio_debug_attrs,
++};
++/***************************************************************************/
++
++/************************************************************/
++static bool ac101_volatile_reg(struct device *dev, unsigned int reg)
++{
++      switch (reg) {
++      case PLL_CTRL2:
++      case HMIC_STS:
++              return true;
++      }
++      return false;
++}
++
++static const struct regmap_config ac101_regmap = {
++      .reg_bits = 8,
++      .val_bits = 16,
++      .reg_stride = 1,
++      .max_register = 0xB5,
++      .cache_type = REGCACHE_FLAT,
++      .volatile_reg = ac101_volatile_reg,
++};
++
++/* Sync reg_cache from the hardware */
++int ac10x_fill_regcache(struct device* dev, struct regmap* map) {
++      int r, i, n;
++      int v;
++
++      n = regmap_get_max_register(map);
++      for (i = 0; i < n; i++) {
++              regcache_cache_bypass(map, true);
++              r = regmap_read(map, i, &v);
++              if (r) {
++                      dev_dbg(dev, "failed to read register %d\n", i);
++                      continue;
++              }
++              regcache_cache_bypass(map, false);
++
++              regcache_cache_only(map, true);
++              r = regmap_write(map, i, v);
++              regcache_cache_only(map, false);
++      }
++      regcache_cache_bypass(map, false);
++      regcache_cache_only(map, false);
++
++      return 0;
++}
++
++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
++{
++      struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
++      int ret = 0;
++      unsigned v = 0;
++
++      AC101_DBG();
++
++      static_ac10x = ac10x;
++
++      ac10x->regmap101 = devm_regmap_init_i2c(i2c, &ac101_regmap);
++      if (IS_ERR(ac10x->regmap101)) {
++              ret = PTR_ERR(ac10x->regmap101);
++              dev_err(&i2c->dev, "Fail to initialize I/O: %d\n", ret);
++              return ret;
++      }
++
++      /* Chip reset */
++      regcache_cache_only(ac10x->regmap101, false);
++      ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0);
++      msleep(50);
++
++      /* sync regcache for FLAT type */
++      ac10x_fill_regcache(&i2c->dev, ac10x->regmap101);
++
++      ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v);
++      if (ret < 0) {
++              dev_err(&i2c->dev, "failed to read vendor ID: %d\n", ret);
++              return ret;
++      }
++
++      if (v != AC101_CHIP_ID) {
++              dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v);
++              dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID);
++              return -ENODEV;
++      }
++
++      ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group);
++      if (ret) {
++              pr_err("failed to create attr group\n");
++      }
++
++      ac10x->gpiod_spk_amp_gate = devm_gpiod_get_optional(&i2c->dev, "spk-amp-switch",
++                                                          GPIOD_OUT_LOW);
++      if (IS_ERR(ac10x->gpiod_spk_amp_gate)) {
++              ac10x->gpiod_spk_amp_gate = NULL;
++              dev_err(&i2c->dev, "failed get spk-amp-switch in device tree\n");
++      }
++
++      return 0;
++}
++
++void ac101_shutdown(struct i2c_client *i2c)
++{
++      struct ac10x_priv *ac10x = i2c_get_clientdata(i2c);
++      struct snd_soc_codec *codec = ac10x->codec;
++      int reg_val;
++
++      if (codec == NULL) {
++              pr_err(": no sound card.\n");
++              return;
++      }
++
++      /*set headphone volume to 0*/
++      reg_val = ac101_read(codec, HPOUT_CTRL);
++      reg_val &= ~(0x3f<<HP_VOL);
++      ac101_write(codec, HPOUT_CTRL, reg_val);
++
++      /*disable pa*/
++      reg_val = ac101_read(codec, HPOUT_CTRL);
++      reg_val &= ~(0x1<<HPPA_EN);
++      ac101_write(codec, HPOUT_CTRL, reg_val);
++
++      /*hardware xzh support*/
++      reg_val = ac101_read(codec, OMIXER_DACA_CTRL);
++      reg_val &= ~(0xf<<HPOUTPUTENABLE);
++      ac101_write(codec, OMIXER_DACA_CTRL, reg_val);
++
++      /*unmute l/r headphone pa*/
++      reg_val = ac101_read(codec, HPOUT_CTRL);
++      reg_val &= ~((0x1<<RHPPA_MUTE)|(0x1<<LHPPA_MUTE));
++      ac101_write(codec, HPOUT_CTRL, reg_val);
++      return;
++}
++
++int ac101_remove(struct i2c_client *i2c)
++{
++      sysfs_remove_group(&i2c->dev.kobj, &audio_debug_attr_group);
++      return 0;
++}
++
++MODULE_DESCRIPTION("ASoC ac10x driver");
++MODULE_AUTHOR("huangxin,liushaohua");
++MODULE_AUTHOR("PeterYang<linsheng.yang@seeed.cc>");
+--- /dev/null
++++ b/sound/soc/codecs/ac101_regs.h
+@@ -0,0 +1,431 @@
++/*
++ * ac101_regs.h
++ *
++ * (C) Copyright 2017-2018
++ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
++ *
++ * PeterYang <linsheng.yang@seeed.cc>
++ *
++ * (C) Copyright 2010-2017
++ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
++ * huangxin <huangxin@reuuimllatech.com>
++ *
++ * some simple description for this code
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ */
++#ifndef __AC101_REGS_H__
++#define __AC101_REGS_H__
++
++/*pll source*/
++#define AC101_MCLK1 1
++#define AC101_MCLK2 2
++#define AC101_BCLK1 3
++#define AC101_BCLK2 4
++
++#define AIF1_CLK 1
++#define AIF2_CLK 2
++
++#define       CHIP_AUDIO_RST          0x0
++#define PLL_CTRL1             0x1
++#define PLL_CTRL2             0x2
++#define SYSCLK_CTRL           0x3
++#define MOD_CLK_ENA           0x4
++#define MOD_RST_CTRL          0x5
++#define AIF_SR_CTRL           0x6
++
++#define AIF1_CLK_CTRL         0x10
++#define AIF1_ADCDAT_CTRL      0x11
++#define AIF1_DACDAT_CTRL      0x12
++#define AIF1_MXR_SRC          0x13
++#define AIF1_VOL_CTRL1                0x14
++#define AIF1_VOL_CTRL2                0x15
++#define AIF1_VOL_CTRL3                0x16
++#define AIF1_VOL_CTRL4                0x17
++#define AIF1_MXR_GAIN         0x18
++#define AIF1_RXD_CTRL         0x19
++#define ADC_DIG_CTRL          0x40
++#define ADC_VOL_CTRL          0x41
++#define ADC_DBG_CTRL          0x42
++
++#define HMIC_CTRL1                    0x44
++#define HMIC_CTRL2                    0x45
++#define HMIC_STS                      0x46
++
++#define DAC_DIG_CTRL          0x48
++#define DAC_VOL_CTRL          0x49
++#define DAC_DBG_CTRL          0x4a
++#define DAC_MXR_SRC                   0x4c
++#define DAC_MXR_GAIN          0x4d
++
++#define ADC_APC_CTRL          0x50
++#define ADC_SRC                               0x51
++#define ADC_SRCBST_CTRL               0x52
++#define OMIXER_DACA_CTRL      0x53
++#define OMIXER_SR                     0x54
++#define OMIXER_BST1_CTRL      0x55
++#define HPOUT_CTRL                    0x56
++#define ESPKOUT_CTRL          0x57
++#define SPKOUT_CTRL                   0x58
++#define LOUT_CTRL                     0x59
++#define ADDA_TUNE1                    0x5a
++#define ADDA_TUNE2                    0x5b
++#define ADDA_TUNE3                    0x5c
++#define HPOUT_STR                     0x5d
++
++/*CHIP_AUDIO_RST*/
++#define AC101_CHIP_ID                 0x0101
++
++/*PLL_CTRL1*/
++#define DPLL_DAC_BIAS         14
++#define PLL_POSTDIV_M         8
++#define CLOSE_LOOP            6
++#define INT                   0
++
++/*PLL_CTRL2*/
++#define PLL_EN                        15
++#define PLL_LOCK_STATUS               14
++#define PLL_PREDIV_NI         4
++#define PLL_POSTDIV_NF                0
++
++/*SYSCLK_CTRL*/
++#define PLLCLK_ENA                    15
++#define PLLCLK_SRC                    12
++#define AIF1CLK_ENA                   11
++#define AIF1CLK_SRC                   8
++#define AIF2CLK_ENA                   7
++#define AIF2CLK_SRC                   4
++#define SYSCLK_ENA                    3
++#define SYSCLK_SRC                    0
++
++/*MOD_CLK_ENA*/
++#define MOD_CLK_AIF1          15
++#define MOD_CLK_AIF2          14
++#define MOD_CLK_AIF3          13
++#define MOD_CLK_SRC1          11
++#define MOD_CLK_SRC2          10
++#define MOD_CLK_HPF_AGC               7
++#define MOD_CLK_HPF_DRC               6
++#define MOD_CLK_ADC_DIG               3
++#define MOD_CLK_DAC_DIG               2
++
++/*MOD_RST_CTRL*/
++#define MOD_RESET_CTL         0
++#define MOD_RESET_AIF1                15
++#define MOD_RESET_AIF2                14
++#define MOD_RESET_AIF3                13
++#define MOD_RESET_SRC1                11
++#define MOD_RESET_SRC2                10
++#define MOD_RESET_HPF_AGC     7
++#define MOD_RESET_HPF_DRC     6
++#define MOD_RESET_ADC_DIG     3
++#define MOD_RESET_DAC_DIG     2
++
++/*AIF_SR_CTRL*/
++#define AIF1_FS                               12      //AIF1 Sample Rate
++#define AIF2_FS                               8       //AIF2 Sample Rate
++#define SRC1_ENA                      3
++#define SRC1_SRC                      2
++#define SRC2_ENA                      1
++#define SRC2_SRC                      0
++
++/*AIF1LCK_CTRL*/
++#define AIF1_MSTR_MOD         15
++#define AIF1_BCLK_INV         14
++#define AIF1_LRCK_INV         13
++#define AIF1_BCLK_DIV         9
++#define AIF1_LRCK_DIV         6
++#define AIF1_WORK_SIZ         4
++#define AIF1_DATA_FMT         2
++#define DSP_MONO_PCM          1
++#define AIF1_TDMM_ENA         0
++
++/*AIF1_ADCDAT_CTRL*/
++#define AIF1_AD0L_ENA         15
++#define AIF1_AD0R_ENA         14
++#define AIF1_AD1L_ENA         13
++#define AIF1_AD1R_ENA         12
++#define AIF1_AD0L_SRC         10
++#define AIF1_AD0R_SRC         8
++#define AIF1_AD1L_SRC         6
++#define AIF1_AD1R_SRC         4
++#define AIF1_ADCP_ENA         3
++#define AIF1_ADUL_ENA         2
++#define AIF1_SLOT_SIZ         0
++
++/*AIF1_DACDAT_CTRL*/
++#define AIF1_DA0L_ENA         15
++#define AIF1_DA0R_ENA         14
++#define AIF1_DA1L_ENA         13
++#define AIF1_DA1R_ENA         12
++#define AIF1_DA0L_SRC         10
++#define AIF1_DA0R_SRC         8
++#define AIF1_DA1L_SRC         6
++#define AIF1_DA1R_SRC         4
++#define AIF1_DACP_ENA         3
++#define AIF1_DAUL_ENA         2
++#define AIF1_SLOT_SIZ         0
++
++/*AIF1_MXR_SRC*/
++#define AIF1_AD0L_AIF1_DA0L_MXR               15
++#define AIF1_AD0L_AIF2_DACL_MXR               14
++#define AIF1_AD0L_ADCL_MXR            13
++#define AIF1_AD0L_AIF2_DACR_MXR               12
++#define AIF1_AD0R_AIF1_DA0R_MXR               11
++#define AIF1_AD0R_AIF2_DACR_MXR               10
++#define AIF1_AD0R_ADCR_MXR            9
++#define AIF1_AD0R_AIF2_DACL_MXR               8
++#define AIF1_AD1L_AIF2_DACL_MXR               7
++#define AIF1_AD1L_ADCL_MXR            6
++#define AIF1_AD1L_MXR_SRC     6
++#define AIF1_AD1R_AIF2_DACR_MXR               3
++#define AIF1_AD1R_ADCR_MXR            2
++#define AIF1_AD1R_MXR_SRC     2
++
++/*AIF1_VOL_CTRL1*/
++#define AIF1_AD0L_VOL         8
++#define AIF1_AD0R_VOL         0
++
++/*AIF1_VOL_CTRL2*/
++#define AIF1_AD1L_VOL         8
++#define AIF1_AD1R_VOL         0
++
++/*AIF1_VOL_CTRL3*/
++#define AIF1_DA0L_VOL         8
++#define AIF1_DA0R_VOL         0
++
++/*AIF1_VOL_CTRL4*/
++#define AIF1_DA1L_VOL         8
++#define AIF1_DA1R_VOL                 0
++
++/*AIF1_MXR_GAIN*/
++#define AIF1_AD0L_MXR_GAIN    12
++#define AIF1_AD0R_MXR_GAIN    8
++#define AIF1_AD1L_MXR_GAIN    6
++#define AIF1_AD1R_MXR_GAIN    2
++
++/*AIF1_RXD_CTRL*/
++#define AIF1_N_DATA_DISCARD   8
++
++/*ADC_DIG_CTRL*/
++#define ENAD                          15
++#define ENDM                          14
++#define ADFIR32                               13
++#define ADOUT_DTS                     2
++#define ADOUT_DLY                     1
++
++/*ADC_VOL_CTRL*/
++#define ADC_VOL_L                     8
++#define ADC_VOL_R                     0
++
++/*ADC_DBG_CTRL*/
++#define ADSW                          15
++#define DMIC_CLK_PIN_CTRL     12
++
++/*HMIC_CTRL1*/
++#define HMIC_M                                12
++#define HMIC_N                                8
++#define HMIC_DATA_IRQ_MODE    7
++#define HMIC_TH1_HYSTERESIS   5
++#define HMIC_PULLOUT_IRQ      4
++#define HMIC_PLUGIN_IRQ               3
++#define HMIC_KEYUP_IRQ                2
++#define HMIC_KEYDOWN_IRQ      1
++#define HMIC_DATA_IRQ_EN      0
++
++/*HMIC_CTRL2*/
++#define HMIC_SAMPLE_SELECT    14
++#define HMIC_TH2_HYSTERESIS   13
++#define HMIC_TH2                      8
++#define HMIC_SF                               6
++#define KEYUP_CLEAR                   5
++#define HMIC_TH1                      0
++
++/*HMIC_STS*/
++#define HMIC_DATA                     8
++#define GET_HMIC_DATA(r)              (((r) >> HMIC_DATA) & 0x1F)
++#define HMIC_PULLOUT_PEND     4
++#define HMIC_PLUGIN_PEND      3
++#define HMIC_KEYUP_PEND               2
++#define HMKC_KEYDOWN_PEND     1
++#define HMIC_DATA_PEND                0
++#define HMIC_PEND_ALL         (0x1F)
++
++/*DAC_DIG_CTRL*/
++#define ENDA                          15
++#define ENHPF                         14
++#define DAFIR32                               13
++#define MODQU                         8
++
++/*DAC_VOL_CTRL*/
++#define DAC_VOL_L                     8
++#define DAC_VOL_R                     0
++
++/*DAC_DBG_CTRL*/
++#define DASW                          15
++#define ENDWA_N                               14
++#define DAC_MOD_DBG                   13
++#define DAC_PTN_SEL                   6
++#define DVC                           0
++
++/*DAC_MXR_SRC*/
++#define DACL_MXR_AIF1_DA0L            15
++#define DACL_MXR_AIF1_DA1L            14
++#define DACL_MXR_AIF2_DACL            13
++#define DACL_MXR_ADCL                 12
++#define DACL_MXR_SRC                  12
++#define DACR_MXR_AIF1_DA0R            11
++#define DACR_MXR_AIF1_DA1R            10
++#define DACR_MXR_AIF2_DACR            9
++#define DACR_MXR_ADCR                 8
++#define DACR_MXR_SRC          8
++
++/*DAC_MXR_GAIN*/
++#define DACL_MXR_GAIN         12
++#define DACR_MXR_GAIN         8
++
++/*ADC_APC_CTRL*/
++#define ADCREN                                15
++#define ADCRG                         12
++#define ADCLEN                                11
++#define ADCLG                         8
++#define MBIASEN                               7
++#define MMIC_BIAS_CHOP_EN             6
++#define MMIC_BIAS_CHOP_CKS            4
++#define HBIASMOD                      2
++#define HBIASEN                               1
++#define HBIASADCEN                    0
++
++/*ADC_SRC*/
++#define RADCMIXMUTEMIC1BOOST    (13)
++#define RADCMIXMUTEMIC2BOOST    (12)
++#define RADCMIXMUTELINEINLR             (11)
++#define RADCMIXMUTELINEINR              (10)
++#define RADCMIXMUTEAUXINR               (9)
++#define RADCMIXMUTEROUTPUT              (8)
++#define RADCMIXMUTELOUTPUT              (7)
++#define LADCMIXMUTEMIC1BOOST    (6)
++#define LADCMIXMUTEMIC2BOOST    (5)
++#define LADCMIXMUTELINEINLR             (4)
++#define LADCMIXMUTELINEINL              (3)
++#define LADCMIXMUTEAUXINL               (2)
++#define LADCMIXMUTELOUTPUT              (1)
++#define LADCMIXMUTEROUTPUT              (0)
++
++/*ADC_SRCBST_CTRL*/
++#define MIC1AMPEN                     15
++#define ADC_MIC1G                     12
++#define MIC2AMPEN                     11
++#define ADC_MIC2G                     8
++#define MIC2SLT                               7
++#define LINEIN_PREG                   4
++#define AUXI_PREG                     0
++
++/*OMIXER_DACA_CTRL*/
++#define DACAREN                               15
++#define DACALEN                               14
++#define RMIXEN                                13
++#define LMIXEN                                12
++#define HPOUTPUTENABLE                        8
++
++/*OMIXER_SR*/
++#define RMIXMUTEMIC1BOOST               (13)
++#define RMIXMUTEMIC2BOOST               (12)
++#define RMIXMUTELINEINLR                (11)
++#define RMIXMUTELINEINR                         (10)
++#define RMIXMUTEAUXINR                          (9)
++#define RMIXMUTEDACR                    (8)
++#define RMIXMUTEDACL                    (7)
++#define LMIXMUTEMIC1BOOST               (6)
++#define LMIXMUTEMIC2BOOST               (5)
++#define LMIXMUTELINEINLR                (4)
++#define LMIXMUTELINEINL                         (3)
++#define LMIXMUTEAUXINL                          (2)
++#define LMIXMUTEDACL                    (1)
++#define LMIXMUTEDACR                    (0)
++
++/*OMIXER_BST1_CTRL*/
++#define BIASVOLTAGE                   12
++#define AXG                           9
++#define OMIXER_MIC1G                  6
++#define OMIXER_MIC2G                  3
++#define LINEING                               0
++
++/*HPOUT_CTRL*/
++#define RHPS                          15
++#define LHPS                          14
++#define RHPPA_MUTE                    13
++#define LHPPA_MUTE                    12
++#define HPPA_EN                               11
++#define HP_VOL                                4
++#define HPPA_DEL                      2
++#define HPPA_IS                               0
++
++/*ESPKOUT_CTRL*/
++#define EAR_RAMP_TIME         11
++#define       ESPA_OUT_CURRENT        9
++#define ESPSR                         7
++#define ESPPA_MUTE                    6
++#define ESPPA_EN                      5
++#define ESP_VOL                               0
++
++/*SPKOUT_CTRL*/
++#define HPCALICKS                     13
++#define RSPKS                         12
++#define RSPKINVEN                     11
++#define RSPK_EN                               9
++#define LSPKS                         8
++#define LSPKINVEN                     7
++#define LSPK_EN                               5
++#define SPK_VOL                               0
++
++/*LOUT_CTRL*/
++#define LINEOUTG                      5
++#define LINEOUTEN                     4
++#define LINEOUTS0                     3
++#define LINEOUTS1                     2
++#define LINEOUTS2                     1
++#define LINEOUTS3                     0
++
++/*ADDA_TUNE1*/
++#define CURRENT_TEST_SELECT   14
++#define BIHE_CTRL                     12
++#define DITHER                                11
++#define DITHER_CLK                    9
++#define ZERO_CROSSOVER_EN     8
++#define ZERO_CROSSOVER_TIME 7
++#define EAR_SPEED_SELECT      6
++#define REF_CHOPPEN_CKS               4
++#define OPMIC_BIAS_CUR                0
++
++/*ADDA_TUNE2*/
++#define OPDAC_BIAS_CUR                14
++#define OPDRV_BIAS_CUR                12
++#define OPMIX_BIAS_CUR                10
++#define OPEAR_BIAS_CUR                8
++#define OPVR_BIAS_CUR         6
++#define OPAAF_BIAS_CUR                4
++#define OPADC1_BIAS_CUR               2
++#define OPADC2_BIAS_CUR               0
++
++/*ADDA_TUNE3*/
++#define LDOEN                         15
++#define LDO_SEL                               12
++#define BIASCALIVERIFY                        11
++#define BIASMODE                      10
++#define BIASCALIDATA                  9
++#define OSCS                          1
++#define OSCEN                         0
++
++/*HPOUT_STR*/
++#define HPVL_SOFT_MOD         14
++#define       HPVL_STEP_CTRL          8
++#define  DACA_CHND_ENA                7
++#define HPPA_MXRD_ENA         6
++#define HPVL_CTRL_OUT         0
++
++#endif//__AC101_REGS_H__
+--- /dev/null
++++ b/sound/soc/codecs/ac108.c
+@@ -0,0 +1,1622 @@
++/*
++ * ac10x.c  --  ac10x ALSA SoC Audio driver
++ *
++ *
++ * Author: Baozhu Zuo<zuobaozhu@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/pm.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/workqueue.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/tlv.h>
++
++#include "ac108.h"
++#include "ac10x.h"
++
++#define _USE_CAPTURE  1
++#define _MASTER_INDEX 0
++
++/* #undef DEBUG
++ * use 'make DEBUG=1' to enable debugging
++ */
++
++/**
++ * TODO:
++ * 1, add PM API:  ac108_suspend,ac108_resume
++ * 2,0x65-0x6a
++ * 3,0x76-0x79 high 4bit
++ */
++struct pll_div {
++      unsigned int freq_in;
++      unsigned int freq_out;
++      unsigned int m1;
++      unsigned int m2;
++      unsigned int n;
++      unsigned int k1;
++      unsigned int k2;
++};
++
++static struct ac10x_priv *ac10x;
++
++struct real_val_to_reg_val {
++      unsigned int real_val;
++      unsigned int reg_val;
++};
++
++static const struct real_val_to_reg_val ac108_sample_rate[] = {
++      { 8000,  0 },
++      { 11025, 1 },
++      { 12000, 2 },
++      { 16000, 3 },
++      { 22050, 4 },
++      { 24000, 5 },
++      { 32000, 6 },
++      { 44100, 7 },
++      { 48000, 8 },
++      { 96000, 9 },
++};
++
++/* Sample resolution */
++static const struct real_val_to_reg_val ac108_samp_res[] = {
++      { 8,  1 },
++      { 12, 2 },
++      { 16, 3 },
++      { 20, 4 },
++      { 24, 5 },
++      { 28, 6 },
++      { 32, 7 },
++};
++
++static const unsigned int ac108_bclkdivs[] = {
++      0,   1,   2,   4,
++      6,   8,  12,  16,
++      24,  32,  48,  64,
++      96, 128, 176, 192,
++};
++
++/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ;        M1[0,31],  M2[0,1],  N[0,1023],  K1[0,31],  K2[0,1] */
++static const struct pll_div ac108_pll_div_list[] = {
++      { 400000,   _FREQ_24_576K, 0,  0, 614, 4, 1 },
++      { 512000,   _FREQ_24_576K, 0,  0, 960, 9, 1 }, /* _FREQ_24_576K/48 */
++      { 768000,   _FREQ_24_576K, 0,  0, 640, 9, 1 }, /* _FREQ_24_576K/32 */
++      { 800000,   _FREQ_24_576K, 0,  0, 614, 9, 1 },
++      { 1024000,  _FREQ_24_576K, 0,  0, 480, 9, 1 }, /* _FREQ_24_576K/24 */
++      { 1600000,  _FREQ_24_576K, 0,  0, 307, 9, 1 },
++      { 2048000,  _FREQ_24_576K, 0,  0, 240, 9, 1 }, /* accurate,  8000 * 256 */
++      { 3072000,  _FREQ_24_576K, 0,  0, 160, 9, 1 }, /* accurate, 12000 * 256 */
++      { 4096000,  _FREQ_24_576K, 2,  0, 360, 9, 1 }, /* accurate, 16000 * 256 */
++      { 6000000,  _FREQ_24_576K, 4,  0, 410, 9, 1 },
++      { 12000000, _FREQ_24_576K, 9,  0, 410, 9, 1 },
++      { 13000000, _FREQ_24_576K, 8,  0, 340, 9, 1 },
++      { 15360000, _FREQ_24_576K, 12, 0, 415, 9, 1 },
++      { 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 },
++      { 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 },
++      { 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 },
++      { 24000000, _FREQ_24_576K, 9,  0, 256,24, 0 }, /* accurate, 24M -> 24.576M */
++
++      { 400000,   _FREQ_22_579K, 0,  0, 566, 4, 1 },
++      { 512000,   _FREQ_22_579K, 0,  0, 880, 9, 1 },
++      { 768000,   _FREQ_22_579K, 0,  0, 587, 9, 1 },
++      { 800000,   _FREQ_22_579K, 0,  0, 567, 9, 1 },
++      { 1024000,  _FREQ_22_579K, 0,  0, 440, 9, 1 },
++      { 1600000,  _FREQ_22_579K, 1,  0, 567, 9, 1 },
++      { 2048000,  _FREQ_22_579K, 0,  0, 220, 9, 1 },
++      { 3072000,  _FREQ_22_579K, 0,  0, 148, 9, 1 },
++      { 4096000,  _FREQ_22_579K, 2,  0, 330, 9, 1 },
++      { 6000000,  _FREQ_22_579K, 2,  0, 227, 9, 1 },
++      { 12000000, _FREQ_22_579K, 8,  0, 340, 9, 1 },
++      { 13000000, _FREQ_22_579K, 9,  0, 350, 9, 1 },
++      { 15360000, _FREQ_22_579K, 10, 0, 325, 9, 1 },
++      { 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 },
++      { 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 },
++      { 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 },
++      { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, /* accurate, 24M -> 22.5792M */
++
++
++      { _FREQ_24_576K / 1,   _FREQ_24_576K, 9,  0, 200, 9, 1 }, /* _FREQ_24_576K */
++      { _FREQ_24_576K / 2,   _FREQ_24_576K, 9,  0, 400, 9, 1 }, /* 12288000,accurate, 48000 * 256 */
++      { _FREQ_24_576K / 4,   _FREQ_24_576K, 4,  0, 400, 9, 1 }, /* 6144000, accurate, 24000 * 256 */
++      { _FREQ_24_576K / 16,  _FREQ_24_576K, 0,  0, 320, 9, 1 }, /* 1536000 */
++      { _FREQ_24_576K / 64,  _FREQ_24_576K, 0,  0, 640, 4, 1 }, /* 384000 */
++      { _FREQ_24_576K / 96,  _FREQ_24_576K, 0,  0, 960, 4, 1 }, /* 256000 */
++      { _FREQ_24_576K / 128, _FREQ_24_576K, 0,  0, 512, 1, 1 }, /* 192000 */
++      { _FREQ_24_576K / 176, _FREQ_24_576K, 0,  0, 880, 4, 0 }, /* 140000 */
++      { _FREQ_24_576K / 192, _FREQ_24_576K, 0,  0, 960, 4, 0 }, /* 128000 */
++
++      { _FREQ_22_579K / 1,   _FREQ_22_579K, 9,  0, 200, 9, 1 }, /* _FREQ_22_579K */
++      { _FREQ_22_579K / 2,   _FREQ_22_579K, 9,  0, 400, 9, 1 }, /* 11289600,accurate, 44100 * 256 */
++      { _FREQ_22_579K / 4,   _FREQ_22_579K, 4,  0, 400, 9, 1 }, /* 5644800, accurate, 22050 * 256 */
++      { _FREQ_22_579K / 16,  _FREQ_22_579K, 0,  0, 320, 9, 1 }, /* 1411200 */
++      { _FREQ_22_579K / 64,  _FREQ_22_579K, 0,  0, 640, 4, 1 }, /* 352800 */
++      { _FREQ_22_579K / 96,  _FREQ_22_579K, 0,  0, 960, 4, 1 }, /* 235200 */
++      { _FREQ_22_579K / 128, _FREQ_22_579K, 0,  0, 512, 1, 1 }, /* 176400 */
++      { _FREQ_22_579K / 176, _FREQ_22_579K, 0,  0, 880, 4, 0 }, /* 128290 */
++      { _FREQ_22_579K / 192, _FREQ_22_579K, 0,  0, 960, 4, 0 }, /* 117600 */
++
++      { _FREQ_22_579K / 6,   _FREQ_22_579K, 2,  0, 360, 9, 1 }, /* 3763200 */
++      { _FREQ_22_579K / 8,   _FREQ_22_579K, 0,  0, 160, 9, 1 }, /* 2822400, accurate, 11025 * 256 */
++      { _FREQ_22_579K / 12,  _FREQ_22_579K, 0,  0, 240, 9, 1 }, /* 1881600 */
++      { _FREQ_22_579K / 24,  _FREQ_22_579K, 0,  0, 480, 9, 1 }, /* 940800 */
++      { _FREQ_22_579K / 32,  _FREQ_22_579K, 0,  0, 640, 9, 1 }, /* 705600 */
++      { _FREQ_22_579K / 48,  _FREQ_22_579K, 0,  0, 960, 9, 1 }, /* 470400 */
++};
++
++
++/* AC108 definition */
++#define AC108_CHANNELS_MAX            8               /* range[1, 16] */
++#define AC108_RATES                   (SNDRV_PCM_RATE_8000_96000 &            \
++                                      ~(SNDRV_PCM_RATE_64000 | \
++                                      SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000))
++#define AC108_FORMATS                 (SNDRV_PCM_FMTBIT_S16_LE | \
++                                      /*SNDRV_PCM_FMTBIT_S20_3LE |   \
++                                      SNDRV_PCM_FMTBIT_S24_LE |*/  \
++                                      SNDRV_PCM_FMTBIT_S32_LE)
++
++static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0);
++static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925, 75, 0);
++
++int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm)
++{
++      int r, v = 0;
++
++      if ((r = regmap_read(i2cm, reg, &v)) < 0)
++              pr_info("ac10x_read info->[REG-0x%02x]\n", reg);
++      else
++              *rt_val = v;
++      return r;
++}
++
++int ac10x_write(u8 reg, u8 val, struct regmap* i2cm)
++{
++      int r;
++
++      if ((r = regmap_write(i2cm, reg, val)) < 0)
++              pr_info("ac10x_write info->[REG-0x%02x,val-0x%02x]\n", reg, val);
++      return r;
++}
++
++int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm)
++{
++      int r;
++
++      if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0)
++              pr_info("%s() info->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val);
++      return r;
++}
++
++/**
++ * snd_ac108_get_volsw - single mixer get callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to get the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_value *ucontrol)
++{
++      struct soc_mixer_control *mc =
++              (struct soc_mixer_control *)kcontrol->private_value;
++      unsigned int mask = (1 << fls(mc->max)) - 1;
++      unsigned int invert = mc->invert;
++      int ret, chip = mc->autodisable;
++      u8 val;
++
++      if ((ret = ac10x_read(mc->reg, &val, ac10x->i2cmap[chip])) < 0)
++              return ret;
++
++      val = ((val >> mc->shift) & mask) - mc->min;
++      if (invert) {
++              val = mc->max - val;
++      }
++      ucontrol->value.integer.value[0] = val;
++      return 0;
++}
++
++/**
++ * snd_ac108_put_volsw - single mixer put callback
++ * @kcontrol: mixer control
++ * @ucontrol: control element information
++ *
++ * Callback to set the value of a single mixer control, or a double mixer
++ * control that spans 2 registers.
++ *
++ * Returns 0 for success.
++ */
++static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_value *ucontrol)
++{
++      struct soc_mixer_control *mc =
++              (struct soc_mixer_control *)kcontrol->private_value;
++      unsigned int sign_bit = mc->sign_bit;
++      unsigned int val, mask = (1 << fls(mc->max)) - 1;
++      unsigned int invert = mc->invert;
++      int ret, chip = mc->autodisable;
++
++      if (sign_bit)
++              mask = BIT(sign_bit + 1) - 1;
++
++      val = ((ucontrol->value.integer.value[0] + mc->min) & mask);
++      if (invert) {
++              val = mc->max - val;
++      }
++
++      mask = mask << mc->shift;
++      val = val << mc->shift;
++
++      ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2cmap[chip]);
++      return ret;
++}
++
++#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \
++{     .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
++      .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
++               SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++      .tlv.p = (tlv_array), \
++      .info = snd_soc_info_volsw, .get = snd_ac108_get_volsw,\
++      .put = snd_ac108_put_volsw, \
++      .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, chip) }
++
++/* single ac108 */
++static const struct snd_kcontrol_new ac108_snd_controls[] = {
++      /* ### chip 0 ### */
++      /*0x70: ADC1 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++      /*0x71: ADC2 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++      /*0x72: ADC3 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++      /*0x73: ADC4 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++
++      /*0x90: Analog PGA1 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL,
++                           ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++      /*0x91: Analog PGA2 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL,
++                           ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++      /*0x92: Analog PGA3 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL,
++                           ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++      /*0x93: Analog PGA4 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL,
++                           ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++};
++/* multiple ac108s */
++static const struct snd_kcontrol_new ac108tdm_snd_controls[] = {
++      /* ### chip 1 ### */
++      /*0x70: ADC1 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL,
++                           0, 0xff, 0, 1, tlv_ch_digital_vol),
++      /*0x71: ADC2 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL,
++                           0, 0xff, 0, 1, tlv_ch_digital_vol),
++      /*0x72: ADC3 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL,
++                           0, 0xff, 0, 1, tlv_ch_digital_vol),
++      /*0x73: ADC4 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL,
++                           0, 0xff, 0, 1, tlv_ch_digital_vol),
++
++      /*0x90: Analog PGA1 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL,
++                           ADC1_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++      /*0x91: Analog PGA2 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL,
++                           ADC2_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++      /*0x92: Analog PGA3 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL,
++                           ADC3_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++      /*0x93: Analog PGA4 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL,
++                           ADC4_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain),
++
++      /* ### chip 0 ### */
++      /*0x70: ADC1 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH5 digital volume", ADC1_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++      /*0x71: ADC2 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH6 digital volume", ADC2_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++      /*0x72: ADC3 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH7 digital volume", ADC3_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++      /*0x73: ADC4 Digital Channel Volume Control Register*/
++      SOC_AC108_SINGLE_TLV("CH8 digital volume", ADC4_DVOL_CTRL,
++                           0, 0xff, 0, 0, tlv_ch_digital_vol),
++
++      /*0x90: Analog PGA1 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC5 PGA gain", ANA_PGA1_CTRL,
++                           ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++      /*0x91: Analog PGA2 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC6 PGA gain", ANA_PGA2_CTRL,
++                           ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++      /*0x92: Analog PGA3 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC7 PGA gain", ANA_PGA3_CTRL,
++                           ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++      /*0x93: Analog PGA4 Control Register*/
++      SOC_AC108_SINGLE_TLV("ADC8 PGA gain", ANA_PGA4_CTRL,
++                           ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain),
++};
++
++
++static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = {
++      /* input widgets */
++      SND_SOC_DAPM_INPUT("MIC1P"),
++      SND_SOC_DAPM_INPUT("MIC1N"),
++
++      SND_SOC_DAPM_INPUT("MIC2P"),
++      SND_SOC_DAPM_INPUT("MIC2N"),
++
++      SND_SOC_DAPM_INPUT("MIC3P"),
++      SND_SOC_DAPM_INPUT("MIC3N"),
++
++      SND_SOC_DAPM_INPUT("MIC4P"),
++      SND_SOC_DAPM_INPUT("MIC4N"),
++
++      SND_SOC_DAPM_INPUT("DMIC1"),
++      SND_SOC_DAPM_INPUT("DMIC2"),
++
++      /*0xa0: ADC1 Analog Control 1 Register*/
++      /*0xa1-0xa6:use the defualt value*/
++      SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1),
++      SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0),
++      SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1),
++
++      /*0xa7: ADC2 Analog Control 1 Register*/
++      /*0xa8-0xad:use the defualt value*/
++      SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1),
++      SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0),
++      SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1),
++
++      /*0xae: ADC3 Analog Control 1 Register*/
++      /*0xaf-0xb4:use the defualt value*/
++      SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1),
++      SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0),
++      SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1),
++
++      /*0xb5: ADC4 Analog Control 1 Register*/
++      /*0xb6-0xbb:use the defualt value*/
++      SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1),
++      SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0),
++      SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1),
++
++
++      /*0x61: ADC Digital Part Enable Register*/
++      SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4,  1, NULL, 0),
++      SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0,  1),
++      SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1,  1),
++      SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2,  1),
++      SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3,  1),
++
++      SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0),
++      SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0),
++      SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0),
++      SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0),
++
++      SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0),
++
++      /*0x62:Digital MIC Enable Register*/
++      SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0),
++      SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0),
++};
++
++static const struct snd_soc_dapm_route ac108_dapm_routes[] = {
++
++      { "ADC1", NULL, "Channel 1 AAF" },
++      { "ADC2", NULL, "Channel 2 AAF" },
++      { "ADC3", NULL, "Channel 3 AAF" },
++      { "ADC4", NULL, "Channel 4 AAF" },
++
++      { "Channel 1 AAF", NULL, "MIC1BIAS" },
++      { "Channel 2 AAF", NULL, "MIC2BIAS" },
++      { "Channel 3 AAF", NULL, "MIC3BIAS" },
++      { "Channel 4 AAF", NULL, "MIC4BIAS" },
++
++      { "MIC1BIAS", NULL, "ADC1 CLK" },
++      { "MIC2BIAS", NULL, "ADC2 CLK" },
++      { "MIC3BIAS", NULL, "ADC3 CLK" },
++      { "MIC4BIAS", NULL, "ADC4 CLK" },
++
++
++      { "ADC1 CLK", NULL, "DSM EN" },
++      { "ADC2 CLK", NULL, "DSM EN" },
++      { "ADC3 CLK", NULL, "DSM EN" },
++      { "ADC4 CLK", NULL, "DSM EN" },
++
++
++      { "DSM EN", NULL, "ADC EN" },
++
++      { "Channel 1 EN", NULL, "DSM EN" },
++      { "Channel 2 EN", NULL, "DSM EN" },
++      { "Channel 3 EN", NULL, "DSM EN" },
++      { "Channel 4 EN", NULL, "DSM EN" },
++
++
++      { "MIC1P", NULL, "Channel 1 EN" },
++      { "MIC1N", NULL, "Channel 1 EN" },
++
++      { "MIC2P", NULL, "Channel 2 EN" },
++      { "MIC2N", NULL, "Channel 2 EN" },
++
++      { "MIC3P", NULL, "Channel 3 EN" },
++      { "MIC3N", NULL, "Channel 3 EN" },
++
++      { "MIC4P", NULL, "Channel 4 EN" },
++      { "MIC4N", NULL, "Channel 4 EN" },
++
++};
++
++static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x)
++{
++      u8 i;
++      for (i = 0; i < ac10x->codec_cnt; i++)
++              ac10x_write(reg, val, ac10x->i2cmap[i]);
++      return 0;
++}
++
++static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x)
++{
++      int r = 0;
++      u8 i;
++      for (i = 0; i < ac10x->codec_cnt; i++)
++              r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
++      return r;
++}
++
++static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg)
++{
++      unsigned char val_r;
++      struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
++      /*read one chip is fine*/
++      ac10x_read(reg, &val_r, ac10x->i2cmap[_MASTER_INDEX]);
++      return val_r;
++}
++
++static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val)
++{
++      struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
++      ac108_multi_write(reg, val, ac10x);
++      return 0;
++}
++
++/**
++ * The Power management related registers are Reg01h~Reg09h
++ * 0x01-0x05,0x08,use the default value
++ * @author baozhu (17-6-21)
++ *
++ * @param ac10x
++ */
++static void ac108_configure_power(struct ac10x_priv *ac10x)
++{
++      /**
++       * 0x06:Enable Analog LDO
++       */
++      ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x);
++      /**
++       * 0x07: 
++       * Control VREF output and micbias voltage ?
++       * REF faststart disable, enable Enable VREF (needed for Analog
++       * LDO and MICBIAS)
++       */
++      ac108_multi_update_bits(PWR_CTRL7,
++                              0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE |
++                              0x01 << VREF_ENABLE,
++                              0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE |
++                              0x01 << VREF_ENABLE,
++                              ac10x);
++      /**
++       * 0x09:
++       * Disable fast-start circuit on VREFP
++       * VREFP_RESCTRL=00=1 MOhm
++       * IGEN_TRIM=100=+25%
++       * Enable VREFP (needed by all audio input channels)
++       */
++      ac108_multi_update_bits(PWR_CTRL9,
++                              0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL |
++                              0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
++                              0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL |
++                              0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
++                              ac10x);
++}
++
++/**
++ * The clock management related registers are Reg20h~Reg25h
++ * The PLL management related registers are Reg10h~Reg18h.
++ * @author baozhu (17-6-20)
++ *
++ * @param ac10x
++ * @param rate : sample rate
++ *
++ * @return int : fail or success
++ */
++static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio)
++{
++      unsigned int i = 0;
++      struct pll_div ac108_pll_div = { 0 };
++
++      if (ac10x->clk_id == SYSCLK_SRC_PLL) {
++              unsigned pll_src, pll_freq_in;
++
++              if (lrck_ratio == 0) {
++                      /* PLL clock source from MCLK */
++                      pll_freq_in = ac10x->sysclk;
++                      pll_src = 0x0;
++              } else {
++                      /* PLL clock source from BCLK */
++                      pll_freq_in = rate * lrck_ratio;
++                      pll_src = 0x1;
++              }
++
++              /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */
++              for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) {
++                      if (ac108_pll_div_list[i].freq_in == pll_freq_in &&
++                          ac108_pll_div_list[i].freq_out % rate == 0) {
++                              ac108_pll_div = ac108_pll_div_list[i];
++                              dev_info(&ac10x->i2c[_MASTER_INDEX]->dev,
++                                       "AC108 PLL freq_in match:%u, freq_out:%u\n\n",
++                                       ac108_pll_div.freq_in, ac108_pll_div.freq_out);
++                              break;
++                      }
++              }
++              /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */
++              ac108_multi_update_bits(PLL_CTRL5,
++                                      0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2,
++                                      ac108_pll_div.k1 << PLL_POSTDIV1 |
++                                      ac108_pll_div.k2 << PLL_POSTDIV2, ac10x);
++              ac108_multi_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB,
++                                      (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac10x);
++              ac108_multi_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB,
++                                      (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac10x);
++              ac108_multi_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2,
++                                      ac108_pll_div.m1 << PLL_PREDIV1 |
++                                      ac108_pll_div.m2 << PLL_PREDIV2, ac10x);
++
++              /*0x18: PLL clk lock enable*/
++              ac108_multi_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN,
++                                      0x1 << PLL_LOCK_EN, ac10x);
++
++              /**
++               * 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk
++               */
++              ac108_multi_update_bits(SYSCLK_CTRL,
++                                      0x01 << PLLCLK_EN | 0x03  << PLLCLK_SRC |
++                                      0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
++                                      0x01 << PLLCLK_EN | pll_src << PLLCLK_SRC |
++                                      0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
++              ac10x->mclk = ac108_pll_div.freq_out;
++      }
++      if (ac10x->clk_id == SYSCLK_SRC_MCLK) {
++              /**
++               *0x20: sysclk source from mclk, enable sysclk
++               */
++              ac108_multi_update_bits(SYSCLK_CTRL,
++                                      0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
++                                      0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
++                                      ac10x);
++              ac10x->mclk = ac10x->sysclk;
++      }
++
++      return 0;
++}
++
++/*
++ * support no more than 16 slots.
++ */
++static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots)
++{
++      int i;
++
++      /*
++       * codec0 enable slots 2,3,0,1 when 1 codec
++       *
++       * codec0 enable slots 6,7,0,1 when 2 codec
++       * codec1 enable slots 2,3,4,5
++       *
++       * ...
++       */
++      for (i = 0; i < ac->codec_cnt; i++) {
++              /* rotate map, due to channels rotated by CPU_DAI */
++              const unsigned vec_mask[] = {
++                      0x3 << 6 | 0x3, // slots 6,7,0,1
++                      0xF << 2,       // slots 2,3,4,5
++                      0,
++                      0,
++              };
++              const unsigned vec_maps[] = {
++                      /*
++                       * chip 0,
++                       * mic 0 sample -> slot 6
++                       * mic 1 sample -> slot 7
++                       * mic 2 sample -> slot 0
++                       * mic 3 sample -> slot 1
++                       */
++                      0x0 << 12 | 0x1 << 14 | 0x2 << 0 | 0x3 << 2,
++                      /*
++                       * chip 1,
++                       * mic 0 sample -> slot 2
++                       * mic 1 sample -> slot 3
++                       * mic 2 sample -> slot 4
++                       * mic 3 sample -> slot 5
++                       */
++                      0x0 << 4  | 0x1 << 6  | 0x2 << 8 | 0x3 << 10,
++                      0,
++                      0,
++              };
++              unsigned vec;
++
++              /* 0x38-0x3A I2S_TX1_CTRLx */
++              if (ac->codec_cnt == 1) {
++                      vec = 0xFUL;
++              } else {
++                      vec = vec_mask[i];
++              }
++              ac10x_write(I2S_TX1_CTRL1, slots - 1, ac->i2cmap[i]);
++              ac10x_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF, ac->i2cmap[i]);
++              ac10x_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF, ac->i2cmap[i]);
++
++              /* 0x3C-0x3F I2S_TX1_CHMP_CTRLx */
++              if (ac->codec_cnt == 1) {
++                      vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6);
++              } else if (ac->codec_cnt == 2) {
++                      vec = vec_maps[i];
++              }
++
++              ac10x_write(I2S_TX1_CHMP_CTRL1, (vec >>  0) & 0xFF, ac->i2cmap[i]);
++              ac10x_write(I2S_TX1_CHMP_CTRL2, (vec >>  8) & 0xFF, ac->i2cmap[i]);
++              ac10x_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF, ac->i2cmap[i]);
++              ac10x_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF, ac->i2cmap[i]);
++      }
++      return 0;
++}
++
++static int ac108_hw_params(struct snd_pcm_substream *substream,
++      struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++      unsigned int i, channels, samp_res, rate;
++      struct snd_soc_codec *codec = dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      unsigned bclkdiv;
++      int ret = 0;
++      u8 v;
++
++      dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__,
++                      snd_pcm_stream_str(substream),
++                      dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active,
++                      dai->stream[SNDRV_PCM_STREAM_CAPTURE].active);
++
++      if (ac10x->i2c101) {
++              ret = ac101_hw_params(substream, params, dai);
++              if (ret > 0) {
++                      dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__);
++                      /* not configure hw_param twice */
++                      return 0;
++              }
++      }
++
++      if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
++           dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active)
++          || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
++              dai->stream[SNDRV_PCM_STREAM_CAPTURE].active)) {
++              /* not configure hw_param twice */
++              /* return 0; */
++      }
++
++      channels = params_channels(params);
++
++      /* Master mode, to clear cpu_dai fifos, output bclk without lrck */
++      ac10x_read(I2S_CTRL, &v, ac10x->i2cmap[_MASTER_INDEX]);
++      if (v & (0x01 << BCLK_IOEN)) {
++              ac10x_update_bits(I2S_CTRL, 0x1 << LRCK_IOEN,
++                                0x0 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
++      }
++
++      switch (params_format(params)) {
++      case SNDRV_PCM_FORMAT_S8:
++              samp_res = 0;
++              break;
++      case SNDRV_PCM_FORMAT_S16_LE:
++              samp_res = 2;
++              break;
++      case SNDRV_PCM_FORMAT_S20_3LE:
++              samp_res = 3;
++              break;
++      case SNDRV_PCM_FORMAT_S24_LE:
++              samp_res = 4;
++              break;
++      case SNDRV_PCM_FORMAT_S32_LE:
++              samp_res = 6;
++              break;
++      default:
++              dev_err(dai->dev, "AC108 don't supported the sample resolution: %u\n",
++                      params_format(params));
++              return -EINVAL;
++      }
++
++      for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) {
++              if (ac108_sample_rate[i].real_val == params_rate(params) /
++                  (ac10x->data_protocol + 1UL)) {
++                      rate = i;
++                      break;
++              }
++      }
++      if (i >= ARRAY_SIZE(ac108_sample_rate)) {
++              return -EINVAL;
++      }
++
++      if (channels == 8 && ac108_sample_rate[rate].real_val == 96000) {
++              /* 24.576M bit clock is not support by ac108 */
++              return -EINVAL;
++      }
++
++      dev_dbg(dai->dev, "rate: %d , channels: %d , samp_res: %d",
++                      ac108_sample_rate[rate].real_val,
++                      channels,
++                      ac108_samp_res[samp_res].real_val);
++
++      /**
++       * 0x33: 
++       *  The 8-Low bit of LRCK period value. It is used to program
++       *  the number of BCLKs per channel of sample frame. This value
++       *  is interpreted as follow:
++       *  The 8-Low bit of LRCK period value. It is used to program
++       *  the number of BCLKs per channel of sample frame. This value
++       *  is interpreted as follow: PCM mode: Number of BCLKs within
++       *  (Left + Right) channel width I2S / Left-Justified /
++       *  Right-Justified mode: Number of BCLKs within each individual
++       *  channel width (Left or Right) N+1
++       *  For example:
++       *  n = 7: 8 BCLK width
++       *  â€¦
++       *  n = 1023: 1024 BCLKs width
++       *  0X32[0:1]:
++       *  The 2-High bit of LRCK period value. 
++       */
++      if (ac10x->i2s_mode != PCM_FORMAT) {
++              if (ac10x->data_protocol) {
++                      ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1,
++                                        ac10x);
++                      /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/
++                      ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
++              } else {
++                      /*TDM mode or normal mode*/
++                      //ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
++                      ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1,
++                                        ac10x);
++                      ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x);
++              }
++      } else {
++              unsigned div;
++
++              /*TDM mode or normal mode*/
++              div = ac108_samp_res[samp_res].real_val * channels - 1;
++              ac108_multi_write(I2S_LRCK_CTRL2, (div & 0xFF), ac10x);
++              ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, (div >> 8) << 0, ac10x);
++      }
++
++      /**
++       * 0x35: 
++       * TX Encoding mode will add  4bits to mark channel number 
++       * TODO: need a chat to explain this 
++       */
++      ac108_multi_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL,
++                              ac108_samp_res[samp_res].reg_val << SAMPLE_RESOLUTION |
++                              ac108_samp_res[samp_res].reg_val << SLOT_WIDTH_SEL, ac10x);
++
++      /**
++       * 0x60: 
++       * ADC Sample Rate synchronised with I2S1 clock zone 
++       */
++      ac108_multi_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1,
++                              ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac10x);
++      ac108_multi_write(HPF_EN, 0x0F, ac10x);
++
++      if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
++              ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val,
++                               ac108_samp_res[samp_res].real_val * channels);
++      } else {
++              ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0);
++      }
++
++      /*
++       * master mode only
++       */
++      bclkdiv = ac10x->mclk / (ac108_sample_rate[rate].real_val * channels *
++                               ac108_samp_res[samp_res].real_val);
++      for (i = 0; i < ARRAY_SIZE(ac108_bclkdivs) - 1; i++) {
++              if (ac108_bclkdivs[i] >= bclkdiv) {
++                      break;
++              }
++      }
++      ac108_multi_update_bits(I2S_BCLK_CTRL, 0x0F << BCLKDIV, i << BCLKDIV, ac10x);
++
++      /*
++       * slots allocation for each chip
++       */
++      ac108_multi_chips_slots(ac10x, channels);
++
++      /*0x21: Module clock enable<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++      ac108_multi_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL |
++                        1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
++      /*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++      ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL |
++                        1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
++      
++      ac108_multi_write(I2S_TX1_CHMP_CTRL1, 0xE4, ac10x);
++      ac108_multi_write(I2S_TX1_CHMP_CTRL2, 0xE4, ac10x);
++      ac108_multi_write(I2S_TX1_CHMP_CTRL3, 0xE4, ac10x);
++      ac108_multi_write(I2S_TX1_CHMP_CTRL4, 0xE4, ac10x);
++      
++      ac108_multi_write(I2S_TX2_CHMP_CTRL1, 0xE4, ac10x);
++      ac108_multi_write(I2S_TX2_CHMP_CTRL2, 0xE4, ac10x);
++      ac108_multi_write(I2S_TX2_CHMP_CTRL3, 0xE4, ac10x);
++      ac108_multi_write(I2S_TX2_CHMP_CTRL4, 0xE4, ac10x);
++
++      dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__,
++                      snd_pcm_stream_str(substream));
++
++      return 0;
++}
++
++static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
++{
++
++      struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
++
++      freq = 24000000;
++      clk_id = SYSCLK_SRC_PLL;
++
++      switch (clk_id) {
++      case SYSCLK_SRC_MCLK:
++              ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
++                                      SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x);
++              break;
++      case SYSCLK_SRC_PLL:
++              ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
++                                      SYSCLK_SRC_PLL << SYSCLK_SRC, ac10x);
++              break;
++      default:
++              return -EINVAL;
++      }
++      ac10x->sysclk = freq;
++      ac10x->clk_id = clk_id;
++
++      return 0;
++}
++
++/**
++ *  The i2s format management related registers are Reg
++ *  30h~Reg36h
++ *  33h,35h will be set in ac108_hw_params, It's BCLK width and
++ *  Sample Resolution.
++ * @author baozhu (17-6-20)
++ * 
++ * @param dai 
++ * @param fmt 
++ * 
++ * @return int 
++ */
++static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
++{
++      unsigned char tx_offset, lrck_polarity, brck_polarity;
++      struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev);
++
++      switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++      case SND_SOC_DAIFMT_CBM_CFM:    /* AC108 Master */
++              if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
++                      /**
++                       * 0x30:chip is master mode ,BCLK & LRCK output
++                       */
++                      ac108_multi_update_bits(I2S_CTRL,
++                                              0x03 << LRCK_IOEN | 0x03 << SDO1_EN |
++                                              0x1 << TXEN | 0x1 << GEN,
++                                              0x03 << LRCK_IOEN | 0x01 << SDO1_EN |
++                                              0x1 << TXEN | 0x1 << GEN, ac10x);
++                      /* multi_chips: only one chip set as Master, and the others also need to set as Slave */
++                      ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN,
++                                        ac10x->i2cmap[_MASTER_INDEX]);
++              } else {
++                      /* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */
++                      dev_err(dai->dev, "used as slave when AC101 is master\n");
++              }
++              break;
++      case SND_SOC_DAIFMT_CBS_CFS:    /* AC108 Slave */
++              /**
++               * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and 
++               *  SDO2_EN, Transmitter Block Enable, Globe Enable
++               */
++              ac108_multi_update_bits(I2S_CTRL,
++                                      0x03 << LRCK_IOEN | 0x03 << SDO1_EN |
++                                      0x1 << TXEN | 0x1 << GEN,
++                                      0x00 << LRCK_IOEN | 0x03 << SDO1_EN |
++                                      0x0 << TXEN | 0x0 << GEN, ac10x);
++              break;
++      default:
++              dev_err(dai->dev, "AC108 Master/Slave mode config error:%u\n\n",
++                      (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
++              return -EINVAL;
++      }
++
++      /*AC108 config I2S/LJ/RJ/PCM format*/
++      switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++      case SND_SOC_DAIFMT_I2S:
++              ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
++              tx_offset = 1;
++              break;
++      case SND_SOC_DAIFMT_RIGHT_J:
++              ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT;
++              tx_offset = 0;
++              break;
++      case SND_SOC_DAIFMT_LEFT_J:
++              ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
++              tx_offset = 0;
++              break;
++      case SND_SOC_DAIFMT_DSP_A:
++              ac10x->i2s_mode = PCM_FORMAT;
++              tx_offset = 1;
++              break;
++      case SND_SOC_DAIFMT_DSP_B:
++              ac10x->i2s_mode = PCM_FORMAT;
++              tx_offset = 0;
++              break;
++      default:
++              dev_err(dai->dev, "AC108 I2S format config error:%u\n\n",
++                      fmt & SND_SOC_DAIFMT_FORMAT_MASK);
++              return -EINVAL;
++      }
++
++      /*AC108 config BCLK&LRCK polarity*/
++      switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++      case SND_SOC_DAIFMT_NB_NF:
++              brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
++              lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
++              break;
++      case SND_SOC_DAIFMT_NB_IF:
++              brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
++              lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
++              break;
++      case SND_SOC_DAIFMT_IB_NF:
++              brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
++              lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
++              break;
++      case SND_SOC_DAIFMT_IB_IF:
++              brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
++              lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
++              break;
++      default:
++              dev_err(dai->dev, "AC108 config BCLK/LRCLK polarity error:%u\n\n",
++                      (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
++              return -EINVAL;
++      }
++
++      ac108_configure_power(ac10x);
++
++      /**
++       *0x31: 0: normal mode, negative edge drive and positive edge sample
++              1: invert mode, positive edge drive and negative edge sample
++       */
++      ac108_multi_update_bits(I2S_BCLK_CTRL,  0x01 << BCLK_POLARITY,
++                              brck_polarity << BCLK_POLARITY, ac10x);
++      /**
++       * 0x32: same as 0x31
++       */
++      ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY,
++                              lrck_polarity << LRCK_POLARITY, ac10x);
++      /**
++       * 0x34:Encoding Mode Selection,Mode 
++       * Selection,data is offset by 1 BCLKs to LRCK 
++       * normal mode for the last half cycle of BCLK in the slot ?
++       * turn to hi-z state (TDM) when not transferring slot ?
++       */
++      ac108_multi_update_bits(I2S_FMT_CTRL1,
++                              0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET |
++                              0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE,
++                              ac10x->data_protocol << ENCD_SEL | ac10x->i2s_mode << MODE_SEL |
++                              tx_offset << TX2_OFFSET | tx_offset << TX1_OFFSET |
++                              0x00 << TX_SLOT_HIZ | 0x01 << TX_STATE, ac10x);
++
++      /**
++       * 0x60: 
++       * MSB / LSB First Select: This driver only support MSB First Select . 
++       * OUT2_MUTE,OUT1_MUTE shoule be set in widget. 
++       * LRCK = 1 BCLK width 
++       * Linear PCM 
++       *  
++       * TODO:pcm mode, bit[0:1] and bit[2] is special
++       */
++      ac108_multi_update_bits(I2S_FMT_CTRL3,
++                              0x01 << TX_MLS | 0x03 << SEXT |
++                              0x01 << LRCK_WIDTH | 0x03 << TX_PDM,
++                              0x00 << TX_MLS | 0x03 << SEXT |
++                              0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac10x);
++
++      ac108_multi_write(HPF_EN, 0x00, ac10x);
++
++      if (ac10x->i2c101) {
++              return ac101_set_dai_fmt(dai, fmt);
++      }
++      return 0;
++}
++
++/*
++ * due to miss channels order in cpu_dai, we meed defer the clock starting.
++ */
++static int ac108_set_clock(int y_start_n_stop)
++{
++      u8 reg;
++      int ret = 0;
++
++      dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop);
++
++      /* spin_lock move to machine trigger */
++
++      if (y_start_n_stop && ac10x->sysclk_en == 0) {
++              /* enable lrck clock */
++              ac10x_read(I2S_CTRL, &reg, ac10x->i2cmap[_MASTER_INDEX]);
++              if (reg & (0x01 << BCLK_IOEN)) {
++                      ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN,
++                                                     0x03 << LRCK_IOEN,
++                                                     ac10x->i2cmap[_MASTER_INDEX]);
++              }
++
++              /*0x10: PLL Common voltage enable, PLL enable */
++              ret = ret || ac108_multi_update_bits(PLL_CTRL1,
++                                                   0x01 << PLL_EN | 0x01 << PLL_COM_EN,
++                                                   0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x);
++              /* enable global clock */
++              ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
++                                                   0x1 << TXEN | 0x1 << GEN, ac10x);
++
++              ac10x->sysclk_en = 1UL;
++      } else if (!y_start_n_stop && ac10x->sysclk_en != 0) {
++              /* disable global clock */
++              ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
++                                                   0x0 << TXEN | 0x0 << GEN, ac10x);
++
++              /*0x10: PLL Common voltage disable, PLL disable */
++              ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN,
++                                                   0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x);
++
++              /* disable lrck clock if it's enabled */
++              ac10x_read(I2S_CTRL, &reg, ac10x->i2cmap[_MASTER_INDEX]);
++              if (reg & (0x01 << LRCK_IOEN)) {
++                      ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN,
++                                                     0x01 << BCLK_IOEN,
++                                                     ac10x->i2cmap[_MASTER_INDEX]);
++              }
++              if (!ret) {
++                      ac10x->sysclk_en = 0UL;
++              }
++      }
++
++      return ret;
++}
++
++static int ac108_prepare(struct snd_pcm_substream *substream,
++                       struct snd_soc_dai *dai)
++{
++      u8 r;
++
++      dev_dbg(dai->dev, "%s() stream=%s\n",
++              __func__,
++              snd_pcm_stream_str(substream));
++      
++      if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
++              ac101_trigger(substream, SNDRV_PCM_TRIGGER_START, dai);
++              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++                     return 0;
++              }
++      }
++
++      ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
++      if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
++              /* disable global clock */
++              ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN,
++                                      0x0 << TXEN | 0x0 << GEN, ac10x);
++      }
++
++      /* delayed clock starting, move to machine trigger() */
++      ac108_set_clock(1);
++
++      return 0;
++}
++
++int ac108_audio_startup(struct snd_pcm_substream *substream,
++      struct snd_soc_dai *dai)
++{
++      struct snd_soc_codec *codec = dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      if (ac10x->i2c101) {
++              return ac101_audio_startup(substream, dai);
++      }
++      return 0;
++}
++
++void ac108_aif_shutdown(struct snd_pcm_substream *substream,
++      struct snd_soc_dai *dai)
++{
++      struct snd_soc_codec *codec = dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      ac108_set_clock(0);
++
++      if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
++              /*0x21: Module clock disable <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++              ac108_multi_write(MOD_CLK_EN, 0x0, ac10x);
++              /*0x22: Module reset asserted <I2S, ADC digital, MIC offset Calibration, ADC analog>*/
++              ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x);
++      }
++
++      if (ac10x->i2c101) {
++              ac101_aif_shutdown(substream, dai);
++      }
++}
++
++int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream)
++{
++      struct snd_soc_codec *codec = dai->codec;
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      if (ac10x->i2c101) {
++              return ac101_aif_mute(dai, mute);
++      }
++      return 0;       
++}
++
++static const struct snd_soc_dai_ops ac108_dai_ops = {
++      .startup        = ac108_audio_startup,
++      .shutdown       = ac108_aif_shutdown,
++
++      /*DAI clocking configuration*/
++      .set_sysclk     = ac108_set_sysclk,
++
++      /*ALSA PCM audio operations*/
++      .hw_params      = ac108_hw_params,
++      .prepare        = ac108_prepare,
++      .mute_stream    = ac108_aif_mute,
++
++      /*DAI format configuration*/
++      .set_fmt        = ac108_set_fmt,
++};
++
++static  struct snd_soc_dai_driver ac108_dai0 = {
++      .name = "ac10x-codec0",
++      #if _USE_CAPTURE
++      .playback = {
++              .stream_name = "Playback",
++              .channels_min = 1,
++              .channels_max = AC108_CHANNELS_MAX,
++              .rates = AC108_RATES,
++              .formats = AC108_FORMATS,
++      },
++      #endif
++      .capture = {
++              .stream_name = "Capture",
++              .channels_min = 1,
++              .channels_max = AC108_CHANNELS_MAX,
++              .rates = AC108_RATES,
++              .formats = AC108_FORMATS,
++      },
++      .ops = &ac108_dai_ops,
++};
++
++static  struct snd_soc_dai_driver ac108_dai1 = {
++      .name = "ac10x-codec1",
++      #if _USE_CAPTURE
++      .playback = {
++              .stream_name = "Playback",
++              .channels_min = 1,
++              .channels_max = AC108_CHANNELS_MAX,
++              .rates = AC108_RATES,
++              .formats = AC108_FORMATS,
++      },
++      #endif
++      .capture = {
++              .stream_name = "Capture",
++              .channels_min = 1,
++              .channels_max = AC108_CHANNELS_MAX,
++              .rates = AC108_RATES,
++              .formats = AC108_FORMATS,
++      },
++      .ops = &ac108_dai_ops,
++};
++
++static  struct snd_soc_dai_driver *ac108_dai[] = {
++      &ac108_dai0,
++      &ac108_dai1,
++};
++
++static int ac108_add_widgets(struct snd_soc_codec *codec)
++{
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
++      const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls;
++      int ctrl_cnt = ARRAY_SIZE(ac108_snd_controls);
++
++      /* only register controls correspond to exist chips */
++      if (ac10x->tdm_chips_cnt >= 2) {
++              snd_kcntl = ac108tdm_snd_controls;
++              ctrl_cnt = ARRAY_SIZE(ac108tdm_snd_controls);
++      }
++      snd_soc_add_codec_controls(codec, snd_kcntl, ctrl_cnt);
++
++      snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets,ARRAY_SIZE(ac108_dapm_widgets));
++      snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes));
++
++      return 0;
++}
++
++static int ac108_codec_probe(struct snd_soc_codec *codec)
++{
++      spin_lock_init(&ac10x->lock);
++
++      ac10x->codec = codec;
++      dev_set_drvdata(codec->dev, ac10x);
++      ac108_add_widgets(codec);
++
++      if (ac10x->i2c101) {
++              ac101_codec_probe(codec);
++      }
++
++      /* change default volume */
++      ac108_multi_update_bits(ADC1_DVOL_CTRL, 0xff, 0xc8, ac10x);
++      ac108_multi_update_bits(ADC2_DVOL_CTRL, 0xff, 0xc8, ac10x);
++      ac108_multi_update_bits(ADC3_DVOL_CTRL, 0xff, 0xc8, ac10x);
++      ac108_multi_update_bits(ADC4_DVOL_CTRL, 0xff, 0xc8, ac10x);
++
++      return 0;
++}
++
++static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
++{
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      dev_dbg(codec->dev, "AC108 level:%d\n", level);
++
++      switch (level) {
++      case SND_SOC_BIAS_ON:
++              ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,
++                                      0x01 << ADC1_MICBIAS_EN, ac10x);
++              ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,
++                                      0x01 << ADC2_MICBIAS_EN, ac10x);
++              ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,
++                                      0x01 << ADC3_MICBIAS_EN, ac10x);
++              ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,
++                                      0x01 << ADC4_MICBIAS_EN, ac10x);
++              break;
++
++      case SND_SOC_BIAS_PREPARE:
++              /* Put the MICBIASes into regulating mode */
++              break;
++
++      case SND_SOC_BIAS_STANDBY:
++              break;
++
++      case SND_SOC_BIAS_OFF:
++              ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN,
++                                      0x00 << ADC1_MICBIAS_EN, ac10x);
++              ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN,
++                                      0x00 << ADC2_MICBIAS_EN, ac10x);
++              ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN,
++                                      0x00 << ADC3_MICBIAS_EN, ac10x);
++              ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN,
++                                      0x00 << ADC4_MICBIAS_EN, ac10x);
++              break;
++      }
++
++      if (ac10x->i2c101) {
++              ac101_set_bias_level(codec, level);
++      }
++      return 0;
++}
++
++int ac108_codec_remove(struct snd_soc_codec *codec) {
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++
++      if (! ac10x->i2c101) {
++              return 0;
++      }
++      return ac101_codec_remove(codec);
++}
++#if __NO_SND_SOC_CODEC_DRV
++void ac108_codec_remove_void(struct snd_soc_codec *codec)
++{
++      ac108_codec_remove(codec);
++}
++#define ac108_codec_remove ac108_codec_remove_void
++#endif
++
++int ac108_codec_suspend(struct snd_soc_codec *codec)
++{
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int i;
++
++      for (i = 0; i < ac10x->codec_cnt; i++) {
++              regcache_cache_only(ac10x->i2cmap[i], true);
++      }
++
++      if (! ac10x->i2c101) {
++              return 0;
++      }
++      return ac101_codec_suspend(codec);
++}
++
++int ac108_codec_resume(struct snd_soc_codec *codec)
++{
++      struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
++      int i, ret;
++
++      /* Sync reg_cache with the hardware */
++      for (i = 0; i < ac10x->codec_cnt; i++) {
++              regcache_cache_only(ac10x->i2cmap[i], false);
++              ret = regcache_sync(ac10x->i2cmap[i]);
++              if (ret != 0) {
++                      dev_err(codec->dev, "Failed to sync i2cmap%d register cache: %d\n",
++                              i, ret);
++                      regcache_cache_only(ac10x->i2cmap[i], true);
++              }
++      }
++
++      if (! ac10x->i2c101) {
++              return 0;
++      }
++      return ac101_codec_resume(codec);
++}
++
++static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
++      .probe          = ac108_codec_probe,
++      .remove         = ac108_codec_remove,
++      .suspend        = ac108_codec_suspend,
++      .resume         = ac108_codec_resume,
++      .set_bias_level = ac108_set_bias_level,
++      .read           = ac108_codec_read,
++      .write          = ac108_codec_write,
++};
++
++static ssize_t ac108_store(struct device *dev, struct device_attribute *attr,
++                         const char *buf, size_t count)
++{
++      int val = 0, flag = 0;
++      u8 i = 0, reg, num, value_w, value_r[4];
++
++      val = simple_strtol(buf, NULL, 16);
++      flag = (val >> 16) & 0xF;
++
++      if (flag) {
++              reg = (val >> 8) & 0xFF;
++              value_w = val & 0xFF;
++              ac108_multi_write(reg, value_w, ac10x);
++              dev_info(dev, "Write 0x%02x to REG:0x%02x\n", value_w, reg);
++      } else {
++              int k;
++
++              reg = (val >> 8) & 0xFF;
++              num = val & 0xff;
++              dev_info(dev, "\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
++
++              for (k = 0; k < ac10x->codec_cnt; k++)
++                      regcache_cache_bypass(ac10x->i2cmap[k], true);
++
++              do {
++                      memset(value_r, 0, sizeof value_r);
++
++                      for (k = 0; k < ac10x->codec_cnt; k++)
++                              ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]);
++
++                      if (ac10x->codec_cnt >= 2)
++                              dev_info(dev, "REG[0x%02x]: 0x%02x 0x%02x", reg,
++                                       value_r[0], value_r[1]);
++                      else
++                              dev_info(dev, "REG[0x%02x]: 0x%02x", reg, value_r[0]);
++                      reg++;
++
++                      if ((++i == num) || (i % 4 == 0))
++                              dev_info(dev, "\n");
++              } while (i < num);
++
++              for (k = 0; k < ac10x->codec_cnt; k++)
++                      regcache_cache_bypass(ac10x->i2cmap[k], false);
++      }
++
++      return count;
++}
++
++static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++      dev_info(dev, "echo flag|reg|val > ac108\n");
++      dev_info(dev, "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
++      dev_info(dev, "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
++      return 0;
++}
++
++static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store);
++static struct attribute *ac108_debug_attrs[] = {
++      &dev_attr_ac108.attr,
++      NULL,
++};
++static struct attribute_group ac108_debug_attr_group = {
++      .name   = "ac108_debug",
++      .attrs  = ac108_debug_attrs,
++};
++
++static const struct i2c_device_id ac108_i2c_id[] = {
++      { "ac108_0", 0 },
++      { "ac108_1", 1 },
++      { "ac108_2", 2 },
++      { "ac108_3", 3 },
++      { "ac101", AC101_I2C_ID },
++      { }
++};
++MODULE_DEVICE_TABLE(i2c, ac108_i2c_id);
++
++static const struct regmap_config ac108_regmap = {
++      .reg_bits = 8,
++      .val_bits = 8,
++      .reg_stride = 1,
++      .max_register = 0xDF,
++      .cache_type = REGCACHE_FLAT,
++};
++static int ac108_i2c_probe(struct i2c_client *i2c)
++{
++      struct device_node *np = i2c->dev.of_node;
++      const struct i2c_device_id *i2c_id;
++      unsigned int val = 0;
++      int ret = 0, index;
++
++      if (ac10x == NULL) {
++              ac10x = kzalloc(sizeof(struct ac10x_priv), GFP_KERNEL);
++              if (ac10x == NULL) {
++                      dev_err(&i2c->dev, "Unable to allocate ac10x private data\n");
++                      return -ENOMEM;
++              }
++      }
++
++      i2c_id = i2c_match_id(ac108_i2c_id, i2c);
++      index = (int)i2c_id->driver_data;
++      if (index == AC101_I2C_ID) {
++              ac10x->i2c101 = i2c;
++              i2c_set_clientdata(i2c, ac10x);
++              ret = ac101_probe(i2c, i2c_id);
++              if (ret) {
++                      ac10x->i2c101 = NULL;
++                      return ret;
++              }
++              goto __ret;
++      }
++
++      ret = of_property_read_u32(np, "data-protocol", &val);
++      if (ret) {
++              dev_err(&i2c->dev, "Please set data-protocol.\n");
++              return -EINVAL;
++      }
++      ac10x->data_protocol = val;
++
++      if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1;
++      ac10x->tdm_chips_cnt = val;
++
++      dev_info(&i2c->dev, " ac10x i2c_id number: %d\n", index);
++      dev_info(&i2c->dev, " ac10x data protocol: %d\n", ac10x->data_protocol);
++
++      ac10x->i2c[index] = i2c;
++      ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap);
++      if (IS_ERR(ac10x->i2cmap[index])) {
++              ret = PTR_ERR(ac10x->i2cmap[index]);
++              dev_err(&i2c->dev, "Fail to initialize i2cmap%d I/O: %d\n", index, ret);
++              return ret;
++      }
++
++      /*
++       * Writing this register with 0x12
++       * will resets all register to their default state.
++       */
++      regcache_cache_only(ac10x->i2cmap[index], false);
++      
++      ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL);
++      msleep(1);
++
++      /* sync regcache for FLAT type */
++      ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
++
++      ac10x->codec_cnt++;
++      dev_info(&i2c->dev, " ac10x codec count  : %d\n", ac10x->codec_cnt);
++
++      ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
++      if (ret) {
++              dev_err(&i2c->dev, "failed to create attr group\n");
++      }
++
++__ret:
++      /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */
++      if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2)
++      || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
++              /* no playback stream */
++              if (! ac10x->i2c101) {
++                      memset(&ac108_dai[_MASTER_INDEX]->playback, '\0',
++                             sizeof ac108_dai[_MASTER_INDEX]->playback);
++              }
++              ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev,
++                                           &ac10x_soc_codec_driver,
++                                           ac108_dai[_MASTER_INDEX], 1);
++              if (ret < 0) {
++                      dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret);
++              }
++      }
++      return ret;
++}
++
++static void ac108_i2c_remove(struct i2c_client *i2c)
++{
++      if (ac10x->codec != NULL) {
++              snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
++              ac10x->codec = NULL;
++      }
++      if (i2c == ac10x->i2c101) {
++              ac101_remove(ac10x->i2c101);
++              ac10x->i2c101 = NULL;
++              goto __ret;
++      }
++
++      if (i2c == ac10x->i2c[0]) {
++              ac10x->i2c[0] = NULL;
++      }
++      if (i2c == ac10x->i2c[1]) {
++              ac10x->i2c[1] = NULL;
++      }
++
++      sysfs_remove_group(&i2c->dev.kobj, &ac108_debug_attr_group);
++
++__ret:
++      if (!ac10x->i2c[0] && !ac10x->i2c[1] && !ac10x->i2c101) {
++              kfree(ac10x);
++              ac10x = NULL;
++      }
++}
++
++static const struct of_device_id ac108_of_match[] = {
++      { .compatible = "x-power,ac108_0", },
++      { .compatible = "x-power,ac108_1", },
++      { .compatible = "x-power,ac108_2", },
++      { .compatible = "x-power,ac108_3", },
++      { .compatible = "x-power,ac101",   },
++      { }
++};
++MODULE_DEVICE_TABLE(of, ac108_of_match);
++
++static struct i2c_driver ac108_i2c_driver = {
++      .driver = {
++              .name = "ac10x-codec",
++              .of_match_table = ac108_of_match,
++      },
++      .probe =    ac108_i2c_probe,
++      .remove =   ac108_i2c_remove,
++      .id_table = ac108_i2c_id,
++};
++
++module_i2c_driver(ac108_i2c_driver);
++
++MODULE_DESCRIPTION("ASoC AC108 driver");
++MODULE_AUTHOR("Baozhu Zuo<zuobaozhu@gmail.com>");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/sound/soc/codecs/ac108.h
+@@ -0,0 +1,749 @@
++/*
++ * ac108.h --  ac108 ALSA Soc Audio driver
++ *
++ * Author: panjunwen
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#ifndef _AC108_H
++#define _AC108_H
++
++/*** AC108 Codec Register Define***/
++
++//Chip Reset
++#define CHIP_RST                      0x00
++#define CHIP_RST_VAL          0x12
++
++//Power Control
++#define PWR_CTRL1                     0x01
++#define PWR_CTRL2                     0x02
++#define PWR_CTRL3                     0x03
++#define PWR_CTRL4                     0x04
++#define PWR_CTRL5                     0x05
++#define PWR_CTRL6                     0x06
++#define PWR_CTRL7                     0x07
++#define PWR_CTRL8                     0x08
++#define PWR_CTRL9                     0x09
++
++//PLL Configure Control
++#define PLL_CTRL1                     0x10
++#define PLL_CTRL2                     0x11
++#define PLL_CTRL3                     0x12
++#define PLL_CTRL4                     0x13
++#define PLL_CTRL5                     0x14
++#define PLL_CTRL6                     0x16
++#define PLL_CTRL7                     0x17
++#define PLL_LOCK_CTRL         0x18
++
++//System Clock Control
++#define SYSCLK_CTRL                   0x20
++#define MOD_CLK_EN                    0x21
++#define MOD_RST_CTRL          0x22
++#define DSM_CLK_CTRL          0x25
++
++//I2S Common Control
++#define I2S_CTRL                      0x30
++#define I2S_BCLK_CTRL         0x31
++#define I2S_LRCK_CTRL1                0x32
++#define I2S_LRCK_CTRL2                0x33
++#define I2S_FMT_CTRL1         0x34
++#define I2S_FMT_CTRL2         0x35
++#define I2S_FMT_CTRL3         0x36
++
++//I2S TX1 Control
++#define I2S_TX1_CTRL1         0x38
++#define I2S_TX1_CTRL2         0x39
++#define I2S_TX1_CTRL3         0x3A
++#define I2S_TX1_CHMP_CTRL1    0x3C
++#define I2S_TX1_CHMP_CTRL2    0x3D
++#define I2S_TX1_CHMP_CTRL3    0x3E
++#define I2S_TX1_CHMP_CTRL4    0x3F
++
++//I2S TX2 Control
++#define I2S_TX2_CTRL1         0x40
++#define I2S_TX2_CTRL2         0x41
++#define I2S_TX2_CTRL3         0x42
++#define I2S_TX2_CHMP_CTRL1    0x44
++#define I2S_TX2_CHMP_CTRL2    0x45
++#define I2S_TX2_CHMP_CTRL3    0x46
++#define I2S_TX2_CHMP_CTRL4    0x47
++
++//I2S RX1 Control
++#define I2S_RX1_CTRL1         0x50
++#define I2S_RX1_CHMP_CTRL1    0x54
++#define I2S_RX1_CHMP_CTRL2    0x55
++#define I2S_RX1_CHMP_CTRL3    0x56
++#define I2S_RX1_CHMP_CTRL4    0x57
++
++//I2S Loopback Debug
++#define I2S_LPB_DEBUG         0x58
++
++//ADC Common Control
++#define ADC_SPRC                      0x60
++#define ADC_DIG_EN                    0x61
++#define DMIC_EN                               0x62
++#define ADC_DSR                               0x63
++#define ADC_FIR                               0x64
++#define ADC_DDT_CTRL          0x65
++
++//HPF Control
++#define HPF_EN                                0x66
++#define HPF_COEF_REGH1                0x67
++#define HPF_COEF_REGH2                0x68
++#define HPF_COEF_REGL1                0x69
++#define HPF_COEF_REGL2                0x6A
++#define HPF_GAIN_REGH1                0x6B
++#define HPF_GAIN_REGH2                0x6C
++#define HPF_GAIN_REGL1                0x6D
++#define HPF_GAIN_REGL2                0x6E
++
++//ADC Digital Channel Volume Control
++#define ADC1_DVOL_CTRL                0x70
++#define ADC2_DVOL_CTRL                0x71
++#define ADC3_DVOL_CTRL                0x72
++#define ADC4_DVOL_CTRL                0x73
++
++//ADC Digital Mixer Source and Gain Control
++#define ADC1_DMIX_SRC         0x76
++#define ADC2_DMIX_SRC         0x77
++#define ADC3_DMIX_SRC         0x78
++#define ADC4_DMIX_SRC         0x79
++
++//ADC Digital Debug Control
++#define ADC_DIG_DEBUG         0x7F
++
++//I2S Pad Drive Control
++#define I2S_DAT_PADDRV_CTRL   0x80
++#define I2S_CLK_PADDRV_CTRL   0x81
++
++//Analog PGA Control
++#define ANA_PGA1_CTRL         0x90
++#define ANA_PGA2_CTRL         0x91
++#define ANA_PGA3_CTRL         0x92
++#define ANA_PGA4_CTRL         0x93
++
++//MIC Offset Control
++#define MIC_OFFSET_CTRL1      0x96
++#define MIC_OFFSET_CTRL2      0x97
++#define MIC1_OFFSET_STATU1    0x98
++#define MIC1_OFFSET_STATU2    0x99
++#define MIC2_OFFSET_STATU1    0x9A
++#define MIC2_OFFSET_STATU2    0x9B
++#define MIC3_OFFSET_STATU1    0x9C
++#define MIC3_OFFSET_STATU2    0x9D
++#define MIC4_OFFSET_STATU1    0x9E
++#define MIC4_OFFSET_STATU2    0x9F
++
++//ADC1 Analog Control
++#define ANA_ADC1_CTRL1                0xA0
++#define ANA_ADC1_CTRL2                0xA1
++#define ANA_ADC1_CTRL3                0xA2
++#define ANA_ADC1_CTRL4                0xA3
++#define ANA_ADC1_CTRL5                0xA4
++#define ANA_ADC1_CTRL6                0xA5
++#define ANA_ADC1_CTRL7                0xA6
++
++//ADC2 Analog Control
++#define ANA_ADC2_CTRL1                0xA7
++#define ANA_ADC2_CTRL2                0xA8
++#define ANA_ADC2_CTRL3                0xA9
++#define ANA_ADC2_CTRL4                0xAA
++#define ANA_ADC2_CTRL5                0xAB
++#define ANA_ADC2_CTRL6                0xAC
++#define ANA_ADC2_CTRL7                0xAD
++
++//ADC3 Analog Control
++#define ANA_ADC3_CTRL1                0xAE
++#define ANA_ADC3_CTRL2                0xAF
++#define ANA_ADC3_CTRL3                0xB0
++#define ANA_ADC3_CTRL4                0xB1
++#define ANA_ADC3_CTRL5                0xB2
++#define ANA_ADC3_CTRL6                0xB3
++#define ANA_ADC3_CTRL7                0xB4
++
++//ADC4 Analog Control
++#define ANA_ADC4_CTRL1                0xB5
++#define ANA_ADC4_CTRL2                0xB6
++#define ANA_ADC4_CTRL3                0xB7
++#define ANA_ADC4_CTRL4                0xB8
++#define ANA_ADC4_CTRL5                0xB9
++#define ANA_ADC4_CTRL6                0xBA
++#define ANA_ADC4_CTRL7                0xBB
++
++//GPIO Configure
++#define GPIO_CFG1                     0xC0
++#define GPIO_CFG2                     0xC1
++#define GPIO_DAT                      0xC2
++#define GPIO_DRV                      0xC3
++#define GPIO_PULL                     0xC4
++#define GPIO_INT_CFG          0xC5
++#define GPIO_INT_EN                   0xC6
++#define GPIO_INT_STATUS               0xC7
++
++//Misc
++#define BGTC_DAT                      0xD1
++#define BGVC_DAT                      0xD2
++#define PRNG_CLK_CTRL         0xDF
++
++/*** AC108 Codec Register Bit Define***/
++
++/*PWR_CTRL1*/
++#define CP12_CTRL                             4
++#define CP12_SENSE_SELECT             3
++
++/*PWR_CTRL2*/
++#define CP12_SENSE_FILT                       6
++#define CP12_COMP_FF_EN                       3
++#define CP12_FORCE_ENABLE             2
++#define CP12_FORCE_RSTB                       1
++
++/*PWR_CTRL3*/
++#define LDO33DIG_CTRL                 0
++
++/*PWR_CTRL6*/
++#define LDO33ANA_2XHDRM                       2
++#define LDO33ANA_ENABLE                       0
++
++/*PWR_CTRL7*/
++#define VREF_SEL                              3
++#define VREF_FASTSTART_ENABLE 1
++#define VREF_ENABLE                           0
++
++/*PWR_CTRL9*/
++#define VREFP_FASTSTART_ENABLE        7
++#define VREFP_RESCTRL                 5
++#define VREFP_LPMODE                  4
++#define IGEN_TRIM                             1
++#define VREFP_ENABLE                  0
++
++/*PLL_CTRL1*/
++#define PLL_IBIAS                             4
++#define PLL_NDET                              3
++#define PLL_LOCKED_STATUS             2
++#define PLL_COM_EN                            1
++#define PLL_EN                                        0
++
++/*PLL_CTRL2*/
++#define PLL_PREDIV2                           5
++#define PLL_PREDIV1                           0
++
++/*PLL_CTRL3*/
++#define PLL_LOOPDIV_MSB                       0
++
++/*PLL_CTRL4*/
++#define PLL_LOOPDIV_LSB                       0
++
++/*PLL_CTRL5*/
++#define PLL_POSTDIV2                  5
++#define PLL_POSTDIV1                  0
++
++/*PLL_CTRL6*/
++#define PLL_LDO                                       6
++#define PLL_CP                                        0
++
++/*PLL_CTRL7*/
++#define PLL_CAP                                       6
++#define PLL_RES                                       4
++#define PLL_TEST_EN                           0
++
++/*PLL_LOCK_CTRL*/
++#define LOCK_LEVEL1                           2
++#define LOCK_LEVEL2                           1
++#define PLL_LOCK_EN                           0
++
++/*SYSCLK_CTRL*/
++#define PLLCLK_EN                             7
++#define PLLCLK_SRC                            4
++#define SYSCLK_SRC                            3
++#define SYSCLK_EN                             0
++
++/*MOD_CLK_EN & MOD_RST_CTRL*/
++#define I2S                                           7
++#define ADC_DIGITAL                           4
++#define MIC_OFFSET_CALIBRATION        1
++#define ADC_ANALOG                            0
++
++/*DSM_CLK_CTRL*/
++#define MIC_OFFSET_DIV                        4
++#define DSM_CLK_SEL                           0
++
++/*I2S_CTRL*/
++#define BCLK_IOEN                             7
++#define LRCK_IOEN                             6
++#define SDO2_EN                                       5
++#define SDO1_EN                                       4
++#define TXEN                                  2
++#define RXEN                                  1
++#define GEN                                           0
++
++/*I2S_BCLK_CTRL*/
++#define EDGE_TRANSFER                 5
++#define BCLK_POLARITY                 4
++#define BCLKDIV                                       0
++
++/*I2S_LRCK_CTRL1*/
++#define LRCK_POLARITY                 4
++#define LRCK_PERIODH                  0
++
++/*I2S_LRCK_CTRL2*/
++#define LRCK_PERIODL                  0
++
++/*I2S_FMT_CTRL1*/
++#define ENCD_SEL                              6
++#define MODE_SEL                              4
++#define TX2_OFFSET                            3
++#define TX1_OFFSET                            2
++#define TX_SLOT_HIZ                           1
++#define TX_STATE                              0
++
++/*I2S_FMT_CTRL2*/
++#define SLOT_WIDTH_SEL                        4
++#define SAMPLE_RESOLUTION             0
++
++/*I2S_FMT_CTRL3*/
++#define TX_MLS                                        7
++#define SEXT                                  5
++#define OUT2_MUTE                             4
++#define OUT1_MUTE                             3
++#define LRCK_WIDTH                            2
++#define TX_PDM                                        0
++
++/*I2S_TX1_CTRL1*/
++#define TX1_CHSEL                             0
++
++/*I2S_TX1_CTRL2*/
++#define TX1_CH8_EN                            7
++#define TX1_CH7_EN                            6
++#define TX1_CH6_EN                            5
++#define TX1_CH5_EN                            4
++#define TX1_CH4_EN                            3
++#define TX1_CH3_EN                            2
++#define TX1_CH2_EN                            1
++#define TX1_CH1_EN                            0
++
++/*I2S_TX1_CTRL3*/
++#define TX1_CH16_EN                           7
++#define TX1_CH15_EN                           6
++#define TX1_CH14_EN                           5
++#define TX1_CH13_EN                           4
++#define TX1_CH12_EN                           3
++#define TX1_CH11_EN                           2
++#define TX1_CH10_EN                           1
++#define TX1_CH9_EN                            0
++
++/*I2S_TX1_CHMP_CTRL1*/
++#define TX1_CH4_MAP                           6
++#define TX1_CH3_MAP                           4
++#define TX1_CH2_MAP                           2
++#define TX1_CH1_MAP                           0
++
++/*I2S_TX1_CHMP_CTRL2*/
++#define TX1_CH8_MAP                           6
++#define TX1_CH7_MAP                           4
++#define TX1_CH6_MAP                           2
++#define TX1_CH5_MAP                           0
++
++/*I2S_TX1_CHMP_CTRL3*/
++#define TX1_CH12_MAP                  6
++#define TX1_CH11_MAP                  4
++#define TX1_CH10_MAP                  2
++#define TX1_CH9_MAP                           0
++
++/*I2S_TX1_CHMP_CTRL4*/
++#define TX1_CH16_MAP                  6
++#define TX1_CH15_MAP                  4
++#define TX1_CH14_MAP                  2
++#define TX1_CH13_MAP                  0
++
++/*I2S_TX2_CTRL1*/
++#define TX2_CHSEL                             0
++
++/*I2S_TX2_CHMP_CTRL1*/
++#define TX2_CH4_MAP                           6
++#define TX2_CH3_MAP                           4
++#define TX2_CH2_MAP                           2
++#define TX2_CH1_MAP                           0
++
++/*I2S_TX2_CHMP_CTRL2*/
++#define TX2_CH8_MAP                           6
++#define TX2_CH7_MAP                           4
++#define TX2_CH6_MAP                           2
++#define TX2_CH5_MAP                           0
++
++/*I2S_TX2_CHMP_CTRL3*/
++#define TX2_CH12_MAP                  6
++#define TX2_CH11_MAP                  4
++#define TX2_CH10_MAP                  2
++#define TX2_CH9_MAP                           0
++
++/*I2S_TX2_CHMP_CTRL4*/
++#define TX2_CH16_MAP                  6
++#define TX2_CH15_MAP                  4
++#define TX2_CH14_MAP                  2
++#define TX2_CH13_MAP                  0
++
++/*I2S_RX1_CTRL1*/
++#define RX1_CHSEL                             0
++
++/*I2S_RX1_CHMP_CTRL1*/
++#define RX1_CH4_MAP                           6
++#define RX1_CH3_MAP                           4
++#define RX1_CH2_MAP                           2
++#define RX1_CH1_MAP                           0
++
++/*I2S_RX1_CHMP_CTRL2*/
++#define RX1_CH8_MAP                           6
++#define RX1_CH7_MAP                           4
++#define RX1_CH6_MAP                           2
++#define RX1_CH5_MAP                           0
++
++/*I2S_RX1_CHMP_CTRL3*/
++#define RX1_CH12_MAP                  6
++#define RX1_CH11_MAP                  4
++#define RX1_CH10_MAP                  2
++#define RX1_CH9_MAP                           0
++
++/*I2S_RX1_CHMP_CTRL4*/
++#define RX1_CH16_MAP                  6
++#define RX1_CH15_MAP                  4
++#define RX1_CH14_MAP                  2
++#define RX1_CH13_MAP                  0
++
++/*I2S_LPB_DEBUG*/
++#define I2S_LPB_DEBUG_EN              0
++
++/*ADC_SPRC*/
++#define ADC_FS_I2S1                           0
++
++/*ADC_DIG_EN*/
++#define DG_EN                                 4
++#define ENAD4                                 3
++#define ENAD3                                 2
++#define ENAD2                                 1
++#define ENAD1                                 0
++
++/*DMIC_EN*/
++#define DMIC2_EN                              1
++#define DMIC1_EN                              0
++
++/*ADC_DSR*/
++#define DIG_ADC4_SRS                  6
++#define DIG_ADC3_SRS                  4
++#define DIG_ADC2_SRS                  2
++#define DIG_ADC1_SRS                  0
++
++/*ADC_DDT_CTRL*/
++#define ADOUT_DLY_EN                  2
++#define ADOUT_DTS                             0
++
++/*HPF_EN*/
++#define DIG_ADC4_HPF_EN                       3
++#define DIG_ADC3_HPF_EN                       2
++#define DIG_ADC2_HPF_EN                       1
++#define DIG_ADC1_HPF_EN                       0
++
++/*ADC1_DMIX_SRC*/
++#define ADC1_ADC4_DMXL_GC             7
++#define ADC1_ADC3_DMXL_GC             6
++#define ADC1_ADC2_DMXL_GC             5
++#define ADC1_ADC1_DMXL_GC             4
++#define ADC1_ADC4_DMXL_SRC            3
++#define ADC1_ADC3_DMXL_SRC            2
++#define ADC1_ADC2_DMXL_SRC            1
++#define ADC1_ADC1_DMXL_SRC            0
++
++/*ADC2_DMIX_SRC*/
++#define ADC2_ADC4_DMXL_GC             7
++#define ADC2_ADC3_DMXL_GC             6
++#define ADC2_ADC2_DMXL_GC             5
++#define ADC2_ADC1_DMXL_GC             4
++#define ADC2_ADC4_DMXL_SRC            3
++#define ADC2_ADC3_DMXL_SRC            2
++#define ADC2_ADC2_DMXL_SRC            1
++#define ADC2_ADC1_DMXL_SRC            0
++
++/*ADC3_DMIX_SRC*/
++#define ADC3_ADC4_DMXL_GC             7
++#define ADC3_ADC3_DMXL_GC             6
++#define ADC3_ADC2_DMXL_GC             5
++#define ADC3_ADC1_DMXL_GC             4
++#define ADC3_ADC4_DMXL_SRC            3
++#define ADC3_ADC3_DMXL_SRC            2
++#define ADC3_ADC2_DMXL_SRC            1
++#define ADC3_ADC1_DMXL_SRC            0
++
++/*ADC4_DMIX_SRC*/
++#define ADC4_ADC4_DMXL_GC             7
++#define ADC4_ADC3_DMXL_GC             6
++#define ADC4_ADC2_DMXL_GC             5
++#define ADC4_ADC1_DMXL_GC             4
++#define ADC4_ADC4_DMXL_SRC            3
++#define ADC4_ADC3_DMXL_SRC            2
++#define ADC4_ADC2_DMXL_SRC            1
++#define ADC4_ADC1_DMXL_SRC            0
++
++/*ADC_DIG_DEBUG*/
++#define ADC_PTN_SEL                           0
++
++/*I2S_DAT_PADDRV_CTRL*/
++#define TX2_DAT_DRV                           4
++#define TX1_DAT_DRV                           0
++
++/*I2S_CLK_PADDRV_CTRL*/
++#define LRCK_DRV                              4
++#define BCLK_DRV                              0
++
++/*ANA_PGA1_CTRL*/
++#define ADC1_ANALOG_PGA                       1
++#define ADC1_ANALOG_PGA_STEP  0
++
++/*ANA_PGA2_CTRL*/
++#define ADC2_ANALOG_PGA                       1
++#define ADC2_ANALOG_PGA_STEP  0
++
++/*ANA_PGA3_CTRL*/
++#define ADC3_ANALOG_PGA                       1
++#define ADC3_ANALOG_PGA_STEP  0
++
++/*ANA_PGA4_CTRL*/
++#define ADC4_ANALOG_PGA                       1
++#define ADC4_ANALOG_PGA_STEP  0
++
++
++/*MIC_OFFSET_CTRL1*/
++#define MIC_OFFSET_CAL_EN4            3
++#define MIC_OFFSET_CAL_EN3            2
++#define MIC_OFFSET_CAL_EN2            1
++#define MIC_OFFSET_CAL_EN1            0
++
++/*MIC_OFFSET_CTRL2*/
++#define MIC_OFFSET_CAL_GAIN           3
++#define MIC_OFFSET_CAL_CHANNEL        1
++#define MIC_OFFSET_CAL_EN_ONCE        0
++
++/*MIC1_OFFSET_STATU1*/
++#define MIC1_OFFSET_CAL_DONE  7
++#define MIC1_OFFSET_CAL_RUN_STA       6
++#define MIC1_OFFSET_MSB                       0
++
++/*MIC1_OFFSET_STATU2*/
++#define MIC1_OFFSET_LSB                       0
++
++/*MIC2_OFFSET_STATU1*/
++#define MIC2_OFFSET_CAL_DONE  7
++#define MIC2_OFFSET_CAL_RUN_STA       6
++#define MIC2_OFFSET_MSB                       0
++
++/*MIC2_OFFSET_STATU2*/
++#define MIC2_OFFSET_LSB                       0
++
++/*MIC3_OFFSET_STATU1*/
++#define MIC3_OFFSET_CAL_DONE  7
++#define MIC3_OFFSET_CAL_RUN_STA       6
++#define MIC3_OFFSET_MSB                       0
++
++/*MIC3_OFFSET_STATU2*/
++#define MIC3_OFFSET_LSB                       0
++
++/*MIC4_OFFSET_STATU1*/
++#define MIC4_OFFSET_CAL_DONE  7
++#define MIC4_OFFSET_CAL_RUN_STA       6
++#define MIC4_OFFSET_MSB                       0
++
++/*MIC4_OFFSET_STATU2*/
++#define MIC4_OFFSET_LSB                       0
++
++/*ANA_ADC1_CTRL1*/
++#define ADC1_PGA_BYPASS                       7
++#define ADC1_PGA_BYP_RCM              6
++#define ADC1_PGA_CTRL_RCM             4
++#define ADC1_PGA_MUTE                 3
++#define ADC1_DSM_ENABLE                       2
++#define ADC1_PGA_ENABLE                       1
++#define ADC1_MICBIAS_EN                       0
++
++/*ANA_ADC1_CTRL3*/
++#define ADC1_ANA_CAL_EN                       5
++#define ADC1_SEL_OUT_EDGE             3
++#define ADC1_DSM_DISABLE              2
++#define ADC1_VREFP_DISABLE            1
++#define ADC1_AAF_DISABLE              0
++
++/*ANA_ADC1_CTRL6*/
++#define PGA_CTRL_TC                           6
++#define PGA_CTRL_RC                           4
++#define PGA_CTRL_I_LIN                        2
++#define PGA_CTRL_I_IN                 0
++
++/*ANA_ADC1_CTRL7*/
++#define PGA_CTRL_HI_Z                 7
++#define PGA_CTRL_SHORT_RF             6
++#define PGA_CTRL_VCM_VG                       4
++#define PGA_CTRL_VCM_IN                       0
++
++/*ANA_ADC2_CTRL1*/
++#define ADC2_PGA_BYPASS                       7
++#define ADC2_PGA_BYP_RCM              6
++#define ADC2_PGA_CTRL_RCM             4
++#define ADC2_PGA_MUTE                 3
++#define ADC2_DSM_ENABLE                       2
++#define ADC2_PGA_ENABLE                       1
++#define ADC2_MICBIAS_EN                       0
++
++/*ANA_ADC2_CTRL3*/
++#define ADC2_ANA_CAL_EN                       5
++#define ADC2_SEL_OUT_EDGE             3
++#define ADC2_DSM_DISABLE              2
++#define ADC2_VREFP_DISABLE            1
++#define ADC2_AAF_DISABLE              0
++
++/*ANA_ADC2_CTRL6*/
++#define PGA_CTRL_IBOOST                       7
++#define PGA_CTRL_IQCTRL                       6
++#define PGA_CTRL_OABIAS                       4
++#define PGA_CTRL_CMLP_DIS             3
++#define PGA_CTRL_PDB_RIN              2
++#define PGA_CTRL_PEAKDET              0
++
++/*ANA_ADC2_CTRL7*/
++#define AAF_LPMODE_EN                 7
++#define AAF_STG2_IB_SEL                       4
++#define AAFDSM_IB_DIV2                        3
++#define AAF_STG1_IB_SEL                       0
++
++/*ANA_ADC3_CTRL1*/
++#define ADC3_PGA_BYPASS                       7
++#define ADC3_PGA_BYP_RCM              6
++#define ADC3_PGA_CTRL_RCM             4
++#define ADC3_PGA_MUTE                 3
++#define ADC3_DSM_ENABLE                       2
++#define ADC3_PGA_ENABLE                       1
++#define ADC3_MICBIAS_EN                       0
++
++/*ANA_ADC3_CTRL3*/
++#define ADC3_ANA_CAL_EN                       5
++#define ADC3_INVERT_CLK                       4
++#define ADC3_SEL_OUT_EDGE             3
++#define ADC3_DSM_DISABLE              2
++#define ADC3_VREFP_DISABLE            1
++#define ADC3_AAF_DISABLE              0
++
++/*ANA_ADC3_CTRL7*/
++#define DSM_COMP_IB_SEL                       6
++#define DSM_OTA_CTRL                  4
++#define DSM_LPMODE                            3
++#define DSM_OTA_IB_SEL                        0
++
++/*ANA_ADC4_CTRL1*/
++#define ADC4_PGA_BYPASS                       7
++#define ADC4_PGA_BYP_RCM              6
++#define ADC4_PGA_CTRL_RCM             4
++#define ADC4_PGA_MUTE                 3
++#define ADC4_DSM_ENABLE                       2
++#define ADC4_PGA_ENABLE                       1
++#define ADC4_MICBIAS_EN                       0
++
++/*ANA_ADC4_CTRL3*/
++#define ADC4_ANA_CAL_EN                       5
++#define ADC4_SEL_OUT_EDGE             3
++#define ADC4_DSM_DISABLE              2
++#define ADC4_VREFP_DISABLE            1
++#define ADC4_AAF_DISABLE              0
++
++/*ANA_ADC4_CTRL6*/
++#define DSM_DEMOFF                            5
++#define DSM_EN_DITHER                 4
++#define DSM_VREFP_LPMODE              2
++#define DSM_VREFP_OUTCTRL             0
++
++/*ANA_ADC4_CTRL7*/
++#define CK8M_EN                                       5
++#define OSC_EN                                        4
++#define ADC4_CLK_GATING                       3
++#define ADC3_CLK_GATING                       2
++#define ADC2_CLK_GATING                       1
++#define ADC1_CLK_GATING                       0
++
++/*GPIO_CFG1*/
++#define GPIO2_SELECT                  4
++#define GPIO1_SELECT                  0
++
++/*GPIO_CFG2*/
++#define GPIO4_SELECT                  4
++#define GPIO3_SELECT                  0
++
++/*GPIO_DAT*///order???
++#define GPIO4_DAT                             3
++#define GPIO3_DAT                             2
++#define GPIO2_DAT                             1
++#define GPIO1_DAT                             0
++
++/*GPIO_DRV*/
++#define GPIO4_DRV                             6
++#define GPIO3_DRV                             4
++#define GPIO2_DRV                             2
++#define GPIO1_DRV                             0
++
++/*GPIO_PULL*/
++#define GPIO4_PULL                            6
++#define GPIO3_PULL                            4
++#define GPIO2_PULL                            2
++#define GPIO1_PULL                            0
++
++/*GPIO_INT_CFG*/
++#define GPIO4_EINT_CFG                        6
++#define GPIO3_EINT_CFG                        4
++#define GPIO2_EINT_CFG                        2
++#define GPIO1_EINT_CFG                        0
++
++/*GPIO_INT_EN*///order???
++#define GPIO4_EINT_EN                 3
++#define GPIO3_EINT_EN                 2
++#define GPIO2_EINT_EN                 1
++#define GPIO1_EINT_EN                 0
++
++/*GPIO_INT_STATUS*///order???
++#define GPIO4_EINT_STA                        3
++#define GPIO3_EINT_STA                        2
++#define GPIO2_EINT_STA                        1
++#define GPIO1_EINT_STA                        0
++
++/*PRNG_CLK_CTRL*/
++#define PRNG_CLK_EN                           1
++#define PRNG_CLK_POS                  0
++
++/*** Some Config Value ***/
++
++//[SYSCLK_CTRL]: PLLCLK_SRC
++#define PLLCLK_SRC_MCLK                       0
++#define PLLCLK_SRC_BCLK                       1
++#define PLLCLK_SRC_GPIO2              2
++#define PLLCLK_SRC_GPIO3              3
++
++//[SYSCLK_CTRL]: SYSCLK_SRC
++#define SYSCLK_SRC_MCLK                       0
++#define SYSCLK_SRC_PLL                        1
++
++//I2S BCLK POLARITY Control
++#define BCLK_NORMAL_DRIVE_N_SAMPLE_P  0
++#define BCLK_INVERT_DRIVE_P_SAMPLE_N  1
++
++//I2S LRCK POLARITY Control
++#define       LRCK_LEFT_LOW_RIGHT_HIGH                0
++#define LRCK_LEFT_HIGH_RIGHT_LOW              1
++
++//I2S Format Selection
++#define PCM_FORMAT                                            0
++#define LEFT_JUSTIFIED_FORMAT                 1
++#define RIGHT_JUSTIFIED_FORMAT                        2
++
++//I2S data protocol types
++
++#define IS_ENCODING_MODE               0
++
++#endif
++
+--- /dev/null
++++ b/sound/soc/codecs/ac10x.h
+@@ -0,0 +1,152 @@
++/*
++ * ac10x.h
++ *
++ * (C) Copyright 2017-2018
++ * Seeed Technology Co., Ltd. <www.seeedstudio.com>
++ *
++ * PeterYang <linsheng.yang@seeed.cc>
++ *
++ * (C) Copyright 2010-2017
++ * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
++ * huangxin <huangxin@reuuimllatech.com>
++ *
++ * some simple description for this code
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ */
++#ifndef __AC10X_H__
++#define __AC10X_H__
++
++#define AC101_I2C_ID          4
++#define _MASTER_AC108         0
++#define _MASTER_AC101         1
++#define _MASTER_MULTI_CODEC   _MASTER_AC101
++
++/* enable headset detecting & headset button pressing */
++#define CONFIG_AC101_SWITCH_DETECT
++
++/* obsolete */
++#define CONFIG_AC10X_TRIG_LOCK        0
++
++#ifdef AC101_DEBG
++    #define AC101_DBG(format,args...)  printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args)
++#else
++    #define AC101_DBG(...)
++#endif
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0)
++#define __NO_SND_SOC_CODEC_DRV     1
++#else
++#define __NO_SND_SOC_CODEC_DRV     0
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
++#if __has_attribute(__fallthrough__)
++# define fallthrough                    __attribute__((__fallthrough__))
++#else
++# define fallthrough                    do {} while (0)  /* fallthrough */
++#endif
++#endif
++
++#if __NO_SND_SOC_CODEC_DRV
++#define codec                      component
++#define snd_soc_codec              snd_soc_component
++#define snd_soc_codec_driver       snd_soc_component_driver
++#define snd_soc_codec_get_drvdata  snd_soc_component_get_drvdata
++#define snd_soc_codec_get_dapm     snd_soc_component_get_dapm
++#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level
++#define snd_soc_kcontrol_codec     snd_soc_kcontrol_component
++#define snd_soc_read               snd_soc_component_read32
++#define snd_soc_register_codec     snd_soc_register_component
++#define snd_soc_unregister_codec   snd_soc_unregister_component
++#define snd_soc_update_bits        snd_soc_component_update_bits
++#define snd_soc_write              snd_soc_component_write
++#define snd_soc_add_codec_controls snd_soc_add_component_controls
++#endif
++
++
++#ifdef CONFIG_AC101_SWITCH_DETECT
++enum headphone_mode_u {
++      HEADPHONE_IDLE,
++      FOUR_HEADPHONE_PLUGIN,
++      THREE_HEADPHONE_PLUGIN,
++};
++#endif
++
++struct ac10x_priv {
++      struct i2c_client *i2c[4];
++      struct regmap* i2cmap[4];
++      int codec_cnt;
++      unsigned sysclk;
++#define _FREQ_24_576K         24576000
++#define _FREQ_22_579K         22579200
++      unsigned mclk;  /* master clock or aif_clock/aclk */
++      int clk_id;
++      unsigned char i2s_mode;
++      unsigned char data_protocol;
++      struct delayed_work dlywork;
++      int tdm_chips_cnt;
++      int sysclk_en;
++
++      /* member for ac101 .begin */
++      struct snd_soc_codec *codec;
++      struct i2c_client *i2c101;
++      struct regmap* regmap101;
++
++      struct mutex dac_mutex;
++      u8 dac_enable;
++      spinlock_t lock;
++      u8 aif1_clken;
++
++      struct work_struct codec_resume;
++      struct gpio_desc* gpiod_spk_amp_gate;
++
++      #ifdef CONFIG_AC101_SWITCH_DETECT
++      struct gpio_desc* gpiod_irq;
++      long irq;
++      volatile int irq_cntr;
++      volatile int pullout_cntr;
++      volatile int state;
++
++      enum headphone_mode_u mode;
++      struct work_struct work_switch;
++      struct work_struct work_clear_irq;
++
++      struct input_dev* inpdev;
++      #endif
++      /* member for ac101 .end */
++};
++
++
++/* AC101 DAI operations */
++int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai);
++int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
++int ac101_hw_params(struct snd_pcm_substream *substream,
++      struct snd_pcm_hw_params *params,
++      struct snd_soc_dai *codec_dai);
++int ac101_trigger(struct snd_pcm_substream *substream, int cmd,
++                struct snd_soc_dai *dai);
++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute);
++
++/* codec driver specific */
++int ac101_codec_probe(struct snd_soc_codec *codec);
++int ac101_codec_remove(struct snd_soc_codec *codec);
++int ac101_codec_suspend(struct snd_soc_codec *codec);
++int ac101_codec_resume(struct snd_soc_codec *codec);
++int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level);
++
++/* i2c device specific */
++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id);
++void ac101_shutdown(struct i2c_client *i2c);
++int ac101_remove(struct i2c_client *i2c);
++
++int ac10x_fill_regcache(struct device* dev, struct regmap* map);
++
++#endif//__AC10X_H__
diff --git a/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch b/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch
new file mode 100644 (file)
index 0000000..f2fb206
--- /dev/null
@@ -0,0 +1,1169 @@
+From f03b7c834baef87e4f740e10a8bbcbfc57bd985a Mon Sep 17 00:00:00 2001
+From: Xingyu Wu <xingyu.wu@starfivetech.com>
+Date: Thu, 15 Jun 2023 11:32:50 +0800
+Subject: [PATCH 080/116] ASoC: starfive: Add SPDIF and PCM driver
+
+Add SPDIF and SPDIF-PCM driver for StarFive JH7110.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ sound/soc/starfive/Kconfig            |  17 +
+ sound/soc/starfive/Makefile           |   5 +
+ sound/soc/starfive/jh7110_spdif.c     | 568 ++++++++++++++++++++++++++
+ sound/soc/starfive/jh7110_spdif.h     | 196 +++++++++
+ sound/soc/starfive/jh7110_spdif_pcm.c | 339 +++++++++++++++
+ 5 files changed, 1125 insertions(+)
+ create mode 100644 sound/soc/starfive/jh7110_spdif.c
+ create mode 100644 sound/soc/starfive/jh7110_spdif.h
+ create mode 100644 sound/soc/starfive/jh7110_spdif_pcm.c
+
+--- a/sound/soc/starfive/Kconfig
++++ b/sound/soc/starfive/Kconfig
+@@ -16,6 +16,23 @@ config SND_SOC_JH7110_PWMDAC
+        Say Y or M if you want to add support for StarFive JH7110
+        PWM-DAC driver.
++config SND_SOC_JH7110_SPDIF
++      tristate "JH7110 SPDIF module"
++      depends on HAVE_CLK && SND_SOC_STARFIVE
++      select SND_SOC_GENERIC_DMAENGINE_PCM
++      select REGMAP_MMIO
++      help
++        Say Y or M if you want to add support for SPDIF driver of StarFive
++        JH7110 SoC.
++
++config SND_SOC_JH7110_SPDIF_PCM
++      bool "PCM PIO extension for JH7110 SPDIF"
++      depends on SND_SOC_JH7110_SPDIF
++      default y if SND_SOC_JH7110_SPDIF
++      help
++        Say Y or N if you want to add a custom ALSA extension that registers
++        a PCM and uses PIO to transfer data.
++
+ config SND_SOC_JH7110_TDM
+       tristate "JH7110 TDM device driver"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+--- a/sound/soc/starfive/Makefile
++++ b/sound/soc/starfive/Makefile
+@@ -1,3 +1,8 @@
+ # StarFive Platform Support
+ obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
++
++obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o
++spdif-y := jh7110_spdif.o
++spdif-$(CONFIG_SND_SOC_JH7110_SPDIF_PCM) += jh7110_spdif_pcm.o
++
+ obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_spdif.c
+@@ -0,0 +1,568 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * SPDIF driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <sound/core.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include "jh7110_spdif.h"
++
++static irqreturn_t spdif_irq_handler(int irq, void *dev_id)
++{
++      struct sf_spdif_dev *dev = dev_id;
++      bool irq_valid = false;
++      unsigned int intr;
++      unsigned int stat;
++
++      regmap_read(dev->regmap, SPDIF_INT_REG, &intr);
++      regmap_read(dev->regmap, SPDIF_STAT_REG, &stat);
++      regmap_update_bits(dev->regmap, SPDIF_CTRL, SPDIF_MASK_ENABLE, 0);
++      regmap_update_bits(dev->regmap, SPDIF_INT_REG, SPDIF_INT_REG_BIT, 0);
++
++      if ((stat & SPDIF_EMPTY_FLAG) || (stat & SPDIF_AEMPTY_FLAG)) {
++              sf_spdif_pcm_push_tx(dev);
++              irq_valid = true;
++      }
++
++      if ((stat & SPDIF_FULL_FLAG) || (stat & SPDIF_AFULL_FLAG)) {
++              sf_spdif_pcm_pop_rx(dev);
++              irq_valid = true;
++      }
++
++      if (stat & SPDIF_PARITY_FLAG)
++              irq_valid = true;
++
++      if (stat & SPDIF_UNDERR_FLAG)
++              irq_valid = true;
++
++      if (stat & SPDIF_OVRERR_FLAG)
++              irq_valid = true;
++
++      if (stat & SPDIF_SYNCERR_FLAG)
++              irq_valid = true;
++
++      if (stat & SPDIF_LOCK_FLAG)
++              irq_valid = true;
++
++      if (stat & SPDIF_BEGIN_FLAG)
++              irq_valid = true;
++
++      if (stat & SPDIF_RIGHT_LEFT)
++              irq_valid = true;
++
++      regmap_update_bits(dev->regmap, SPDIF_CTRL,
++              SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE);
++
++      if (irq_valid)
++              return IRQ_HANDLED;
++      else
++              return IRQ_NONE;
++}
++
++static int sf_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
++                          struct snd_soc_dai *dai)
++{
++      struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
++      bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
++
++      if (tx) {
++              /* tx mode */
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_TR_MODE, SPDIF_TR_MODE);
++
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_MASK_FIFO, SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK);
++      } else {
++              /* rx mode */
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_TR_MODE, 0);
++
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_MASK_FIFO, SPDIF_FULL_MASK | SPDIF_AFULL_MASK);
++      }
++
++      switch (cmd) {
++      case SNDRV_PCM_TRIGGER_START:
++      case SNDRV_PCM_TRIGGER_RESUME:
++      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++              /* clock recovery form the SPDIF data stream  0:clk_enable */
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_CLK_ENABLE, 0);
++
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_ENABLE, SPDIF_ENABLE);
++              break;
++      case SNDRV_PCM_TRIGGER_STOP:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++              /* clock recovery form the SPDIF data stream  1:power save mode */
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_ENABLE, 0);
++              break;
++      default:
++              dev_err(dai->dev, "%s L.%d cmd:%d\n", __func__, __LINE__, cmd);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int sf_spdif_hw_params(struct snd_pcm_substream *substream,
++                            struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++      struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
++      unsigned int channels = params_channels(params);
++      unsigned int rate = params_rate(params);
++      unsigned int format = params_format(params);
++      unsigned int tsamplerate;
++      unsigned int mclk;
++      unsigned int audio_root;
++      int ret;
++
++      switch (channels) {
++      case 1:
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_CHANNEL_MODE, SPDIF_CHANNEL_MODE);
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_DUPLICATE, SPDIF_DUPLICATE);
++              spdif->channels = false;
++              break;
++      case 2:
++              regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                                 SPDIF_CHANNEL_MODE, 0);
++              spdif->channels = true;
++              break;
++      default:
++              dev_err(dai->dev, "invalid channels number\n");
++              return -EINVAL;
++      }
++
++      switch (format) {
++      case SNDRV_PCM_FORMAT_S16_LE:
++      case SNDRV_PCM_FORMAT_S24_LE:
++      case SNDRV_PCM_FORMAT_S24_3LE:
++      case SNDRV_PCM_FORMAT_S32_LE:
++              break;
++      default:
++              dev_err(dai->dev, "invalid format\n");
++              return -EINVAL;
++      }
++
++      switch (rate) {
++      case 8000:
++              break;
++      case 11025:
++              audio_root = 148500000;
++              /* 11025 * 512 = 5644800 */
++              /* But now pll2 is 1188m and mclk should be 5711539 closely. */
++              mclk = 5711539;
++              break;
++      case 16000:
++              break;
++      case 22050:
++              audio_root = 148500000;
++              mclk = 11423077;
++              break;
++      default:
++              dev_err(dai->dev, "channel:%d sample rate:%d\n", channels, rate);
++              return -EINVAL;
++      }
++
++      /* use mclk_inner clock from 1188m PLL2 will be better about 11k and 22k*/
++      if ((rate == 11025) || (rate == 22050)) {
++              ret = clk_set_parent(spdif->mclk, spdif->mclk_inner);
++              if (ret) {
++                      dev_err(dai->dev,
++                              "failed to set parent to mclk_inner ret=%d\n", ret);
++                      goto fail_ext;
++              }
++
++              ret = clk_set_rate(spdif->audio_root, audio_root);
++              if (ret) {
++                      dev_err(dai->dev, "failed to set audio_root rate :%d\n", ret);
++                      goto fail_ext;
++              }
++
++              ret = clk_set_rate(spdif->mclk_inner, mclk);
++              if (ret) {
++                      dev_err(dai->dev, "failed to set mclk_inner rate :%d\n", ret);
++                      goto fail_ext;
++              }
++
++              mclk = clk_get_rate(spdif->mclk_inner);
++      } else {
++              ret = clk_set_parent(spdif->mclk, spdif->mclk_ext);
++              if (ret) {
++                      dev_err(dai->dev,
++                              "failed to set parent to mclk_ext ret=%d\n", ret);
++                      goto fail_ext;
++              }
++
++              mclk = clk_get_rate(spdif->mclk_ext);
++      }
++
++      /* (FCLK)4096000/128=32000 */
++      tsamplerate = (mclk / 128 + rate / 2) / rate - 1;
++      if (tsamplerate < 3)
++              tsamplerate = 3;
++
++      /* transmission sample rate */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL, 0xFF, tsamplerate);
++
++      return 0;
++
++fail_ext:
++      return ret;
++}
++
++static int sf_spdif_clks_get(struct platform_device *pdev,
++                           struct sf_spdif_dev *spdif)
++{
++      static struct clk_bulk_data clks[] = {
++              { .id = "apb" },                /* clock-names in dts file */
++              { .id = "core" },
++              { .id = "audroot" },
++              { .id = "mclk_inner"},
++              { .id = "mclk_ext"},
++              { .id = "mclk"},
++      };
++      int ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
++
++      spdif->spdif_apb = clks[0].clk;
++      spdif->spdif_core = clks[1].clk;
++      spdif->audio_root = clks[2].clk;
++      spdif->mclk_inner = clks[3].clk;
++      spdif->mclk_ext = clks[4].clk;
++      spdif->mclk = clks[5].clk;
++
++      return ret;
++}
++
++static int sf_spdif_resets_get(struct platform_device *pdev,
++                             struct sf_spdif_dev *spdif)
++{
++      struct reset_control_bulk_data resets[] = {
++                      { .id = "apb" },
++      };
++      int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets);
++
++      if (ret)
++              return ret;
++
++      spdif->rst_apb = resets[0].rstc;
++
++      return 0;
++}
++
++static int starfive_spdif_crg_enable(struct sf_spdif_dev *spdif, bool enable)
++{
++      int ret;
++
++      dev_dbg(spdif->dev, "starfive_spdif clk&rst %sable.\n", enable ? "en":"dis");
++      if (enable) {
++              ret = clk_prepare_enable(spdif->spdif_apb);
++              if (ret) {
++                      dev_err(spdif->dev, "failed to prepare enable spdif_apb\n");
++                      goto failed_apb_clk;
++              }
++
++              ret = clk_prepare_enable(spdif->spdif_core);
++              if (ret) {
++                      dev_err(spdif->dev, "failed to prepare enable spdif_core\n");
++                      goto failed_core_clk;
++              }
++
++              ret = reset_control_deassert(spdif->rst_apb);
++              if (ret) {
++                      dev_err(spdif->dev, "failed to deassert apb\n");
++                      goto failed_rst;
++              }
++      } else {
++              clk_disable_unprepare(spdif->spdif_core);
++              clk_disable_unprepare(spdif->spdif_apb);
++      }
++
++      return 0;
++
++failed_rst:
++      clk_disable_unprepare(spdif->spdif_core);
++failed_core_clk:
++      clk_disable_unprepare(spdif->spdif_apb);
++failed_apb_clk:
++      return ret;
++}
++
++static int sf_spdif_dai_probe(struct snd_soc_dai *dai)
++{
++      struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
++
++      pm_runtime_get_sync(spdif->dev);
++
++      /* reset */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_ENABLE | SPDIF_SFR_ENABLE | SPDIF_FIFO_ENABLE, 0);
++
++      /* clear irq */
++      regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
++                         SPDIF_INT_REG_BIT, 0);
++
++      /* power save mode */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
++
++      /* power save mode */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE);
++
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE,
++                         SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE);
++
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_SETPREAMBB, SPDIF_SETPREAMBB);
++
++      regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
++                         BIT8TO20MASK<<SPDIF_PREAMBLEDEL, 0x3<<SPDIF_PREAMBLEDEL);
++
++      regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL,
++                         ALLBITMASK, 0x20|(0x20<<SPDIF_AFULL_THRESHOLD));
++
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_PARITYGEN, SPDIF_PARITYGEN);
++
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE);
++
++      /* APB access to FIFO enable, disable if use DMA/FIFO */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_USE_FIFO_IF, 0);
++
++      /* two channel */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         SPDIF_CHANNEL_MODE, 0);
++
++      pm_runtime_put_sync(spdif->dev);
++      return 0;
++}
++
++static const struct snd_soc_dai_ops sf_spdif_dai_ops = {
++      .probe = sf_spdif_dai_probe,
++      .trigger = sf_spdif_trigger,
++      .hw_params = sf_spdif_hw_params,
++};
++
++#ifdef CONFIG_PM_SLEEP
++static int spdif_system_suspend(struct device *dev)
++{
++      struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++
++      /* save the register value */
++      regmap_read(spdif->regmap, SPDIF_CTRL, &spdif->reg_spdif_ctrl);
++      regmap_read(spdif->regmap, SPDIF_INT_REG, &spdif->reg_spdif_int);
++      regmap_read(spdif->regmap, SPDIF_FIFO_CTRL, &spdif->reg_spdif_fifo_ctrl);
++
++      return pm_runtime_force_suspend(dev);
++}
++
++static int spdif_system_resume(struct device *dev)
++{
++      struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++      int ret = pm_runtime_force_resume(dev);
++
++      if (ret)
++              return ret;
++
++      /* restore the register value */
++      regmap_update_bits(spdif->regmap, SPDIF_CTRL,
++                         ALLBITMASK, spdif->reg_spdif_ctrl);
++      regmap_update_bits(spdif->regmap, SPDIF_INT_REG,
++                         ALLBITMASK, spdif->reg_spdif_int);
++      regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL,
++                         ALLBITMASK, spdif->reg_spdif_fifo_ctrl);
++
++      return 0;
++}
++#endif
++
++#ifdef CONFIG_PM
++static int spdif_runtime_suspend(struct device *dev)
++{
++      struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++
++      return starfive_spdif_crg_enable(spdif, false);
++}
++
++static int spdif_runtime_resume(struct device *dev)
++{
++      struct sf_spdif_dev *spdif = dev_get_drvdata(dev);
++
++      return starfive_spdif_crg_enable(spdif, true);
++}
++#endif
++
++static const struct dev_pm_ops spdif_pm_ops = {
++      SET_RUNTIME_PM_OPS(spdif_runtime_suspend, spdif_runtime_resume, NULL)
++      SET_SYSTEM_SLEEP_PM_OPS(spdif_system_suspend, spdif_system_resume)
++};
++
++#define SF_PCM_RATE_44100_192000 (SNDRV_PCM_RATE_44100 | \
++                                SNDRV_PCM_RATE_48000 | \
++                                SNDRV_PCM_RATE_96000 | \
++                                SNDRV_PCM_RATE_192000)
++
++#define SF_PCM_RATE_8000_22050 (SNDRV_PCM_RATE_8000 | \
++                              SNDRV_PCM_RATE_11025 | \
++                              SNDRV_PCM_RATE_16000 | \
++                              SNDRV_PCM_RATE_22050)
++
++static struct snd_soc_dai_driver sf_spdif_dai = {
++      .name = "spdif",
++      .id = 0,
++      .playback = {
++              .stream_name = "Playback",
++              .channels_min = 1,
++              .channels_max = 2,
++              .rates = SF_PCM_RATE_8000_22050,
++              .formats = SNDRV_PCM_FMTBIT_S16_LE |
++                         SNDRV_PCM_FMTBIT_S24_LE |
++                         SNDRV_PCM_FMTBIT_S24_3LE |
++                         SNDRV_PCM_FMTBIT_S32_LE,
++      },
++      .ops = &sf_spdif_dai_ops,
++      .symmetric_rate = 1,
++};
++
++static const struct snd_soc_component_driver sf_spdif_component = {
++      .name = "starfive-spdif",
++};
++
++static const struct regmap_config sf_spdif_regmap_config = {
++      .reg_bits = 32,
++      .reg_stride = 4,
++      .val_bits = 32,
++      .max_register = 0x200,
++};
++
++static int sf_spdif_probe(struct platform_device *pdev)
++{
++      struct sf_spdif_dev *spdif;
++      struct resource *res;
++      void __iomem *base;
++      int ret;
++      int irq;
++
++      spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
++      if (!spdif)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, spdif);
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      base = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      spdif->spdif_base = base;
++      spdif->regmap = devm_regmap_init_mmio(&pdev->dev, spdif->spdif_base,
++                                            &sf_spdif_regmap_config);
++      if (IS_ERR(spdif->regmap))
++              return PTR_ERR(spdif->regmap);
++
++      spdif->dev = &pdev->dev;
++
++      ret = sf_spdif_clks_get(pdev, spdif);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to get audio clock\n");
++              return ret;
++      }
++
++      ret = sf_spdif_resets_get(pdev, spdif);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to get audio reset controls\n");
++              return ret;
++      }
++
++      ret = starfive_spdif_crg_enable(spdif, true);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to enable audio clock\n");
++              return ret;
++      }
++
++      spdif->fifo_th = 16;
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq >= 0) {
++              ret = devm_request_irq(&pdev->dev, irq, spdif_irq_handler, 0,
++                              pdev->name, spdif);
++              if (ret < 0) {
++                      dev_err(&pdev->dev, "failed to request irq\n");
++                      return ret;
++              }
++      }
++
++      ret = devm_snd_soc_register_component(&pdev->dev, &sf_spdif_component,
++                                       &sf_spdif_dai, 1);
++      if (ret)
++              goto err_clk_disable;
++
++      if (irq >= 0) {
++              ret = sf_spdif_pcm_register(pdev);
++              spdif->use_pio = true;
++      } else {
++              ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
++                                      0);
++              spdif->use_pio = false;
++      }
++
++      if (ret)
++              goto err_clk_disable;
++
++      starfive_spdif_crg_enable(spdif, false);
++      pm_runtime_enable(&pdev->dev);
++      dev_dbg(&pdev->dev, "spdif register done.\n");
++
++      return 0;
++
++err_clk_disable:
++      return ret;
++}
++
++static const struct of_device_id sf_spdif_of_match[] = {
++      { .compatible = "starfive,jh7110-spdif", },
++      {},
++};
++MODULE_DEVICE_TABLE(of, sf_spdif_of_match);
++
++static struct platform_driver sf_spdif_driver = {
++      .driver = {
++              .name = "starfive-spdif",
++              .of_match_table = sf_spdif_of_match,
++              .pm = &spdif_pm_ops,
++      },
++      .probe = sf_spdif_probe,
++};
++module_platform_driver(sf_spdif_driver);
++
++MODULE_AUTHOR("curry.zhang <curry.zhang@starfive.com>");
++MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
++MODULE_DESCRIPTION("starfive SPDIF driver");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_spdif.h
+@@ -0,0 +1,196 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * SPDIF driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++#ifndef __SND_SOC_JH7110_SPDIF_H
++#define __SND_SOC_JH7110_SPDIF_H
++
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/types.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/pcm.h>
++
++#define SPDIF_CTRL                    0x0
++#define SPDIF_INT_REG                 0x4
++#define SPDIF_FIFO_CTRL                       0x8
++#define SPDIF_STAT_REG                        0xC
++
++#define SPDIF_FIFO_ADDR                       0x100
++#define DMAC_SPDIF_POLLING_LEN                256
++
++/* ctrl: sampled on the rising clock edge */
++#define       SPDIF_TSAMPLERATE       0       /* [SRATEW-1:0] */
++/* 0:SFR reg reset to defualt value; auto set back to '1' after reset */
++#define SPDIF_SFR_ENABLE      (1<<8)
++/* 0:reset of SPDIF block, SRF bits are unchanged; 1:enables SPDIF module */
++#define SPDIF_ENABLE          (1<<9)
++/* 0:FIFO pointers are reset to zero,threshold levels for FIFO are unchaned; auto set back to 1 */
++#define SPDIF_FIFO_ENABLE     (1<<10)
++/* 1:blocked and the modules are in power save mode; 0:block feeds the modules */
++#define SPDIF_CLK_ENABLE      (1<<11)
++#define SPDIF_TR_MODE         (1<<12) /* 0:rx; 1:tx */
++/* 0:party bit rx in a sub-frame is repeated on the parity; 1:check on a parity error */
++#define SPDIF_PARITCHECK      (1<<13)
++/*
++ * 0:parity bit from FIFO is transmitted in sub-frame;
++ * 1:parity bit generated inside the core and added to a transmitted sub-frame
++ */
++#define SPDIF_PARITYGEN               (1<<14)
++/* 0:validity bit in frame isn't checked and all frame are written; 1:validity bit rx is checked */
++#define SPDIF_VALIDITYCHECK   (1<<15)
++#define SPDIF_CHANNEL_MODE    (1<<16) /* 0:two-channel; 1:single-channel */
++/* only tx -single-channel mode; 0:secondary channel; 1: left(primary) channel */
++#define SPDIF_DUPLICATE               (1<<17)
++/*
++ * only tx;
++ * 0:first preamble B after reset tx valid sub-frame;
++ * 1:first preamble B is tx after preambleddel(INT_REG)
++ */
++#define SPDIF_SETPREAMBB      (1<<18)
++/* 0:FIFO disabled ,APB accese FIFO; 1:FIFO enable, APB access to FIFO disable; */
++#define SPDIF_USE_FIFO_IF     (1<<19)
++#define SPDIF_PARITY_MASK     (1<<21)
++#define SPDIF_UNDERR_MASK     (1<<22)
++#define SPDIF_OVRERR_MASK     (1<<23)
++#define SPDIF_EMPTY_MASK      (1<<24)
++#define       SPDIF_AEMPTY_MASK       (1<<25)
++#define SPDIF_FULL_MASK               (1<<26)
++#define SPDIF_AFULL_MASK      (1<<27)
++#define SPDIF_SYNCERR_MASK    (1<<28)
++#define SPDIF_LOCK_MASK               (1<<29)
++#define SPDIF_BEGIN_MASK      (1<<30)
++#define SPDIF_INTEREQ_MAKS    (1<<31)
++
++#define SPDIF_MASK_ENABLE     (SPDIF_PARITY_MASK | SPDIF_UNDERR_MASK | \
++                               SPDIF_OVRERR_MASK | SPDIF_EMPTY_MASK | \
++                               SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | \
++                               SPDIF_AFULL_MASK | SPDIF_SYNCERR_MASK | \
++                               SPDIF_LOCK_MASK | SPDIF_BEGIN_MASK | \
++                               SPDIF_INTEREQ_MAKS)
++
++#define SPDIF_MASK_FIFO       (SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK | \
++                       SPDIF_FULL_MASK | SPDIF_AFULL_MASK)
++
++/* INT_REG */
++#define SPDIF_RSAMPLERATE     0       /* [SRATEW-1:0] */
++#define SPDIF_PREAMBLEDEL     8       /* [PDELAYW+7:8] first B delay */
++#define SPDIF_PARITYO         (1<<21) /* 0:clear parity error */
++#define SPDIF_TDATA_UNDERR    (1<<22) /* tx data underrun error;0:clear */
++#define SPDIF_RDATA_OVRERR    (1<<23) /* rx data overrun error; 0:clear */
++#define SPDIF_FIFO_EMPTY      (1<<24) /* empty; 0:clear */
++#define SPDIF_FIOF_AEMPTY     (1<<25) /* almost empty; 0:clear */
++#define SPDIF_FIFO_FULL               (1<<26) /* FIFO full; 0:clear */
++#define SPDIF_FIFO_AFULL      (1<<27) /* FIFO almost full; 0:clear */
++#define SPDIF_SYNCERR         (1<<28) /* sync error; 0:clear */
++#define SPDIF_LOCK            (1<<29) /* sync; 0:clear */
++#define SPDIF_BLOCK_BEGIN     (1<<30) /* new start block rx data */
++
++#define SPDIF_INT_REG_BIT     (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | \
++                               SPDIF_RDATA_OVRERR | SPDIF_FIFO_EMPTY | \
++                               SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | \
++                               SPDIF_FIFO_AFULL | SPDIF_SYNCERR | \
++                               SPDIF_LOCK | SPDIF_BLOCK_BEGIN)
++
++#define SPDIF_ERROR_INT_STATUS        (SPDIF_PARITYO | \
++                               SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR)
++#define SPDIF_FIFO_INT_STATUS (SPDIF_FIFO_EMPTY | SPDIF_FIOF_AEMPTY | \
++                               SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL)
++
++#define SPDIF_INT_PARITY_ERROR        (-1)
++#define SPDIF_INT_TDATA_UNDERR        (-2)
++#define SPDIF_INT_RDATA_OVRERR        (-3)
++#define SPDIF_INT_FIFO_EMPTY  1
++#define SPDIF_INT_FIFO_AEMPTY 2
++#define SPDIF_INT_FIFO_FULL   3
++#define SPDIF_INT_FIFO_AFULL  4
++#define SPDIF_INT_SYNCERR     (-4)
++#define SPDIF_INT_LOCK                5 /* reciever has become synchronized with input data stream */
++#define SPDIF_INT_BLOCK_BEGIN 6 /* start a new block in recieve data, written into FIFO */
++
++/* FIFO_CTRL */
++#define SPDIF_AEMPTY_THRESHOLD        0       /* [depth-1:0] */
++#define SPDIF_AFULL_THRESHOLD 16      /* [depth+15:16] */
++
++/* STAT_REG */
++#define SPDIF_FIFO_LEVEL      (1<<0)
++#define SPDIF_PARITY_FLAG     (1<<21) /* 1:error; 0:repeated */
++#define SPDIF_UNDERR_FLAG     (1<<22) /* 1:error */
++#define SPDIF_OVRERR_FLAG     (1<<23) /* 1:error */
++#define SPDIF_EMPTY_FLAG      (1<<24) /* 1:fifo empty */
++#define SPDIF_AEMPTY_FLAG     (1<<25) /* 1:fifo almost empty */
++#define SPDIF_FULL_FLAG               (1<<26) /* 1:fifo full */
++#define SPDIF_AFULL_FLAG      (1<<27) /* 1:fifo almost full */
++#define SPDIF_SYNCERR_FLAG    (1<<28) /* 1:rx sync error */
++#define SPDIF_LOCK_FLAG               (1<<29) /* 1:RX sync */
++#define SPDIF_BEGIN_FLAG      (1<<30) /* 1:start a new block */
++/* 1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO */
++#define SPDIF_RIGHT_LEFT      (1<<31)
++
++#define BIT8TO20MASK  0x1FFF
++#define ALLBITMASK            0xFFFFFFFF
++
++#define SPDIF_STAT            (SPDIF_PARITY_FLAG | SPDIF_UNDERR_FLAG | \
++                               SPDIF_OVRERR_FLAG | SPDIF_EMPTY_FLAG | \
++                               SPDIF_AEMPTY_FLAG | SPDIF_FULL_FLAG | \
++                               SPDIF_AFULL_FLAG | SPDIF_SYNCERR_FLAG | \
++                               SPDIF_LOCK_FLAG | SPDIF_BEGIN_FLAG | \
++                               SPDIF_RIGHT_LEFT)
++struct sf_spdif_dev {
++      void __iomem *spdif_base;
++      struct regmap *regmap;
++      struct device *dev;
++      u32 fifo_th;
++      int active;
++
++      /* data related to DMA transfers b/w i2s and DMAC */
++      struct snd_dmaengine_dai_dma_data play_dma_data;
++      struct snd_dmaengine_dai_dma_data capture_dma_data;
++
++      bool use_pio;
++      struct snd_pcm_substream __rcu *tx_substream;
++      struct snd_pcm_substream __rcu *rx_substream;
++
++      unsigned int (*tx_fn)(struct sf_spdif_dev *dev,
++                            struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
++                            bool *period_elapsed, snd_pcm_format_t format);
++      unsigned int (*rx_fn)(struct sf_spdif_dev *dev,
++                            struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
++                            bool *period_elapsed, snd_pcm_format_t format);
++
++      snd_pcm_format_t format;
++      bool channels;
++      unsigned int tx_ptr;
++      unsigned int rx_ptr;
++      struct clk *spdif_apb;
++      struct clk *spdif_core;
++      struct clk *audio_root;
++      struct clk *mclk_inner;
++      struct clk *mclk;
++      struct clk *mclk_ext;
++      struct reset_control *rst_apb;
++      unsigned int reg_spdif_ctrl;
++      unsigned int reg_spdif_int;
++      unsigned int reg_spdif_fifo_ctrl;
++
++      struct snd_dmaengine_dai_dma_data dma_data;
++};
++
++#if IS_ENABLED(CONFIG_SND_SOC_JH7110_SPDIF_PCM)
++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev);
++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev);
++int sf_spdif_pcm_register(struct platform_device *pdev);
++#else
++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) { }
++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) { }
++int sf_spdif_pcm_register(struct platform_device *pdev)
++{
++      return -EINVAL;
++}
++#endif
++
++#endif        /* __SND_SOC_JH7110_SPDIF_H */
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_spdif_pcm.c
+@@ -0,0 +1,339 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * SPDIF PCM driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++
++#include <linux/io.h>
++#include <linux/rcupdate.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++
++#include "jh7110_spdif.h"
++
++#define BUFFER_BYTES_MAX      (3 * 2 * 8 * PERIOD_BYTES_MIN)
++#define PERIOD_BYTES_MIN      4096
++#define PERIODS_MIN           2
++
++static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev,
++                                  struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
++                                  bool *period_elapsed, snd_pcm_format_t format)
++{
++      unsigned int period_pos = tx_ptr % runtime->period_size;
++      u32 data[2];
++      int i;
++
++      /* two- channel and signal-channel mode */
++      if (dev->channels) {
++              const u16 (*p16)[2] = (void *)runtime->dma_area;
++              const u32 (*p32)[2] = (void *)runtime->dma_area;
++
++              for (i = 0; i < dev->fifo_th; i++) {
++                      if (format == SNDRV_PCM_FORMAT_S16_LE) {
++                              data[0] = p16[tx_ptr][0];
++                              data[0] = data[0]<<8;
++                              data[0] &= 0x00ffff00;
++                              data[1] = p16[tx_ptr][1];
++                              data[1] = data[1]<<8;
++                              data[1] &= 0x00ffff00;
++                      } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
++                              data[0] = p32[tx_ptr][0];
++                              data[1] = p32[tx_ptr][1];
++
++                              /*
++                               * To adapt S24_3LE and ALSA pass parameter of S24_LE.
++                               * operation of S24_LE should be same to S24_3LE.
++                               * So it would wrong when playback S24_LE file.
++                               * when want to playback S24_LE file, should add in there:
++                               * data[0] = data[0]>>8;
++                               * data[1] = data[1]>>8;
++                               */
++
++                              data[0] &= 0x00ffffff;
++                              data[1] &= 0x00ffffff;
++                      } else if (format == SNDRV_PCM_FORMAT_S24_3LE) {
++                              data[0] = p32[tx_ptr][0];
++                              data[1] = p32[tx_ptr][1];
++                              data[0] &= 0x00ffffff;
++                              data[1] &= 0x00ffffff;
++                      } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
++                              data[0] = p32[tx_ptr][0];
++                              data[0] = data[0]>>8;
++                              data[1] = p32[tx_ptr][1];
++                              data[1] = data[1]>>8;
++                      }
++
++                      iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
++                      iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR);
++                      period_pos++;
++                      if (++tx_ptr >= runtime->buffer_size)
++                              tx_ptr = 0;
++              }
++      } else {
++              const u16 (*p16) = (void *)runtime->dma_area;
++              const u32 (*p32) = (void *)runtime->dma_area;
++
++              for (i = 0; i < dev->fifo_th; i++) {
++                      if (format == SNDRV_PCM_FORMAT_S16_LE) {
++                              data[0] = p16[tx_ptr];
++                              data[0] = data[0]<<8;
++                              data[0] &= 0x00ffff00;
++                      } else if (format == SNDRV_PCM_FORMAT_S24_LE ||
++                              format == SNDRV_PCM_FORMAT_S24_3LE) {
++                              data[0] = p32[tx_ptr];
++                              data[0] &= 0x00ffffff;
++                      } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
++                              data[0] = p32[tx_ptr];
++                              data[0] = data[0]>>8;
++                      }
++
++                      iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
++                      period_pos++;
++                      if (++tx_ptr >= runtime->buffer_size)
++                              tx_ptr = 0;
++              }
++      }
++
++      *period_elapsed = period_pos >= runtime->period_size;
++      return tx_ptr;
++}
++
++static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev,
++                                  struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
++                                  bool *period_elapsed, snd_pcm_format_t format)
++{
++      u16 (*p16)[2] = (void *)runtime->dma_area;
++      u32 (*p32)[2] = (void *)runtime->dma_area;
++      unsigned int period_pos = rx_ptr % runtime->period_size;
++      u32 data[2];
++      int i;
++
++      for (i = 0; i < dev->fifo_th; i++) {
++              data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
++              data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
++              if (format == SNDRV_PCM_FORMAT_S16_LE) {
++                      p16[rx_ptr][0] = data[0]>>8;
++                      p16[rx_ptr][1] = data[1]>>8;
++              } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
++                      p32[rx_ptr][0] = data[0];
++                      p32[rx_ptr][1] = data[1];
++              } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
++                      p32[rx_ptr][0] = data[0]<<8;
++                      p32[rx_ptr][1] = data[1]<<8;
++              }
++
++              period_pos++;
++              if (++rx_ptr >= runtime->buffer_size)
++                      rx_ptr = 0;
++      }
++
++      *period_elapsed = period_pos >= runtime->period_size;
++      return rx_ptr;
++}
++
++static const struct snd_pcm_hardware sf_pcm_hardware = {
++      .info = SNDRV_PCM_INFO_INTERLEAVED |
++              SNDRV_PCM_INFO_MMAP |
++              SNDRV_PCM_INFO_MMAP_VALID |
++              SNDRV_PCM_INFO_BLOCK_TRANSFER |
++              SNDRV_PCM_INFO_PAUSE |
++              SNDRV_PCM_INFO_RESUME,
++      .rates = SNDRV_PCM_RATE_8000 |
++              SNDRV_PCM_RATE_11025 |
++              SNDRV_PCM_RATE_16000 |
++              SNDRV_PCM_RATE_22050 |
++              SNDRV_PCM_RATE_32000 |
++              SNDRV_PCM_RATE_44100 |
++              SNDRV_PCM_RATE_48000,
++      .rate_min = 8000,
++      .rate_max = 48000,
++      .formats = SNDRV_PCM_FMTBIT_S16_LE |
++              SNDRV_PCM_FMTBIT_S24_LE |
++              SNDRV_PCM_FMTBIT_S24_3LE |
++              SNDRV_PCM_FMTBIT_S32_LE,
++      .channels_min = 1,
++      .channels_max = 2,
++      .buffer_bytes_max = BUFFER_BYTES_MAX,
++      .period_bytes_min = PERIOD_BYTES_MIN,
++      .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
++      .periods_min = PERIODS_MIN,
++      .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
++      .fifo_size = 16,
++};
++
++static void sf_spdif_pcm_transfer(struct sf_spdif_dev *dev, bool push)
++{
++      struct snd_pcm_substream *substream;
++      bool active, period_elapsed;
++
++      rcu_read_lock();
++      if (push)
++              substream = rcu_dereference(dev->tx_substream);
++      else
++              substream = rcu_dereference(dev->rx_substream);
++
++      active = substream && snd_pcm_running(substream);
++      if (active) {
++              unsigned int ptr;
++              unsigned int new_ptr;
++
++              if (push) {
++                      ptr = READ_ONCE(dev->tx_ptr);
++                      new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
++                                      &period_elapsed, dev->format);
++                      cmpxchg(&dev->tx_ptr, ptr, new_ptr);
++              } else {
++                      ptr = READ_ONCE(dev->rx_ptr);
++                      new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
++                                      &period_elapsed, dev->format);
++                      cmpxchg(&dev->rx_ptr, ptr, new_ptr);
++              }
++
++              if (period_elapsed)
++                      snd_pcm_period_elapsed(substream);
++      }
++      rcu_read_unlock();
++}
++
++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev)
++{
++      sf_spdif_pcm_transfer(dev, true);
++}
++
++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev)
++{
++      sf_spdif_pcm_transfer(dev, false);
++}
++
++static int sf_pcm_open(struct snd_soc_component *component,
++                     struct snd_pcm_substream *substream)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++      struct sf_spdif_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
++
++      snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware);
++      snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
++      runtime->private_data = dev;
++
++      return 0;
++}
++
++static int sf_pcm_close(struct snd_soc_component *component,
++                      struct snd_pcm_substream *substream)
++{
++      synchronize_rcu();
++      return 0;
++}
++
++static int sf_pcm_hw_params(struct snd_soc_component *component,
++                          struct snd_pcm_substream *substream,
++                          struct snd_pcm_hw_params *hw_params)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      struct sf_spdif_dev *dev = runtime->private_data;
++
++      switch (params_channels(hw_params)) {
++      case 1:
++      case 2:
++              break;
++      default:
++              dev_err(dev->dev, "invalid channels number\n");
++              return -EINVAL;
++      }
++
++      dev->format = params_format(hw_params);
++      switch (dev->format) {
++      case SNDRV_PCM_FORMAT_S16_LE:
++      case SNDRV_PCM_FORMAT_S24_LE:
++      case SNDRV_PCM_FORMAT_S24_3LE:
++      case SNDRV_PCM_FORMAT_S32_LE:
++              break;
++      default:
++              dev_err(dev->dev, "invalid format\n");
++              return -EINVAL;
++      }
++
++      dev->tx_fn = sf_spdif_pcm_tx;
++      dev->rx_fn = sf_spdif_pcm_rx;
++
++      return 0;
++}
++
++static int sf_pcm_trigger(struct snd_soc_component *component,
++                        struct snd_pcm_substream *substream, int cmd)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      struct sf_spdif_dev *dev = runtime->private_data;
++      int ret = 0;
++
++      switch (cmd) {
++      case SNDRV_PCM_TRIGGER_START:
++      case SNDRV_PCM_TRIGGER_RESUME:
++      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++                      WRITE_ONCE(dev->tx_ptr, 0);
++                      rcu_assign_pointer(dev->tx_substream, substream);
++              } else {
++                      WRITE_ONCE(dev->rx_ptr, 0);
++                      rcu_assign_pointer(dev->rx_substream, substream);
++              }
++              break;
++      case SNDRV_PCM_TRIGGER_STOP:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++                      rcu_assign_pointer(dev->tx_substream, NULL);
++              else
++                      rcu_assign_pointer(dev->rx_substream, NULL);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component,
++                                      struct snd_pcm_substream *substream)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      struct sf_spdif_dev *dev = runtime->private_data;
++      snd_pcm_uframes_t pos;
++
++      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++              pos = READ_ONCE(dev->tx_ptr);
++      else
++              pos = READ_ONCE(dev->rx_ptr);
++
++      return pos < runtime->buffer_size ? pos : 0;
++}
++
++static int sf_pcm_new(struct snd_soc_component *component,
++                    struct snd_soc_pcm_runtime *rtd)
++{
++      size_t size = sf_pcm_hardware.buffer_bytes_max;
++
++      snd_pcm_set_managed_buffer_all(rtd->pcm,
++                      SNDRV_DMA_TYPE_CONTINUOUS,
++                      NULL, size, size);
++
++      return 0;
++}
++
++static const struct snd_soc_component_driver sf_pcm_component = {
++      .open           = sf_pcm_open,
++      .close          = sf_pcm_close,
++      .hw_params      = sf_pcm_hw_params,
++      .trigger        = sf_pcm_trigger,
++      .pointer        = sf_pcm_pointer,
++      .pcm_construct  = sf_pcm_new,
++};
++
++int sf_spdif_pcm_register(struct platform_device *pdev)
++{
++      return devm_snd_soc_register_component(&pdev->dev, &sf_pcm_component,
++                                             NULL, 0);
++}
diff --git a/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch b/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch
new file mode 100644 (file)
index 0000000..006760d
--- /dev/null
@@ -0,0 +1,537 @@
+From 9c4858f9fe4d8f8fe5cf347b3ca727016b7ba492 Mon Sep 17 00:00:00 2001
+From: Walker Chen <walker.chen@starfivetech.com>
+Date: Tue, 20 Jun 2023 15:57:53 +0800
+Subject: [PATCH 081/116] ASoC: starfive: Add JH7110 PDM driver
+
+Add pdm driver support for the StarFive JH7110 SoC.
+
+Signed-off-by: Walker Chen <walker.chen@starfivetech.com>
+---
+ sound/soc/starfive/Kconfig      |   8 +
+ sound/soc/starfive/Makefile     |   2 +
+ sound/soc/starfive/jh7110_pdm.c | 493 ++++++++++++++++++++++++++++++++
+ 3 files changed, 503 insertions(+)
+ create mode 100644 sound/soc/starfive/jh7110_pdm.c
+
+--- a/sound/soc/starfive/Kconfig
++++ b/sound/soc/starfive/Kconfig
+@@ -7,6 +7,14 @@ config SND_SOC_STARFIVE
+         the Starfive SoCs' Audio interfaces. You will also need to
+         select the audio interfaces to support below.
++config SND_SOC_JH7110_PDM
++      tristate "JH7110 PDM device driver"
++      depends on HAVE_CLK && SND_SOC_STARFIVE
++      select SND_SOC_JH7110_I2S
++      select REGMAP_MMIO
++      help
++        Say Y or M if you want to add support for StarFive pdm driver.
++
+ config SND_SOC_JH7110_PWMDAC
+       tristate "JH7110 PWM-DAC device driver"
+       depends on HAVE_CLK && SND_SOC_STARFIVE
+--- a/sound/soc/starfive/Makefile
++++ b/sound/soc/starfive/Makefile
+@@ -1,4 +1,6 @@
+ # StarFive Platform Support
++obj-$(CONFIG_SND_SOC_JH7110_PDM) += jh7110_pdm.o
++
+ obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o
+ obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o
+--- /dev/null
++++ b/sound/soc/starfive/jh7110_pdm.c
+@@ -0,0 +1,493 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PDM driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ */
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/reset.h>
++#include <linux/module.h>
++#include <linux/of_irq.h>
++#include <linux/of_platform.h>
++#include <linux/regmap.h>
++#include <linux/pm_runtime.h>
++#include <linux/types.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dai.h>
++#include <sound/tlv.h>
++
++#define PDM_DMIC_CTRL0                        0x00
++#define PDM_DC_SCALE0                 0x04
++#define PDM_DMIC_CTRL1                        0x10
++#define PDM_DC_SCALE1                 0x14
++
++/* PDM CTRL OFFSET */
++#define PDM_DMIC_MSB_SHIFT            1
++#define PDM_DMIC_MSB_MASK             (0x7 << PDM_DMIC_MSB_SHIFT)
++#define PDM_DMIC_VOL_SHIFT            16
++#define PDM_DMIC_VOL_MASK             (0x3f << PDM_DMIC_VOL_SHIFT)
++#define PDM_VOL_DB_MUTE                       (0x3f << PDM_DMIC_VOL_SHIFT)
++#define PDM_VOL_DB_MAX                        0
++
++#define PDM_DMIC_RVOL_MASK            BIT(22)
++#define PDM_DMIC_LVOL_MASK            BIT(23)
++#define PDM_DMIC_I2S_SLAVE            BIT(24)
++#define PDM_DMIC_HPF_EN                       BIT(28)
++#define PDM_DMIC_FASTMODE_MASK                BIT(29)
++#define PDM_DMIC_DC_BYPASS_MASK               BIT(30)
++#define PDM_SW_RST_MASK                       BIT(31)
++#define PDM_SW_RST_RELEASE            BIT(31)
++
++/* PDM SCALE OFFSET */
++#define DMIC_DCOFF3_SHIFT             24
++#define DMIC_DCOFF2_SHIFT             16
++#define DMIC_DCOFF1_SHIFT             8
++
++#define DMIC_DCOFF3_MASK              (0xf << DMIC_DCOFF3_SHIFT)
++#define DMIC_DCOFF3_VAL                       (0xc << DMIC_DCOFF3_SHIFT)
++#define DMIC_DCOFF1_MASK              (0xff << DMIC_DCOFF1_SHIFT)
++#define DMIC_DCOFF1_VAL                       (0x5 << DMIC_DCOFF1_SHIFT)
++#define DMIC_SCALE_MASK                       0x3f
++#define DMIC_SCALE_DEF_VAL            0x8
++
++enum PDM_MSB_SHIFT {
++      PDM_MSB_SHIFT_NONE = 0,
++      PDM_MSB_SHIFT_1,
++      PDM_MSB_SHIFT_2,
++      PDM_MSB_SHIFT_3,
++      PDM_MSB_SHIFT_4,
++      PDM_MSB_SHIFT_5,
++      PDM_MSB_SHIFT_6,
++      PDM_MSB_SHIFT_7,
++};
++
++struct sf_pdm {
++      struct regmap *pdm_map;
++      struct device *dev;
++      struct clk *clk_pdm_apb;
++      struct clk *clk_pdm_mclk;
++      struct clk *clk_mclk;
++      struct clk *clk_mclk_ext;
++      struct reset_control *rst_pdm_dmic;
++      struct reset_control *rst_pdm_apb;
++      unsigned char flag_first;
++      unsigned int saved_ctrl0;
++      unsigned int saved_scale0;
++};
++
++static const DECLARE_TLV_DB_SCALE(volume_tlv, -9450, 150, 0);
++
++static const struct snd_kcontrol_new sf_pdm_snd_controls[] = {
++      SOC_SINGLE("DC compensation Control", PDM_DMIC_CTRL0, 30, 1, 0),
++      SOC_SINGLE("High Pass Filter Control", PDM_DMIC_CTRL0, 28, 1, 0),
++      SOC_SINGLE("Left Channel Volume Control", PDM_DMIC_CTRL0, 23, 1, 0),
++      SOC_SINGLE("Right Channel Volume Control", PDM_DMIC_CTRL0, 22, 1, 0),
++      SOC_SINGLE_TLV("Volume", PDM_DMIC_CTRL0, 16, 0x3F, 1, volume_tlv),
++      SOC_SINGLE("Data MSB Shift", PDM_DMIC_CTRL0, 1, 7, 0),
++      SOC_SINGLE("SCALE", PDM_DC_SCALE0, 0, 0x3F, 0),
++      SOC_SINGLE("DC offset", PDM_DC_SCALE0, 8, 0xFFFFF, 0),
++};
++
++static void sf_pdm_enable(struct regmap *map)
++{
++      /* Left and Right Channel Volume Control Enable */
++      regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_RVOL_MASK, 0);
++      regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_LVOL_MASK, 0);
++}
++
++static void sf_pdm_disable(struct regmap *map)
++{
++      /* Left and Right Channel Volume Control Disable */
++      regmap_update_bits(map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_RVOL_MASK, PDM_DMIC_RVOL_MASK);
++      regmap_update_bits(map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_LVOL_MASK, PDM_DMIC_LVOL_MASK);
++}
++
++static int sf_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
++                         struct snd_soc_dai *dai)
++{
++      struct sf_pdm *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:
++              if (priv->flag_first) {
++                      priv->flag_first = 0;
++                      mdelay(200);
++              }
++
++              sf_pdm_enable(priv->pdm_map);
++              return 0;
++
++      case SNDRV_PCM_TRIGGER_STOP:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++              sf_pdm_disable(priv->pdm_map);
++              return 0;
++
++      default:
++              return -EINVAL;
++      }
++}
++
++static int sf_pdm_hw_params(struct snd_pcm_substream *substream,
++                           struct snd_pcm_hw_params *params,
++                           struct snd_soc_dai *dai)
++{
++      struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai);
++      unsigned int sample_rate;
++      unsigned int data_width;
++      int ret;
++      const int pdm_mul = 128;
++
++      sample_rate = params_rate(params);
++      switch (sample_rate) {
++      case 8000:
++      case 11025:
++      case 16000:
++              break;
++      default:
++              dev_err(priv->dev, "can't support sample rate:%d\n", sample_rate);
++              return -EINVAL;
++      }
++
++      data_width = params_width(params);
++      switch (data_width) {
++      case 16:
++      case 32:
++              break;
++      default:
++              dev_err(priv->dev, "can't support bit width %d\n", data_width);
++              return -EINVAL;
++      }
++
++      /* set pdm_mclk,  PDM MCLK = 128 * LRCLK */
++      ret = clk_set_rate(priv->clk_pdm_mclk, pdm_mul * sample_rate);
++      if (ret) {
++              dev_err(priv->dev, "Can't set pdm_mclk: %d\n", ret);
++              return ret;
++      }
++
++      return 0;
++}
++
++static const struct snd_soc_dai_ops sf_pdm_dai_ops = {
++      .trigger        = sf_pdm_trigger,
++      .hw_params      = sf_pdm_hw_params,
++};
++
++static void sf_pdm_module_init(struct sf_pdm *priv)
++{
++      /* Reset */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_SW_RST_MASK, 0x00);
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_SW_RST_MASK, PDM_SW_RST_RELEASE);
++
++      /* Make sure the device is initially disabled */
++      sf_pdm_disable(priv->pdm_map);
++
++      /* MUTE */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_VOL_MASK, PDM_VOL_DB_MUTE);
++
++      /* UNMUTE */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_VOL_MASK, PDM_VOL_DB_MAX);
++
++      /* enable high pass filter */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_HPF_EN, PDM_DMIC_HPF_EN);
++
++      /* PDM work as slave mode */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_I2S_SLAVE, PDM_DMIC_I2S_SLAVE);
++
++      /* disable fast mode */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_FASTMODE_MASK, 0);
++
++      /* dmic msb shift 0 */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_MSB_MASK, 0);
++
++      /* scale: 0x8 */
++      regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++                         DMIC_SCALE_MASK, DMIC_SCALE_DEF_VAL);
++
++      regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++                         DMIC_DCOFF1_MASK, DMIC_DCOFF1_VAL);
++
++      regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++                         DMIC_DCOFF3_MASK, DMIC_DCOFF3_VAL);
++
++      /* scale: 0x3f */
++      regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
++                         DMIC_SCALE_MASK, DMIC_SCALE_MASK);
++
++      /* dmic msb shift 2 */
++      regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
++                         PDM_DMIC_MSB_MASK, PDM_MSB_SHIFT_4);
++}
++
++#define SF_PDM_RATES  (SNDRV_PCM_RATE_8000 | \
++                      SNDRV_PCM_RATE_11025 | \
++                      SNDRV_PCM_RATE_16000)
++
++#define SF_PDM_FORMATS        (SNDRV_PCM_FMTBIT_S16_LE | \
++                      SNDRV_PCM_FMTBIT_S32_LE)
++
++static struct snd_soc_dai_driver sf_pdm_dai_drv = {
++      .name = "PDM",
++      .id = 0,
++      .capture = {
++              .stream_name    = "Capture",
++              .channels_min   = 2,
++              .channels_max   = 2,
++              .rates          = SF_PDM_RATES,
++              .formats        = SF_PDM_FORMATS,
++      },
++      .ops = &sf_pdm_dai_ops,
++      .symmetric_rate = 1,
++};
++
++static int sf_pdm_component_probe(struct snd_soc_component *component)
++{
++      struct sf_pdm *priv = snd_soc_component_get_drvdata(component);
++
++      snd_soc_component_init_regmap(component, priv->pdm_map);
++      snd_soc_add_component_controls(component, sf_pdm_snd_controls,
++                                     ARRAY_SIZE(sf_pdm_snd_controls));
++
++      return 0;
++}
++
++static int sf_pdm_clock_enable(struct sf_pdm *priv)
++{
++      int ret;
++
++      ret = clk_prepare_enable(priv->clk_pdm_mclk);
++      if (ret) {
++              dev_err(priv->dev, "failed to prepare enable clk_pdm_mclk\n");
++              return ret;
++      }
++
++      ret = clk_prepare_enable(priv->clk_pdm_apb);
++      if (ret) {
++              dev_err(priv->dev, "failed to prepare enable clk_pdm_apb\n");
++              goto disable_pdm_mclk;
++      }
++
++      ret = reset_control_deassert(priv->rst_pdm_dmic);
++      if (ret) {
++              dev_err(priv->dev, "failed to deassert pdm_dmic\n");
++              goto disable_pdm_apb;
++      }
++
++      ret = reset_control_deassert(priv->rst_pdm_apb);
++      if (ret) {
++              dev_err(priv->dev, "failed to deassert pdm_apb\n");
++              goto disable_pdm_apb;
++      }
++
++      ret = clk_set_parent(priv->clk_mclk, priv->clk_mclk_ext);
++      if (ret) {
++              dev_err(priv->dev, "failed to set parent clk_mclk ret=%d\n", ret);
++              goto disable_pdm_apb;
++      }
++
++      return 0;
++
++disable_pdm_apb:
++      clk_disable_unprepare(priv->clk_pdm_apb);
++disable_pdm_mclk:
++      clk_disable_unprepare(priv->clk_pdm_mclk);
++
++      return ret;
++}
++
++#ifdef CONFIG_PM
++static int sf_pdm_runtime_suspend(struct device *dev)
++{
++      struct sf_pdm *priv = dev_get_drvdata(dev);
++
++      clk_disable_unprepare(priv->clk_pdm_apb);
++      clk_disable_unprepare(priv->clk_pdm_mclk);
++
++      return 0;
++}
++
++static int sf_pdm_runtime_resume(struct device *dev)
++{
++      struct sf_pdm *priv = dev_get_drvdata(dev);
++      int ret;
++
++      ret = sf_pdm_clock_enable(priv);
++      if (!ret)
++              sf_pdm_module_init(priv);
++
++      return ret;
++}
++#endif
++
++#ifdef CONFIG_PM_SLEEP
++static int sf_pdm_suspend(struct snd_soc_component *component)
++{
++      return pm_runtime_force_suspend(component->dev);
++}
++
++static int sf_pdm_resume(struct snd_soc_component *component)
++{
++      return pm_runtime_force_resume(component->dev);
++}
++
++#else
++#define sf_pdm_suspend        NULL
++#define sf_pdm_resume NULL
++#endif
++
++static const struct snd_soc_component_driver sf_pdm_component_drv = {
++      .name = "jh7110-pdm",
++      .probe = sf_pdm_component_probe,
++      .suspend = sf_pdm_suspend,
++      .resume = sf_pdm_resume,
++};
++
++static const struct regmap_config sf_pdm_regmap_cfg = {
++      .reg_bits       = 32,
++      .val_bits       = 32,
++      .reg_stride     = 4,
++      .max_register   = 0x20,
++};
++
++static int sf_pdm_clock_get(struct platform_device *pdev, struct sf_pdm *priv)
++{
++      int ret;
++
++      static struct clk_bulk_data clks[] = {
++              { .id = "pdm_mclk" },
++              { .id = "pdm_apb" },
++              { .id = "clk_mclk" },
++              { .id = "mclk_ext" },
++      };
++
++      ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to get pdm clocks\n");
++              goto exit;
++      }
++
++      priv->clk_pdm_mclk = clks[0].clk;
++      priv->clk_pdm_apb = clks[1].clk;
++      priv->clk_mclk = clks[2].clk;
++      priv->clk_mclk_ext = clks[3].clk;
++
++      priv->rst_pdm_dmic = devm_reset_control_get_exclusive(&pdev->dev, "pdm_dmic");
++      if (IS_ERR(priv->rst_pdm_dmic)) {
++              dev_err(&pdev->dev, "failed to get pdm_dmic reset control\n");
++              ret = PTR_ERR(priv->rst_pdm_dmic);
++              goto exit;
++      }
++
++      priv->rst_pdm_apb = devm_reset_control_get_exclusive(&pdev->dev, "pdm_apb");
++      if (IS_ERR(priv->rst_pdm_apb)) {
++              dev_err(&pdev->dev, "failed to get pdm_apb reset control\n");
++              ret = PTR_ERR(priv->rst_pdm_apb);
++              goto exit;
++      }
++
++      /*
++       * pdm clock must always be enabled as hardware issue that
++       * no data in the first 4 seconds of the first recording
++       */
++      ret = sf_pdm_clock_enable(priv);
++
++exit:
++      return ret;
++}
++
++static int sf_pdm_probe(struct platform_device *pdev)
++{
++      struct sf_pdm *priv;
++      struct resource *res;
++      void __iomem *regs;
++      int ret;
++
++      priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++      platform_set_drvdata(pdev, priv);
++
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdm");
++      regs = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(regs))
++              return PTR_ERR(regs);
++
++      priv->pdm_map = devm_regmap_init_mmio(&pdev->dev, regs, &sf_pdm_regmap_cfg);
++      if (IS_ERR(priv->pdm_map)) {
++              dev_err(&pdev->dev, "failed to init regmap: %ld\n",
++                      PTR_ERR(priv->pdm_map));
++              return PTR_ERR(priv->pdm_map);
++      }
++
++      priv->dev = &pdev->dev;
++      priv->flag_first = 1;
++
++      ret = sf_pdm_clock_get(pdev, priv);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to enable audio-pdm clock\n");
++              return ret;
++      }
++
++      dev_set_drvdata(&pdev->dev, priv);
++
++      ret = devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv,
++                                            &sf_pdm_dai_drv, 1);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to register pdm dai\n");
++              return ret;
++      }
++      pm_runtime_enable(&pdev->dev);
++
++      return 0;
++}
++
++static int sf_pdm_dev_remove(struct platform_device *pdev)
++{
++      pm_runtime_disable(&pdev->dev);
++      return 0;
++}
++
++static const struct of_device_id sf_pdm_of_match[] = {
++      {.compatible = "starfive,jh7110-pdm",},
++      {}
++};
++MODULE_DEVICE_TABLE(of, sf_pdm_of_match);
++
++static const struct dev_pm_ops sf_pdm_pm_ops = {
++      SET_RUNTIME_PM_OPS(sf_pdm_runtime_suspend,
++                         sf_pdm_runtime_resume, NULL)
++};
++
++static struct platform_driver sf_pdm_driver = {
++      .driver = {
++              .name = "jh7110-pdm",
++              .of_match_table = sf_pdm_of_match,
++              .pm = &sf_pdm_pm_ops,
++      },
++      .probe = sf_pdm_probe,
++      .remove = sf_pdm_dev_remove,
++};
++module_platform_driver(sf_pdm_driver);
++
++MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
++MODULE_DESCRIPTION("Starfive PDM Controller Driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch b/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch
new file mode 100644 (file)
index 0000000..ed8a3af
--- /dev/null
@@ -0,0 +1,55 @@
+From 59cbdfeee0fc1ad382a0bc8f7fa897a9f5d03df0 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Fri, 9 Jun 2023 16:54:36 +0800
+Subject: [PATCH 082/116] dt-binding: input: Add tink_ft5406
+
+Add tink_ft5406.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ .../bindings/input/tinker_ft5406.yaml         | 39 +++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/input/tinker_ft5406.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/input/tinker_ft5406.yaml
+@@ -0,0 +1,39 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/input/touchscreen/goodix.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Tinker FT5406 touchscreen controller Bindings
++
++maintainers:
++  - Changhuang Liang <changhuang.liang@starfivetech.com>
++
++allOf:
++  - $ref: touchscreen.yaml#
++
++properties:
++  compatible:
++    const: tinker_ft5406
++
++  reg:
++    const: 0x38
++
++additionalProperties: false
++
++required:
++  - compatible
++  - reg
++
++examples:
++  - |
++    i2c {
++      #address-cells = <1>;
++      #size-cells = <0>;
++      tinker_ft5406@38 {
++        compatible = "tinker_ft5406";
++        reg = <0x38>;
++      };
++    };
++
++...
diff --git a/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch b/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch
new file mode 100644 (file)
index 0000000..f45085f
--- /dev/null
@@ -0,0 +1,444 @@
+From 4800b6e0f2190d991cd4e5352167a9422841f195 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Wed, 21 Dec 2022 16:20:04 +0800
+Subject: [PATCH 083/116] input: touchscreen: Add tinker_ft5406 driver support
+
+Add tinker_ft5406 driver support
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/input/touchscreen/Kconfig         |   6 +
+ drivers/input/touchscreen/Makefile        |   1 +
+ drivers/input/touchscreen/tinker_ft5406.c | 406 ++++++++++++++++++++++
+ 3 files changed, 413 insertions(+)
+ create mode 100644 drivers/input/touchscreen/tinker_ft5406.c
+
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -1399,4 +1399,10 @@ config TOUCHSCREEN_HIMAX_HX83112B
+         To compile this driver as a module, choose M here: the
+         module will be called himax_hx83112b.
++config TOUCHSCREEN_TINKER_FT5406
++      tristate "tinker ft5406"
++      depends on I2C
++      help
++        Control ft5406 touch ic.
++
+ endif
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -118,3 +118,4 @@ obj-$(CONFIG_TOUCHSCREEN_IQS5XX)   += iqs5
+ obj-$(CONFIG_TOUCHSCREEN_IQS7211)     += iqs7211.o
+ obj-$(CONFIG_TOUCHSCREEN_ZINITIX)     += zinitix.o
+ obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B)      += himax_hx83112b.o
++obj-$(CONFIG_TOUCHSCREEN_TINKER_FT5406)       += tinker_ft5406.o
+--- /dev/null
++++ b/drivers/input/touchscreen/tinker_ft5406.c
+@@ -0,0 +1,406 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ *
++ * TINKER BOARD FT5406 touch driver.
++ *
++ * Copyright (c) 2016 ASUSTek Computer Inc.
++ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/input.h>
++#include <linux/input/mt.h>
++#include <linux/module.h>
++#include <linux/workqueue.h>
++
++#define RETRY_COUNT 10
++#define FT_ONE_TCH_LEN        6
++
++#define FT_REG_FW_VER                 0xA6
++#define FT_REG_FW_MIN_VER             0xB2
++#define FT_REG_FW_SUB_MIN_VER 0xB3
++
++#define VALID_TD_STATUS_VAL           10
++#define MAX_TOUCH_POINTS              5
++
++#define FT_PRESS                      0x7F
++#define FT_MAX_ID                     0x0F
++
++#define FT_TOUCH_X_H  0
++#define FT_TOUCH_X_L  1
++#define FT_TOUCH_Y_H  2
++#define FT_TOUCH_Y_L  3
++#define FT_TOUCH_EVENT        0
++#define FT_TOUCH_ID           2
++
++#define FT_TOUCH_X_H_REG      3
++#define FT_TOUCH_X_L_REG      4
++#define FT_TOUCH_Y_H_REG      5
++#define FT_TOUCH_Y_L_REG      6
++#define FT_TD_STATUS_REG      2
++#define FT_TOUCH_EVENT_REG    3
++#define FT_TOUCH_ID_REG               5
++
++#define FT_TOUCH_DOWN         0
++#define FT_TOUCH_CONTACT      2
++
++struct ts_event {
++      u16 au16_x[MAX_TOUCH_POINTS]; /*x coordinate */
++      u16 au16_y[MAX_TOUCH_POINTS]; /*y coordinate */
++      u8 au8_touch_event[MAX_TOUCH_POINTS]; /*touch event: 0:down; 1:up; 2:contact */
++      u8 au8_finger_id[MAX_TOUCH_POINTS]; /*touch ID */
++      u16 pressure;
++      u8 touch_point;
++      u8 point_num;
++};
++
++struct tinker_ft5406_data {
++      struct device *dev;
++      struct i2c_client *client;
++      struct input_dev *input_dev;
++      struct ts_event event;
++      struct work_struct ft5406_work;
++
++      int screen_width;
++      int screen_height;
++      int xy_reverse;
++      int known_ids;
++      int retry_count;
++      bool finish_work;
++};
++
++struct tinker_ft5406_data *g_ts_data;
++
++static int fts_i2c_read(struct i2c_client *client, char *writebuf,
++                         int writelen, char *readbuf, int readlen)
++{
++      int ret;
++
++      if (writelen > 0) {
++              struct i2c_msg msgs[] = {
++                      {
++                               .addr = client->addr,
++                               .flags = 0,
++                               .len = writelen,
++                               .buf = writebuf,
++                       },
++                      {
++                               .addr = client->addr,
++                               .flags = I2C_M_RD,
++                               .len = readlen,
++                               .buf = readbuf,
++                       },
++              };
++              ret = i2c_transfer(client->adapter, msgs, 2);
++              if (ret < 0)
++                      dev_err(&client->dev, "i2c read error, %d\n", ret);
++      } else {
++              struct i2c_msg msgs[] = {
++                      {
++                               .addr = client->addr,
++                               .flags = I2C_M_RD,
++                               .len = readlen,
++                               .buf = readbuf,
++                       },
++              };
++              ret = i2c_transfer(client->adapter, msgs, 1);
++              if (ret < 0)
++                      dev_err(&client->dev, "i2c read error, %d\n", ret);
++      }
++
++      return ret;
++}
++
++static int fts_read_reg(struct i2c_client *client, u8 addr, u8 *val)
++{
++      return fts_i2c_read(client, &addr, 1, val, 1);
++}
++
++static int fts_check_fw_ver(struct i2c_client *client)
++{
++      u8 reg_addr, fw_ver[3];
++      int ret;
++
++      reg_addr = FT_REG_FW_VER;
++      ret = fts_i2c_read(client, &reg_addr, 1, &fw_ver[0], 1);
++      if (ret < 0)
++              goto error;
++
++      reg_addr = FT_REG_FW_MIN_VER;
++      ret = fts_i2c_read(client, &reg_addr, 1, &fw_ver[1], 1);
++      if (ret < 0)
++              goto error;
++
++      reg_addr = FT_REG_FW_SUB_MIN_VER;
++      ret = fts_i2c_read(client, &reg_addr, 1, &fw_ver[2], 1);
++      if (ret < 0)
++              goto error;
++
++      dev_info(&client->dev, "Firmware version = %d.%d.%d\n",
++                      fw_ver[0], fw_ver[1], fw_ver[2]);
++      return 0;
++
++error:
++      return ret;
++}
++
++static int fts_read_td_status(struct tinker_ft5406_data *ts_data)
++{
++      u8 td_status;
++      int ret = -1;
++
++      ret = fts_read_reg(ts_data->client, FT_TD_STATUS_REG, &td_status);
++      if (ret < 0) {
++              dev_err(&ts_data->client->dev,
++                              "Get reg td_status failed, %d\n", ret);
++              return ret;
++      }
++      return (int)td_status;
++}
++
++static int fts_read_touchdata(struct tinker_ft5406_data *ts_data)
++{
++      struct ts_event *event = &ts_data->event;
++      int ret = -1, i;
++      u8 buf[FT_ONE_TCH_LEN-2] = { 0 };
++      u8 reg_addr, pointid = FT_MAX_ID;
++
++      for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) {
++              reg_addr = FT_TOUCH_X_H_REG + (i * FT_ONE_TCH_LEN);
++              ret = fts_i2c_read(ts_data->client, &reg_addr, 1, buf, FT_ONE_TCH_LEN-2);
++              if (ret < 0) {
++                      dev_err(&ts_data->client->dev, "Read touchdata failed.\n");
++                      return ret;
++              }
++
++              pointid = (buf[FT_TOUCH_ID]) >> 4;
++              if (pointid >= MAX_TOUCH_POINTS)
++                      break;
++              event->au8_finger_id[i] = pointid;
++              event->au16_x[i] = (s16) (buf[FT_TOUCH_X_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_X_L];
++              event->au16_y[i] = (s16) (buf[FT_TOUCH_Y_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_Y_L];
++              event->au8_touch_event[i] = buf[FT_TOUCH_EVENT] >> 6;
++
++              if (ts_data->xy_reverse) {
++                      event->au16_x[i] = ts_data->screen_width - event->au16_x[i] - 1;
++                      event->au16_y[i] = ts_data->screen_height - event->au16_y[i] - 1;
++              }
++      }
++      event->pressure = FT_PRESS;
++
++      return 0;
++}
++
++static void fts_report_value(struct tinker_ft5406_data *ts_data)
++{
++      struct ts_event *event = &ts_data->event;
++      int i, modified_ids = 0, released_ids;
++
++      for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) {
++              if (event->au8_touch_event[i] == FT_TOUCH_DOWN ||
++                      event->au8_touch_event[i] == FT_TOUCH_CONTACT) {
++                      modified_ids |= 1 << event->au8_finger_id[i];
++                      input_mt_slot(ts_data->input_dev, event->au8_finger_id[i]);
++                      input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER,
++                              true);
++                      input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR,
++                                      event->pressure);
++                      input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X,
++                                      event->au16_x[i]);
++                      input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y,
++                                      event->au16_y[i]);
++
++                      if (!((1 << event->au8_finger_id[i]) & ts_data->known_ids))
++                              dev_dbg(&ts_data->client->dev, "Touch id-%d: x = %d, y = %d\n",
++                                      event->au8_finger_id[i],
++                                      event->au16_x[i],
++                                      event->au16_y[i]);
++              }
++      }
++
++      released_ids = ts_data->known_ids & ~modified_ids;
++      for (i = 0; released_ids && i < MAX_TOUCH_POINTS; i++) {
++              if (released_ids & (1<<i)) {
++                      dev_dbg(&ts_data->client->dev, "Release id-%d, known = %x modified = %x\n",
++                                      i, ts_data->known_ids, modified_ids);
++                      input_mt_slot(ts_data->input_dev, i);
++                      input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, false);
++                      modified_ids &= ~(1 << i);
++              }
++      }
++      ts_data->known_ids = modified_ids;
++      input_mt_report_pointer_emulation(ts_data->input_dev, true);
++      input_sync(ts_data->input_dev);
++}
++
++static void fts_retry_clear(struct tinker_ft5406_data *ts_data)
++{
++      if (ts_data->retry_count != 0)
++              ts_data->retry_count = 0;
++}
++
++static int fts_retry_wait(struct tinker_ft5406_data *ts_data)
++{
++      if (ts_data->retry_count < RETRY_COUNT) {
++              dev_info(&ts_data->client->dev,
++                      "Wait and retry, count = %d\n", ts_data->retry_count);
++              ts_data->retry_count++;
++              msleep(1000);
++              return 1;
++      }
++      dev_err(&ts_data->client->dev, "Attach retry count\n");
++      return 0;
++}
++
++static void tinker_ft5406_work(struct work_struct *work)
++{
++      struct ts_event *event = &g_ts_data->event;
++      int ret = 0, td_status;
++
++      /* polling 60fps */
++      while (!g_ts_data->finish_work) {
++              td_status = fts_read_td_status(g_ts_data);
++              if (td_status < 0) {
++                      ret = fts_retry_wait(g_ts_data);
++                      if (ret == 0) {
++                              dev_err(&g_ts_data->client->dev, "Stop touch polling\n");
++                              break;
++                      }
++              } else if (td_status < VALID_TD_STATUS_VAL + 1 &&
++                      (td_status > 0 || g_ts_data->known_ids != 0)) {
++                      fts_retry_clear(g_ts_data);
++                      memset(event, -1, sizeof(struct ts_event));
++                      event->touch_point = td_status;
++                      ret = fts_read_touchdata(g_ts_data);
++                      if (ret == 0)
++                              fts_report_value(g_ts_data);
++              }
++              msleep_interruptible(17);
++      }
++}
++
++static int tinker_ft5406_open(struct input_dev *dev)
++{
++      schedule_work(&g_ts_data->ft5406_work);
++      return 0;
++}
++
++static void tinker_ft5406_close(struct input_dev *dev)
++{
++      g_ts_data->finish_work = true;
++      cancel_work_sync(&g_ts_data->ft5406_work);
++      g_ts_data->finish_work = false;
++}
++
++static int tinker_ft5406_probe(struct i2c_client *client)
++{
++      struct input_dev *input_dev;
++      int ret = 0;
++
++      dev_info(&client->dev, "Address = 0x%x\n", client->addr);
++
++      g_ts_data = kzalloc(sizeof(struct tinker_ft5406_data), GFP_KERNEL);
++      if (g_ts_data == NULL) {
++              dev_err(&client->dev, "No memory for device\n");
++              return -ENOMEM;
++      }
++
++      g_ts_data->client = client;
++      i2c_set_clientdata(client, g_ts_data);
++
++      g_ts_data->screen_width = 800;
++      g_ts_data->screen_height = 480;
++      g_ts_data->xy_reverse = 1;
++
++      dev_info(&client->dev, "width = %d, height = %d, reverse = %d\n",
++                      g_ts_data->screen_width, g_ts_data->screen_height, g_ts_data->xy_reverse);
++
++      ret = fts_check_fw_ver(g_ts_data->client);
++      if (ret) {
++              dev_err(&client->dev, "Checking touch ic failed\n");
++              goto check_fw_err;
++      }
++
++      input_dev = input_allocate_device();
++      if (!input_dev) {
++              dev_err(&client->dev, "Failed to allocate input device\n");
++              goto input_allocate_failed;
++      }
++      input_dev->name = "fts_ts";
++      input_dev->id.bustype = BUS_I2C;
++      input_dev->dev.parent = &g_ts_data->client->dev;
++      input_dev->open = tinker_ft5406_open;
++      input_dev->close = tinker_ft5406_close;
++
++      g_ts_data->input_dev = input_dev;
++      input_set_drvdata(input_dev, g_ts_data);
++
++      __set_bit(EV_SYN, input_dev->evbit);
++      __set_bit(EV_KEY, input_dev->evbit);
++      __set_bit(EV_ABS, input_dev->evbit);
++      __set_bit(BTN_TOUCH, input_dev->keybit);
++
++      input_mt_init_slots(input_dev, MAX_TOUCH_POINTS, 0);
++      input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, g_ts_data->screen_width, 0, 0);
++      input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, g_ts_data->screen_height, 0, 0);
++
++      ret = input_register_device(input_dev);
++      if (ret) {
++              dev_err(&client->dev, "Input device registration failed\n");
++              goto input_register_failed;
++      }
++
++      INIT_WORK(&g_ts_data->ft5406_work, tinker_ft5406_work);
++
++      return 0;
++
++input_register_failed:
++      input_free_device(input_dev);
++input_allocate_failed:
++check_fw_err:
++      kfree(g_ts_data);
++      g_ts_data = NULL;
++      return ret;
++}
++
++static void tinker_ft5406_remove(struct i2c_client *client)
++{
++      cancel_work_sync(&g_ts_data->ft5406_work);
++      if (g_ts_data->input_dev) {
++              input_unregister_device(g_ts_data->input_dev);
++              input_free_device(g_ts_data->input_dev);
++      }
++      kfree(g_ts_data);
++      g_ts_data = NULL;
++}
++
++static const struct i2c_device_id tinker_ft5406_id[] = {
++      {"tinker_ft5406", 0},
++      {},
++};
++
++static struct i2c_driver tinker_ft5406_driver = {
++      .driver = {
++              .name = "tinker_ft5406",
++      },
++      .probe = tinker_ft5406_probe,
++      .remove = tinker_ft5406_remove,
++      .id_table = tinker_ft5406_id,
++};
++module_i2c_driver(tinker_ft5406_driver);
++
++MODULE_DESCRIPTION("TINKER BOARD FT5406 Touch driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch b/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch
new file mode 100644 (file)
index 0000000..69d7c7f
--- /dev/null
@@ -0,0 +1,246 @@
+From b477a1a53553336edcfeb83be1b35817928daed8 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 5 Jun 2023 14:46:16 +0800
+Subject: [PATCH 084/116] dt-binding: media: Add JH7110 Camera Subsystem.
+
+Add the bindings documentation for Starfive JH7110 Camera Subsystem
+which is used for handing image sensor data.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+Signed-off-by: Jack Zhu <jack.zhu@starfivetech.com>
+---
+ .../bindings/media/starfive,jh7110-camss.yaml | 228 ++++++++++++++++++
+ 1 file changed, 228 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
+@@ -0,0 +1,228 @@
++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Starfive SoC CAMSS ISP
++
++maintainers:
++  - Jack Zhu <jack.zhu@starfivetech.com>
++  - Changhuang Liang <changhuang.liang@starfivetech.com>
++
++description:
++  The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It
++  consists of a VIN controller (Video In Controller, a top-level control unit)
++  and an ISP.
++
++properties:
++  compatible:
++    const: starfive,jh7110-vin
++
++  reg:
++    maxItems: 8
++
++  reg-names:
++    items:
++      - const: csi2rx
++      - const: vclk
++      - const: vrst
++      - const: sctrl
++      - const: isp
++      - const: trst
++      - const: pmu
++      - const: syscrg
++
++  clocks:
++    maxItems: 16
++
++  clock-names:
++    items:
++      - const: clk_apb_func
++      - const: clk_pclk
++      - const: clk_sys_clk
++      - const: clk_wrapper_clk_c
++      - const: clk_dvp_inv
++      - const: clk_axiwr
++      - const: clk_mipi_rx0_pxl
++      - const: clk_pixel_clk_if0
++      - const: clk_pixel_clk_if1
++      - const: clk_pixel_clk_if2
++      - const: clk_pixel_clk_if3
++      - const: clk_m31dphy_cfgclk_in
++      - const: clk_m31dphy_refclk_in
++      - const: clk_m31dphy_txclkesc_lan0
++      - const: clk_ispcore_2x
++      - const: clk_isp_axi
++
++  resets:
++    maxItems: 14
++
++  reset-names:
++    items:
++      - const: rst_wrapper_p
++      - const: rst_wrapper_c
++      - const: rst_pclk
++      - const: rst_sys_clk
++      - const: rst_axird
++      - const: rst_axiwr
++      - const: rst_pixel_clk_if0
++      - const: rst_pixel_clk_if1
++      - const: rst_pixel_clk_if2
++      - const: rst_pixel_clk_if3
++      - const: rst_m31dphy_hw
++      - const: rst_m31dphy_b09_always_on
++      - const: rst_isp_top_n
++      - const: rst_isp_top_axi
++
++  power-domains:
++    items:
++      - description: JH7110 ISP Power Domain Switch Controller.
++
++  interrupts:
++    maxItems: 5
++
++  ports:
++    $ref: /schemas/graph.yaml#/properties/ports
++
++    properties:
++      port@0:
++        $ref: /schemas/graph.yaml#/$defs/port-base
++        unevaluatedProperties: false
++        description: Input port for receiving DVP data.
++
++        properties:
++          endpoint:
++            $ref: video-interfaces.yaml#
++            unevaluatedProperties: false
++
++            properties:
++              bus-type:
++                enum: [5, 6]
++
++              bus-width:
++                enum: [8, 10, 12]
++
++              data-shift:
++                enum: [0, 2]
++                default: 0
++
++              hsync-active:
++                enum: [0, 1]
++                default: 1
++
++              vsync-active:
++                enum: [0, 1]
++                default: 1
++
++            required:
++              - bus-type
++              - bus-width
++
++      port@1:
++        $ref: /schemas/graph.yaml#/properties/port
++        description: Input port for receiving CSI data.
++
++    required:
++      - port@0
++      - port@1
++
++required:
++  - compatible
++  - reg
++  - reg-names
++  - clocks
++  - clock-names
++  - resets
++  - reset-names
++  - power-domains
++  - interrupts
++  - ports
++
++additionalProperties: false
++
++examples:
++  - |
++              vin_sysctl: vin_sysctl@19800000 {
++                      compatible = "starfive,jh7110-vin";
++                      reg = <0x0 0x19800000 0x0 0x10000>,
++                                  <0x0 0x19810000 0x0 0x10000>,
++                                  <0x0 0x19820000 0x0 0x10000>,
++                                  <0x0 0x19840000 0x0 0x10000>,
++                                  <0x0 0x19870000 0x0 0x30000>,
++                                  <0x0 0x11840000 0x0 0x10000>,
++                                  <0x0 0x17030000 0x0 0x10000>,
++                                  <0x0 0x13020000 0x0 0x10000>;
++                      reg-names = "csi2rx", "vclk", "vrst", "sctrl",
++                                        "isp", "trst", "pmu", "syscrg";
++                      clocks = <&clkisp JH7110_DOM4_APB_FUNC>,
++                                     <&clkisp JH7110_U0_VIN_PCLK>,
++                                     <&clkisp JH7110_U0_VIN_SYS_CLK>,
++                                     <&clkisp JH7110_U0_ISPV2_TOP_WRAPPER_CLK_C>,
++                                     <&clkisp JH7110_DVP_INV>,
++                                     <&clkisp JH7110_U0_VIN_CLK_P_AXIWR>,
++                                     <&clkisp JH7110_MIPI_RX0_PXL>,
++                                     <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF0>,
++                                     <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF1>,
++                                     <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF2>,
++                                     <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF3>,
++                                     <&clkisp JH7110_U0_M31DPHY_CFGCLK_IN>,
++                                     <&clkisp JH7110_U0_M31DPHY_REFCLK_IN>,
++                                     <&clkisp JH7110_U0_M31DPHY_TXCLKESC_LAN0>,
++                                     <&clkgen JH7110_ISP_TOP_CLK_ISPCORE_2X>,
++                                     <&clkgen JH7110_ISP_TOP_CLK_ISP_AXI>;
++                      clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk",
++                                          "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr",
++                                          "clk_mipi_rx0_pxl", "clk_pixel_clk_if0",
++                                          "clk_pixel_clk_if1", "clk_pixel_clk_if2",
++                                          "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in",
++                                          "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0",
++                                          "clk_ispcore_2x", "clk_isp_axi";
++                      resets = <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_P>,
++                                     <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_C>,
++                                     <&rstgen RSTN_U0_VIN_N_PCLK>,
++                                     <&rstgen RSTN_U0_VIN_N_SYS_CLK>,
++                                     <&rstgen RSTN_U0_VIN_P_AXIRD>,
++                                     <&rstgen RSTN_U0_VIN_P_AXIWR>,
++                                     <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF0>,
++                                     <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF1>,
++                                     <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF2>,
++                                     <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF3>,
++                                     <&rstgen RSTN_U0_M31DPHY_HW>,
++                                     <&rstgen RSTN_U0_M31DPHY_B09_ALWAYS_ON>,
++                                     <&rstgen RSTN_U0_DOM_ISP_TOP_N>,
++                                     <&rstgen RSTN_U0_DOM_ISP_TOP_AXI>;
++                      reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk",
++                                      "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0",
++                                      "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3",
++                                      "rst_m31dphy_hw", "rst_m31dphy_b09_always_on",
++                                      "rst_isp_top_n", "rst_isp_top_axi";
++                      starfive,aon-syscon = <&aon_syscon 0x00>;
++                      power-domains = <&pwrc JH7110_PD_ISP>;
++                      /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */
++                      interrupts = <92>, <87>, <88>, <89>, <90>;
++
++      ports {
++        #address-cells = <1>;
++        #size-cells = <0>;
++        port@0 {
++          reg = <0>;
++          vin_from_sc2235: endpoint {
++            remote-endpoint = <&sc2235_to_vin>;
++            bus-type = <5>;
++            bus-width = <8>;
++            data-shift = <2>;
++            hsync-active = <1>;
++            vsync-active = <0>;
++            pclk-sample = <1>;
++          };
++        };
++
++        port@1 {
++          reg = <1>;
++          vin_from_csi2rx: endpoint {
++            remote-endpoint = <&csi2rx_to_vin>;
++          };
++        };
++      };
++    };
diff --git a/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch b/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch
new file mode 100644 (file)
index 0000000..b5d5f73
--- /dev/null
@@ -0,0 +1,24014 @@
+From 908b10ebc95eb29caae8c4737b23a29af5c6298f Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 5 Jun 2023 13:54:16 +0800
+Subject: [PATCH 085/116] media: starfive: Add vin driver support
+
+Add vin driver support.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/media/platform/Kconfig                |    1 +
+ drivers/media/platform/Makefile               |    1 +
+ drivers/media/platform/starfive/Kconfig       |   56 +
+ drivers/media/platform/starfive/Makefile      |   24 +
+ .../platform/starfive/v4l2_driver/Readme.txt  |   11 +
+ .../starfive/v4l2_driver/imx219_mipi.c        | 1583 ++++++++
+ .../starfive/v4l2_driver/ov13850_mipi.c       | 1921 ++++++++++
+ .../starfive/v4l2_driver/ov4689_mipi.c        | 2975 +++++++++++++++
+ .../platform/starfive/v4l2_driver/ov5640.c    | 3227 +++++++++++++++++
+ .../platform/starfive/v4l2_driver/sc2235.c    | 1914 ++++++++++
+ .../starfive/v4l2_driver/stf_common.h         |  185 +
+ .../platform/starfive/v4l2_driver/stf_csi.c   |  465 +++
+ .../platform/starfive/v4l2_driver/stf_csi.h   |   61 +
+ .../starfive/v4l2_driver/stf_csi_hw_ops.c     |  310 ++
+ .../starfive/v4l2_driver/stf_csiphy.c         |  357 ++
+ .../starfive/v4l2_driver/stf_csiphy.h         |  188 +
+ .../starfive/v4l2_driver/stf_csiphy_hw_ops.c  |  335 ++
+ .../starfive/v4l2_driver/stf_dmabuf.c         |  123 +
+ .../starfive/v4l2_driver/stf_dmabuf.h         |   12 +
+ .../platform/starfive/v4l2_driver/stf_dvp.c   |  385 ++
+ .../platform/starfive/v4l2_driver/stf_dvp.h   |   67 +
+ .../starfive/v4l2_driver/stf_dvp_hw_ops.c     |  187 +
+ .../platform/starfive/v4l2_driver/stf_event.c |   36 +
+ .../platform/starfive/v4l2_driver/stf_isp.c   | 1521 ++++++++
+ .../platform/starfive/v4l2_driver/stf_isp.h   |  222 ++
+ .../starfive/v4l2_driver/stf_isp_hw_ops.c     | 1550 ++++++++
+ .../starfive/v4l2_driver/stf_isp_ioctl.h      |  133 +
+ .../platform/starfive/v4l2_driver/stf_video.c | 1552 ++++++++
+ .../platform/starfive/v4l2_driver/stf_video.h |   83 +
+ .../platform/starfive/v4l2_driver/stf_vin.c   | 1515 ++++++++
+ .../platform/starfive/v4l2_driver/stf_vin.h   |  182 +
+ .../starfive/v4l2_driver/stf_vin_hw_ops.c     |  433 +++
+ .../platform/starfive/v4l2_driver/stfcamss.c  | 1369 +++++++
+ .../platform/starfive/v4l2_driver/stfcamss.h  |  117 +
+ include/uapi/linux/jh7110-isp.h               |  253 ++
+ include/uapi/linux/v4l2-controls.h            |    6 +
+ include/video/stf-vin.h                       |  443 +++
+ 37 files changed, 23803 insertions(+)
+ create mode 100644 drivers/media/platform/starfive/Kconfig
+ create mode 100644 drivers/media/platform/starfive/Makefile
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/Readme.txt
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov5640.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/sc2235.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_common.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_event.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.h
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.c
+ create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.h
+ create mode 100644 include/uapi/linux/jh7110-isp.h
+ create mode 100644 include/video/stf-vin.h
+
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/K
+ source "drivers/media/platform/rockchip/Kconfig"
+ source "drivers/media/platform/samsung/Kconfig"
+ source "drivers/media/platform/st/Kconfig"
++source "drivers/media/platform/starfive/Kconfig"
+ source "drivers/media/platform/sunxi/Kconfig"
+ source "drivers/media/platform/ti/Kconfig"
+ source "drivers/media/platform/verisilicon/Kconfig"
+--- a/drivers/media/platform/Makefile
++++ b/drivers/media/platform/Makefile
+@@ -23,6 +23,7 @@ obj-y += renesas/
+ obj-y += rockchip/
+ obj-y += samsung/
+ obj-y += st/
++obj-y += starfive/
+ obj-y += sunxi/
+ obj-y += ti/
+ obj-y += verisilicon/
+--- /dev/null
++++ b/drivers/media/platform/starfive/Kconfig
+@@ -0,0 +1,56 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++comment "Starfive media platform drivers"
++
++config VIN_SENSOR_OV5640
++      tristate "VIN SENSOR support OV5640"
++      depends on VIDEO_STF_VIN
++      select V4L2_FWNODE
++      default n
++      help
++        Say Y here if you want to have support for VIN sensor OV5640
++
++config VIN_SENSOR_SC2235
++      tristate "VIN SENSOR support SC2235"
++      depends on VIDEO_STF_VIN
++      select V4L2_FWNODE
++      default n
++      help
++        Say Y here if you want to have support for VIN sensor SC2235
++
++config VIN_SENSOR_OV4689
++      tristate "VIN SENSOR support OV4689"
++      depends on VIDEO_STF_VIN
++      select V4L2_FWNODE
++      default n
++
++      help
++        Say Y here if you want to have support for VIN sensor OV4689
++
++config VIN_SENSOR_OV13850
++      bool "VIN SENSOR support OV13850"
++      depends on VIDEO_STF_VIN
++      select V4L2_FWNODE
++      default n
++      help
++        Say Y here if you want to have support for VIN sensor OV13850
++
++config VIN_SENSOR_IMX219
++      tristate "VIN SENSOR support IMX219"
++      depends on VIDEO_STF_VIN
++      select V4L2_FWNODE
++      default n
++      help
++        Say Y here if you want to have support for VIN sensor IMX219
++
++config VIDEO_STF_VIN
++      tristate "starfive VIC video in support"
++      depends on V4L_PLATFORM_DRIVERS
++      depends on VIDEO_DEV
++      select MEDIA_CONTROLLER
++      select VIDEOBUF2_DMA_CONTIG
++      select VIDEO_V4L2_SUBDEV_API
++      select V4L2_FWNODE
++      help
++        To compile this driver as a module, choose M here: the module
++        will be called stf-vin.
+--- /dev/null
++++ b/drivers/media/platform/starfive/Makefile
+@@ -0,0 +1,24 @@
++# SPDX-License-Identifier: GPL-2.0
++
++obj-$(CONFIG_VIN_SENSOR_OV5640) += v4l2_driver/ov5640.o
++obj-$(CONFIG_VIN_SENSOR_SC2235) += v4l2_driver/sc2235.o
++obj-$(CONFIG_VIN_SENSOR_OV4689) += v4l2_driver/ov4689_mipi.o
++obj-$(CONFIG_VIN_SENSOR_OV13850) += v4l2_driver/ov13850_mipi.o
++obj-$(CONFIG_VIN_SENSOR_IMX219) += v4l2_driver/imx219_mipi.o
++
++starfivecamss-objs +=         v4l2_driver/stfcamss.o \
++                      v4l2_driver/stf_event.o \
++                      v4l2_driver/stf_dvp.o   \
++                      v4l2_driver/stf_csi.o   \
++                      v4l2_driver/stf_csiphy.o   \
++                      v4l2_driver/stf_isp.o   \
++                      v4l2_driver/stf_video.o \
++                      v4l2_driver/stf_vin.o \
++                      v4l2_driver/stf_vin_hw_ops.o \
++                      v4l2_driver/stf_csi_hw_ops.o \
++                      v4l2_driver/stf_csiphy_hw_ops.o \
++                      v4l2_driver/stf_isp_hw_ops.o \
++                      v4l2_driver/stf_dvp_hw_ops.o \
++                      v4l2_driver/stf_dmabuf.o
++
++obj-$(CONFIG_VIDEO_STF_VIN) += starfivecamss.o \
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/Readme.txt
+@@ -0,0 +1,11 @@
++
++/dev/video0: Output the camera data directly.
++/dev/video1: Output the data of the camera converted by isp.
++
++ensure linux/arch/riscv/configs/starfive_jh7110_defconfig:
++CONFIG_VIDEO_STF_VIN=y
++CONFIG_VIN_SENSOR_SC2235=y
++CONFIG_VIN_SENSOR_OV4689=y
++
++Only support the lane0/lane5 of dphy as clock lane, lane1/lane2/lane3/lane4
++as data lane.
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c
+@@ -0,0 +1,1583 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX219 cameras.
++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
++ *
++ * Based on Sony imx258 camera driver
++ * Copyright (C) 2018 Intel Corporation
++ *
++ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
++ * Copyright 2018 Qtechnology A/S
++ *
++ * Flip handling taken from the Sony IMX319 driver.
++ * Copyright (C) 2018 Intel Corporation
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++#include <asm/unaligned.h>
++
++#define IMX219_REG_VALUE_08BIT                1
++#define IMX219_REG_VALUE_16BIT                2
++
++#define IMX219_REG_MODE_SELECT                0x0100
++#define IMX219_MODE_STANDBY           0x00
++#define IMX219_MODE_STREAMING         0x01
++
++/* Chip ID */
++#define IMX219_REG_CHIP_ID            0x0000
++#define IMX219_CHIP_ID                        0x0219
++
++/* External clock frequency is 24.0M */
++#define IMX219_XCLK_FREQ              24000000
++
++/* Pixel rate is fixed at 182.4M for all the modes */
++#define IMX219_PIXEL_RATE             182400000
++
++#define IMX219_DEFAULT_LINK_FREQ      456000000
++
++/* V_TIMING internal */
++#define IMX219_REG_VTS                        0x0160
++#define IMX219_VTS_15FPS              0x0dc6
++#define IMX219_VTS_30FPS_1080P                0x06e3
++#define IMX219_VTS_30FPS_BINNED               0x06e3
++#define IMX219_VTS_30FPS_1280x720     0x06e3
++#define IMX219_VTS_30FPS_640x480      0x06e3
++#define IMX219_VTS_MAX                        0xffff
++
++#define IMX219_VBLANK_MIN             4
++
++/*Frame Length Line*/
++#define IMX219_FLL_MIN                        0x08a6
++#define IMX219_FLL_MAX                        0xffff
++#define IMX219_FLL_STEP                       1
++#define IMX219_FLL_DEFAULT            0x0c98
++
++/* HBLANK control - read only */
++#define IMX219_PPL_DEFAULT            3448
++
++/* Exposure control */
++#define IMX219_REG_EXPOSURE           0x015a
++#define IMX219_EXPOSURE_MIN           4
++#define IMX219_EXPOSURE_STEP          1
++#define IMX219_EXPOSURE_DEFAULT               0x640
++#define IMX219_EXPOSURE_MAX           65535
++
++/* Analog gain control */
++#define IMX219_REG_ANALOG_GAIN                0x0157
++#define IMX219_ANA_GAIN_MIN           0
++#define IMX219_ANA_GAIN_MAX           232
++#define IMX219_ANA_GAIN_STEP          1
++#define IMX219_ANA_GAIN_DEFAULT               0xd0
++
++/* Digital gain control */
++#define IMX219_REG_DIGITAL_GAIN               0x0158
++#define IMX219_DGTL_GAIN_MIN          0x0100
++#define IMX219_DGTL_GAIN_MAX          0x0fff
++#define IMX219_DGTL_GAIN_DEFAULT      0x0100
++#define IMX219_DGTL_GAIN_STEP         1
++
++#define IMX219_REG_ORIENTATION                0x0172
++
++/* Test Pattern Control */
++#define IMX219_REG_TEST_PATTERN               0x0600
++#define IMX219_TEST_PATTERN_DISABLE   0
++#define IMX219_TEST_PATTERN_SOLID_COLOR       1
++#define IMX219_TEST_PATTERN_COLOR_BARS        2
++#define IMX219_TEST_PATTERN_GREY_COLOR        3
++#define IMX219_TEST_PATTERN_PN9               4
++
++/* Test pattern colour components */
++#define IMX219_REG_TESTP_RED          0x0602
++#define IMX219_REG_TESTP_GREENR               0x0604
++#define IMX219_REG_TESTP_BLUE         0x0606
++#define IMX219_REG_TESTP_GREENB               0x0608
++#define IMX219_TESTP_COLOUR_MIN               0
++#define IMX219_TESTP_COLOUR_MAX               0x03ff
++#define IMX219_TESTP_COLOUR_STEP      1
++#define IMX219_TESTP_RED_DEFAULT      IMX219_TESTP_COLOUR_MAX
++#define IMX219_TESTP_GREENR_DEFAULT   0
++#define IMX219_TESTP_BLUE_DEFAULT     0
++#define IMX219_TESTP_GREENB_DEFAULT   0
++
++/* IMX219 native and active pixel array size. */
++#define IMX219_NATIVE_WIDTH           3296U
++#define IMX219_NATIVE_HEIGHT          2480U
++#define IMX219_PIXEL_ARRAY_LEFT               8U
++#define IMX219_PIXEL_ARRAY_TOP                8U
++#define IMX219_PIXEL_ARRAY_WIDTH      3280U
++#define IMX219_PIXEL_ARRAY_HEIGHT     2464U
++
++struct imx219_reg {
++      u16 address;
++      u8 val;
++};
++
++struct imx219_reg_list {
++      unsigned int num_of_regs;
++      const struct imx219_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx219_mode {
++      /* Frame width */
++      unsigned int width;
++      /* Frame height */
++      unsigned int height;
++
++      unsigned int fps;
++
++      /* Analog crop rectangle. */
++      struct v4l2_rect crop;
++
++      /* V-timing */
++      unsigned int vts_def;
++
++      /* Default register values */
++      struct imx219_reg_list reg_list;
++};
++
++/*
++ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
++ * driver.
++ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
++ */
++
++static const struct imx219_reg mode_1920_1080_regs[] = {
++      {0x0100, 0x00},
++      {0x30eb, 0x05},
++      {0x30eb, 0x0c},
++      {0x300a, 0xff},
++      {0x300b, 0xff},
++      {0x30eb, 0x05},
++      {0x30eb, 0x09},
++      {0x0114, 0x01},
++      {0x0128, 0x00},
++      {0x012a, 0x18},
++      {0x012b, 0x00},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++      {0x0164, 0x02},
++      {0x0165, 0xa8},
++      {0x0166, 0x0a},
++      {0x0167, 0x27},
++      {0x0168, 0x02},
++      {0x0169, 0xb4},
++      {0x016a, 0x06},
++      {0x016b, 0xeb},
++      {0x016c, 0x07},
++      {0x016d, 0x80},
++      {0x016e, 0x04},
++      {0x016f, 0x38},
++      {0x0170, 0x01},
++      {0x0171, 0x01},
++      {0x0174, 0x00},
++      {0x0175, 0x00},
++      {0x0301, 0x05},
++      {0x0303, 0x01},
++      {0x0304, 0x03},
++      {0x0305, 0x03},
++      {0x0306, 0x00},
++      {0x0307, 0x39},
++      {0x030b, 0x01},
++      {0x030c, 0x00},
++      {0x030d, 0x72},
++      {0x0624, 0x07},
++      {0x0625, 0x80},
++      {0x0626, 0x04},
++      {0x0627, 0x38},
++      {0x455e, 0x00},
++      {0x471e, 0x4b},
++      {0x4767, 0x0f},
++      {0x4750, 0x14},
++      {0x4540, 0x00},
++      {0x47b4, 0x14},
++      {0x4713, 0x30},
++      {0x478b, 0x10},
++      {0x478f, 0x10},
++      {0x4793, 0x10},
++      {0x4797, 0x0e},
++      {0x479b, 0x0e},
++};
++
++static const struct imx219_reg mode_1280_720_regs[] = {
++      {0x0100, 0x00},
++      {0x30eb, 0x05},
++      {0x30eb, 0x0c},
++      {0x300a, 0xff},
++      {0x300b, 0xff},
++      {0x30eb, 0x05},
++      {0x30eb, 0x09},
++      {0x0114, 0x01},
++      {0x0128, 0x00},
++      {0x012a, 0x18},
++      {0x012b, 0x00},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++      {0x0164, 0x01},
++      {0x0165, 0x68},
++      {0x0166, 0x0b},
++      {0x0167, 0x67},
++      {0x0168, 0x02},
++      {0x0169, 0x00},
++      {0x016a, 0x07},
++      {0x016b, 0x9f},
++      {0x016c, 0x05},
++      {0x016d, 0x00},
++      {0x016e, 0x02},
++      {0x016f, 0xd0},
++      {0x0170, 0x01},
++      {0x0171, 0x01},
++      {0x0174, 0x01},
++      {0x0175, 0x01},
++      {0x0301, 0x05},
++      {0x0303, 0x01},
++      {0x0304, 0x03},
++      {0x0305, 0x03},
++      {0x0306, 0x00},
++      {0x0307, 0x39},
++      {0x030b, 0x01},
++      {0x030c, 0x00},
++      {0x030d, 0x72},
++      {0x0624, 0x06},
++      {0x0625, 0x68},
++      {0x0626, 0x04},
++      {0x0627, 0xd0},
++      {0x455e, 0x00},
++      {0x471e, 0x4b},
++      {0x4767, 0x0f},
++      {0x4750, 0x14},
++      {0x4540, 0x00},
++      {0x47b4, 0x14},
++      {0x4713, 0x30},
++      {0x478b, 0x10},
++      {0x478f, 0x10},
++      {0x4793, 0x10},
++      {0x4797, 0x0e},
++      {0x479b, 0x0e},
++};
++
++static const struct imx219_reg mode_640_480_regs[] = {
++      {0x0100, 0x00},
++      {0x30eb, 0x05},
++      {0x30eb, 0x0c},
++      {0x300a, 0xff},
++      {0x300b, 0xff},
++      {0x30eb, 0x05},
++      {0x30eb, 0x09},
++      {0x0114, 0x01},
++      {0x0128, 0x00},
++      {0x012a, 0x18},
++      {0x012b, 0x00},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++      {0x0164, 0x03},
++      {0x0165, 0xe8},
++      {0x0166, 0x08},
++      {0x0167, 0xe7},
++      {0x0168, 0x02},
++      {0x0169, 0xf0},
++      {0x016a, 0x06},
++      {0x016b, 0xaf},
++      {0x016c, 0x02},
++      {0x016d, 0x80},
++      {0x016e, 0x01},
++      {0x016f, 0xe0},
++      {0x0170, 0x01},
++      {0x0171, 0x01},
++      {0x0174, 0x03},
++      {0x0175, 0x03},
++      {0x0301, 0x05},
++      {0x0303, 0x01},
++      {0x0304, 0x03},
++      {0x0305, 0x03},
++      {0x0306, 0x00},
++      {0x0307, 0x39},
++      {0x030b, 0x01},
++      {0x030c, 0x00},
++      {0x030d, 0x72},
++      {0x0624, 0x06},
++      {0x0625, 0x68},
++      {0x0626, 0x04},
++      {0x0627, 0xd0},
++      {0x455e, 0x00},
++      {0x471e, 0x4b},
++      {0x4767, 0x0f},
++      {0x4750, 0x14},
++      {0x4540, 0x00},
++      {0x47b4, 0x14},
++      {0x4713, 0x30},
++      {0x478b, 0x10},
++      {0x478f, 0x10},
++      {0x4793, 0x10},
++      {0x4797, 0x0e},
++      {0x479b, 0x0e},
++};
++
++static const struct imx219_reg raw8_framefmt_regs[] = {
++      {0x018c, 0x08},
++      {0x018d, 0x08},
++      {0x0309, 0x08},
++};
++
++static const struct imx219_reg raw10_framefmt_regs[] = {
++      {0x018c, 0x0a},
++      {0x018d, 0x0a},
++      {0x0309, 0x0a},
++};
++
++static const s64 imx219_link_freq_menu[] = {
++      IMX219_DEFAULT_LINK_FREQ,
++};
++
++static const char * const imx219_test_pattern_menu[] = {
++      "Disabled",
++      "Color Bars",
++      "Solid Color",
++      "Grey Color Bars",
++      "PN9"
++};
++
++static const int imx219_test_pattern_val[] = {
++      IMX219_TEST_PATTERN_DISABLE,
++      IMX219_TEST_PATTERN_COLOR_BARS,
++      IMX219_TEST_PATTERN_SOLID_COLOR,
++      IMX219_TEST_PATTERN_GREY_COLOR,
++      IMX219_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx219_supply_name[] = {
++      /* Supplies can be enabled in any order */
++      "VANA",  /* Analog (2.8V) supply */
++      "VDIG",  /* Digital Core (1.8V) supply */
++      "VDDL",  /* IF (1.2V) supply */
++};
++
++#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
++
++/*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++      MEDIA_BUS_FMT_SRGGB10_1X10,
++      MEDIA_BUS_FMT_SGRBG10_1X10,
++      MEDIA_BUS_FMT_SGBRG10_1X10,
++      MEDIA_BUS_FMT_SBGGR10_1X10,
++
++      MEDIA_BUS_FMT_SRGGB8_1X8,
++      MEDIA_BUS_FMT_SGRBG8_1X8,
++      MEDIA_BUS_FMT_SGBRG8_1X8,
++      MEDIA_BUS_FMT_SBGGR8_1X8,
++};
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software stanby) must be not less than:
++ *   t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
++ * where
++ *   t4 is fixed, and is max 200uS,
++ *   t5 is fixed, and is 6000uS,
++ *   t6 depends on the sensor external clock, and is max 32000 clock periods.
++ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
++ * So for any acceptable external clock t6 is always within the range of
++ * 1185 to 5333 uS, and is always less than t5.
++ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
++ * initialize the sensor over I2C, and then exit the software standby.
++ *
++ * This start-up time can be optimized a bit more, if we start the writes
++ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
++ * initialization over I2C may complete before (t4+t5) expires, and we must
++ * ensure that capture is not started before (t4+t5).
++ *
++ * This delay doesn't account for the power supply startup time. If needed,
++ * this should be taken care of via the regulator framework. E.g. in the
++ * case of DT for regulator-fixed one should define the startup-delay-us
++ * property.
++ */
++#define IMX219_XCLR_MIN_DELAY_US      6200
++#define IMX219_XCLR_DELAY_RANGE_US    1000
++
++/* Mode configs */
++static const struct imx219_mode supported_modes[] = {
++      {
++              /* 1080P 30fps cropped */
++              .width = 1920,
++              .height = 1080,
++              .fps = 30,
++              .crop = {
++                      .left = 688,
++                      .top = 700,
++                      .width = 1920,
++                      .height = 1080
++              },
++              .vts_def = IMX219_VTS_30FPS_1080P,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
++                      .regs = mode_1920_1080_regs,
++              },
++      },
++      {
++              /* 1280x720 30fps mode */
++              .width = 1280,
++              .height = 720,
++              .fps = 30,
++              .crop = {
++                      .left = 360,
++                      .top = 512,
++                      .width = 2560,
++                      .height = 1440
++              },
++              .vts_def = IMX219_VTS_30FPS_1280x720,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_1280_720_regs),
++                      .regs = mode_1280_720_regs,
++              },
++      },
++      {
++              /* 640x480 30fps mode */
++              .width = 640,
++              .height = 480,
++              .fps = 30,
++              .crop = {
++                      .left = 1008,
++                      .top = 760,
++                      .width = 1280,
++                      .height = 960
++              },
++              .vts_def = IMX219_VTS_30FPS_640x480,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_640_480_regs),
++                      .regs = mode_640_480_regs,
++              },
++      },
++};
++
++struct imx219 {
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++      //struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++
++      struct v4l2_mbus_framefmt fmt;
++
++      struct clk *xclk; /* system clock to IMX219 */
++      u32 xclk_freq;
++
++      struct gpio_desc *reset_gpio;
++      struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
++
++      struct v4l2_ctrl_handler ctrl_handler;
++      /* V4L2 Controls */
++      struct v4l2_ctrl *pixel_rate;
++      struct v4l2_ctrl *link_freq;
++      struct v4l2_ctrl *exposure;
++      struct v4l2_ctrl *vflip;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vblank;
++      struct v4l2_ctrl *hblank;
++
++      /* Current mode */
++      const struct imx219_mode *mode;
++      struct v4l2_fract frame_interval;
++
++      /*
++       * Mutex for serialized access:
++       * Protect sensor module set pad format and start/stop streaming safely.
++       */
++      struct mutex mutex;
++
++      /* Streaming on/off */
++      int streaming;
++};
++
++static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
++{
++      return container_of(_sd, struct imx219, sd);
++}
++
++/* Read registers up to 2 at a time */
++static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      struct i2c_msg msgs[2];
++      u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++      u8 data_buf[4] = { 0, };
++      int ret;
++
++      if (len > 4)
++              return -EINVAL;
++
++      /* Write register address */
++      msgs[0].addr = client->addr;
++      msgs[0].flags = 0;
++      msgs[0].len = ARRAY_SIZE(addr_buf);
++      msgs[0].buf = addr_buf;
++
++      /* Read data from register */
++      msgs[1].addr = client->addr;
++      msgs[1].flags = I2C_M_RD;
++      msgs[1].len = len;
++      msgs[1].buf = &data_buf[4 - len];
++
++      ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++      if (ret != ARRAY_SIZE(msgs))
++              return -EIO;
++
++      *val = get_unaligned_be32(data_buf);
++
++      return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      u8 buf[6];
++
++      if (len > 4)
++              return -EINVAL;
++
++      put_unaligned_be16(reg, buf);
++      put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++      if (i2c_master_send(client, buf, len + 2) != len + 2)
++              return -EIO;
++
++      return 0;
++}
++
++/* Write a list of registers */
++static int imx219_write_regs(struct imx219 *imx219,
++                           const struct imx219_reg *regs, u32 len)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      unsigned int i;
++      int ret;
++
++      for (i = 0; i < len; i++) {
++              ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
++              if (ret) {
++                      dev_err_ratelimited(&client->dev,
++                                          "Failed to write reg 0x%4.4x. error = %d\n",
++                                          regs[i].address, ret);
++
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx219_get_format_code(struct imx219 *imx219, u32 code)
++{
++      unsigned int i;
++
++      lockdep_assert_held(&imx219->mutex);
++
++      for (i = 0; i < ARRAY_SIZE(codes); i++)
++              if (codes[i] == code)
++                      break;
++
++      if (i >= ARRAY_SIZE(codes))
++              i = 0;
++
++      i = (i & ~3) | (imx219->vflip->val ? 2 : 0) |
++          (imx219->hflip->val ? 1 : 0);
++
++      return codes[i];
++}
++
++static void imx219_set_default_format(struct imx219 *imx219)
++{
++      struct v4l2_mbus_framefmt *fmt;
++
++      fmt = &imx219->fmt;
++      fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++              fmt->colorspace, fmt->ycbcr_enc);
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++      fmt->width = supported_modes[0].width;
++      fmt->height = supported_modes[0].height;
++      fmt->field = V4L2_FIELD_NONE;
++}
++
++static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      struct v4l2_mbus_framefmt *try_fmt =
++              v4l2_subdev_get_try_format(sd, fh->state, 0);
++      struct v4l2_rect *try_crop;
++
++      mutex_lock(&imx219->mutex);
++
++      /* Initialize try_fmt */
++      try_fmt->width = supported_modes[0].width;
++      try_fmt->height = supported_modes[0].height;
++      try_fmt->code = imx219_get_format_code(imx219, MEDIA_BUS_FMT_SRGGB10_1X10);
++      try_fmt->field = V4L2_FIELD_NONE;
++
++      /* Initialize try_crop rectangle. */
++      try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0);
++      try_crop->top = IMX219_PIXEL_ARRAY_TOP;
++      try_crop->left = IMX219_PIXEL_ARRAY_LEFT;
++      try_crop->width = IMX219_PIXEL_ARRAY_WIDTH;
++      try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT;
++
++      mutex_unlock(&imx219->mutex);
++
++      return 0;
++}
++
++static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct imx219 *imx219 =
++              container_of(ctrl->handler, struct imx219, ctrl_handler);
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      int ret;
++
++      if (ctrl->id == V4L2_CID_VBLANK) {
++              int exposure_max, exposure_def;
++
++              /* Update max exposure while meeting expected vblanking */
++              exposure_max = imx219->mode->height + ctrl->val - 4;
++              exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++                      exposure_max : IMX219_EXPOSURE_DEFAULT;
++              __v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum,
++                              exposure_max, imx219->exposure->step, exposure_def);
++      }
++
++      /*
++       * Applying V4L2 control value only happens
++       * when power is up for streaming
++       */
++      if (pm_runtime_get_if_in_use(&client->dev) == 0)
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
++                              IMX219_REG_VALUE_08BIT, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE:
++              ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
++                              IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_DIGITAL_GAIN:
++              ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
++                              IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
++                              IMX219_REG_VALUE_16BIT, imx219_test_pattern_val[ctrl->val]);
++              break;
++      case V4L2_CID_HFLIP:
++      case V4L2_CID_VFLIP:
++              ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
++                      imx219->hflip->val | imx219->vflip->val << 1);
++              break;
++      case V4L2_CID_VBLANK:
++              ret = imx219_write_reg(imx219, IMX219_REG_VTS, IMX219_REG_VALUE_16BIT,
++                              imx219->mode->height + ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_RED:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
++                              IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_GREENR:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
++                              IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_BLUE:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
++                              IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_GREENB:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
++                              IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      default:
++              dev_info(&client->dev,
++                       "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id, ctrl->val);
++              ret = -EINVAL;
++              break;
++      }
++
++      pm_runtime_put(&client->dev);
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
++      .s_ctrl = imx219_set_ctrl,
++};
++
++static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *state,
++                               struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++
++      if (code->index >= (ARRAY_SIZE(codes) / 4))
++              return -EINVAL;
++
++      mutex_lock(&imx219->mutex);
++      code->code = imx219_get_format_code(imx219, codes[code->index * 4]);
++      mutex_unlock(&imx219->mutex);
++
++      return 0;
++}
++
++static int imx219_enum_frame_size(struct v4l2_subdev *sd,
++                                struct v4l2_subdev_state *state,
++                                struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      u32 code;
++
++      if (fse->index >= ARRAY_SIZE(supported_modes))
++              return -EINVAL;
++
++      mutex_lock(&imx219->mutex);
++      code = imx219_get_format_code(imx219, fse->code);
++      mutex_unlock(&imx219->mutex);
++      if (fse->code != code)
++              return -EINVAL;
++
++      fse->min_width = supported_modes[fse->index].width;
++      fse->max_width = fse->min_width;
++      fse->min_height = supported_modes[fse->index].height;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static int imx219_try_frame_interval(struct imx219 *imx219,
++                                   struct v4l2_fract *fi,
++                                   u32 w, u32 h)
++{
++      const struct imx219_mode *mode;
++
++      mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
++                      width, height, w, h);
++      if (!mode || (mode->width != w || mode->height != h))
++              return -EINVAL;
++
++      fi->numerator = 1;
++      fi->denominator = mode->fps;
++
++      return mode->fps;
++}
++
++static int imx219_enum_frame_interval(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_frame_interval_enum *fie)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      struct v4l2_fract tpf;
++      u32 code;
++      int ret;
++
++      if (fie->index >= ARRAY_SIZE(supported_modes))
++              return -EINVAL;
++
++      mutex_lock(&imx219->mutex);
++      code = imx219_get_format_code(imx219, fie->code);
++      if (fie->code != code) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      ret = imx219_try_frame_interval(imx219, &tpf,
++                              fie->width, fie->height);
++      if (ret < 0) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      mutex_unlock(&imx219->mutex);
++      fie->interval = tpf;
++
++      return 0;
++
++out:
++      mutex_unlock(&imx219->mutex);
++      return ret;
++}
++
++static int imx219_g_frame_interval(struct v4l2_subdev *sd,
++                                 struct v4l2_subdev_frame_interval *fi)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++
++      mutex_lock(&imx219->mutex);
++      fi->interval = imx219->frame_interval;
++      mutex_unlock(&imx219->mutex);
++
++      return 0;
++}
++
++static int imx219_s_frame_interval(struct v4l2_subdev *sd,
++                                 struct v4l2_subdev_frame_interval *fi)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      const struct imx219_mode *mode = imx219->mode;
++      int frame_rate, ret = 0;
++
++      if (fi->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&imx219->mutex);
++
++      if (imx219->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      frame_rate = imx219_try_frame_interval(imx219, &fi->interval,
++                                             mode->width, mode->height);
++      if (frame_rate < 0) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      imx219->frame_interval = fi->interval;
++
++out:
++      mutex_unlock(&imx219->mutex);
++      return ret;
++}
++
++static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++                      fmt->colorspace, fmt->ycbcr_enc);
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx219_update_pad_format(struct imx219 *imx219,
++                                   const struct imx219_mode *mode,
++                                   struct v4l2_subdev_format *fmt)
++{
++      fmt->format.width = mode->width;
++      fmt->format.height = mode->height;
++      fmt->format.field = V4L2_FIELD_NONE;
++      imx219_reset_colorspace(&fmt->format);
++}
++
++static int __imx219_get_pad_format(struct imx219 *imx219,
++                                 struct v4l2_subdev_state *state,
++                                 struct v4l2_subdev_format *fmt)
++{
++      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++              struct v4l2_mbus_framefmt *try_fmt =
++                      v4l2_subdev_get_try_format(&imx219->sd, state, fmt->pad);
++              /* update the code which could change due to vflip or hflip: */
++              try_fmt->code = imx219_get_format_code(imx219, try_fmt->code);
++              fmt->format = *try_fmt;
++      } else {
++              imx219_update_pad_format(imx219, imx219->mode, fmt);
++              fmt->format.code = imx219_get_format_code(imx219, imx219->fmt.code);
++      }
++
++      return 0;
++}
++
++static int imx219_get_pad_format(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *state,
++                               struct v4l2_subdev_format *fmt)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      int ret;
++
++      mutex_lock(&imx219->mutex);
++      ret = __imx219_get_pad_format(imx219, state, fmt);
++      mutex_unlock(&imx219->mutex);
++
++      return ret;
++}
++
++static int imx219_set_pad_format(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_format *fmt)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      const struct imx219_mode *mode;
++      struct v4l2_mbus_framefmt *framefmt;
++      int exposure_max, exposure_def, hblank;
++      unsigned int i;
++
++      mutex_lock(&imx219->mutex);
++
++      for (i = 0; i < ARRAY_SIZE(codes); i++)
++              if (codes[i] == fmt->format.code)
++                      break;
++      if (i >= ARRAY_SIZE(codes))
++              i = 0;
++
++      /* Bayer order varies with flips */
++      fmt->format.code = imx219_get_format_code(imx219, codes[i]);
++
++      mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
++                      width, height, fmt->format.width, fmt->format.height);
++      imx219_update_pad_format(imx219, mode, fmt);
++      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++              framefmt = v4l2_subdev_get_try_format(sd, state, fmt->pad);
++              *framefmt = fmt->format;
++      } else if (imx219->mode != mode ||
++                 imx219->fmt.code != fmt->format.code) {
++              imx219->fmt = fmt->format;
++              imx219->mode = mode;
++              /* Update limits and set FPS to default */
++              __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
++                              IMX219_VTS_MAX - mode->height, 1,
++                              mode->vts_def - mode->height);
++              __v4l2_ctrl_s_ctrl(imx219->vblank, mode->vts_def - mode->height);
++              /* Update max exposure while meeting expected vblanking */
++              exposure_max = mode->vts_def - 4;
++              exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++                      exposure_max : IMX219_EXPOSURE_DEFAULT;
++              __v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum,
++                              exposure_max, imx219->exposure->step, exposure_def);
++              /*
++               * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
++               * depends on mode->width only, and is not changeble in any
++               * way other than changing the mode.
++               */
++              hblank = IMX219_PPL_DEFAULT - mode->width;
++              __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, hblank);
++      }
++
++      mutex_unlock(&imx219->mutex);
++
++      return 0;
++}
++
++static int imx219_set_framefmt(struct imx219 *imx219)
++{
++      switch (imx219->fmt.code) {
++      case MEDIA_BUS_FMT_SRGGB8_1X8:
++      case MEDIA_BUS_FMT_SGRBG8_1X8:
++      case MEDIA_BUS_FMT_SGBRG8_1X8:
++      case MEDIA_BUS_FMT_SBGGR8_1X8:
++              return imx219_write_regs(imx219, raw8_framefmt_regs,
++                              ARRAY_SIZE(raw8_framefmt_regs));
++
++      case MEDIA_BUS_FMT_SRGGB10_1X10:
++      case MEDIA_BUS_FMT_SGRBG10_1X10:
++      case MEDIA_BUS_FMT_SGBRG10_1X10:
++      case MEDIA_BUS_FMT_SBGGR10_1X10:
++              return imx219_write_regs(imx219, raw10_framefmt_regs,
++                              ARRAY_SIZE(raw10_framefmt_regs));
++      }
++
++      return -EINVAL;
++}
++
++static const struct v4l2_rect *
++__imx219_get_pad_crop(struct imx219 *imx219, struct v4l2_subdev_state *state,
++                              unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++      switch (which) {
++      case V4L2_SUBDEV_FORMAT_TRY:
++              return v4l2_subdev_get_try_crop(&imx219->sd, state, pad);
++      case V4L2_SUBDEV_FORMAT_ACTIVE:
++              return &imx219->mode->crop;
++      }
++
++      return NULL;
++}
++
++static int imx219_get_selection(struct v4l2_subdev *sd,
++                                      struct v4l2_subdev_state *state,
++                                      struct v4l2_subdev_selection *sel)
++{
++      switch (sel->target) {
++      case V4L2_SEL_TGT_CROP: {
++              struct imx219 *imx219 = to_imx219(sd);
++
++              mutex_lock(&imx219->mutex);
++              sel->r = *__imx219_get_pad_crop(imx219, state, sel->pad, sel->which);
++              mutex_unlock(&imx219->mutex);
++              return 0;
++      }
++
++      case V4L2_SEL_TGT_NATIVE_SIZE:
++              sel->r.top = 0;
++              sel->r.left = 0;
++              sel->r.width = IMX219_NATIVE_WIDTH;
++              sel->r.height = IMX219_NATIVE_HEIGHT;
++              return 0;
++
++      case V4L2_SEL_TGT_CROP_DEFAULT:
++      case V4L2_SEL_TGT_CROP_BOUNDS:
++              sel->r.top = IMX219_PIXEL_ARRAY_TOP;
++              sel->r.left = IMX219_PIXEL_ARRAY_LEFT;
++              sel->r.width = IMX219_PIXEL_ARRAY_WIDTH;
++              sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
++              return 0;
++      }
++
++      return -EINVAL;
++}
++
++static int imx219_start_streaming(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      const struct imx219_reg_list *reg_list;
++      int ret;
++
++      /* Apply default values of current mode */
++      reg_list = &imx219->mode->reg_list;
++      ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
++      if (ret) {
++              dev_err(&client->dev, "%s failed to set mode\n", __func__);
++              goto err;
++      }
++
++      ret = imx219_set_framefmt(imx219);
++      if (ret) {
++              dev_err(&client->dev, "%s failed to set frame format: %d\n",
++                      __func__, ret);
++              goto err;
++      }
++
++      /* Apply customized values from user */
++      ret =  __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
++      if (ret)
++              goto err;
++
++      /* set stream on register */
++      ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++                             IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++      if (ret)
++              goto err;
++
++      /* vflip and hflip cannot change during streaming */
++      __v4l2_ctrl_grab(imx219->vflip, true);
++      __v4l2_ctrl_grab(imx219->hflip, true);
++
++      return 0;
++
++err:
++      return ret;
++}
++
++static void imx219_stop_streaming(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      int ret;
++
++      /* set stream off register */
++      ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++                      IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++      if (ret)
++              dev_err(&client->dev, "%s failed to set stream\n", __func__);
++
++      __v4l2_ctrl_grab(imx219->vflip, false);
++      __v4l2_ctrl_grab(imx219->hflip, false);
++}
++
++static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      struct i2c_client *client = v4l2_get_subdevdata(sd);
++      int ret = 0;
++
++      mutex_lock(&imx219->mutex);
++
++      if (enable) {
++              ret = pm_runtime_get_sync(&client->dev);
++              if (ret < 0) {
++                      pm_runtime_put_noidle(&client->dev);
++                      mutex_unlock(&imx219->mutex);
++                      return ret;
++              }
++
++              if (imx219->streaming)
++                      goto unlock;
++
++              /*
++               * Apply default & customized values
++               * and then start streaming.
++               */
++              ret = imx219_start_streaming(imx219);
++              if (ret)
++                      goto err_unlock;
++      } else {
++              imx219_stop_streaming(imx219);
++              pm_runtime_put(&client->dev);
++      }
++
++unlock:
++      imx219->streaming += enable ? 1 : -1;
++      WARN_ON(imx219->streaming < 0);
++
++      mutex_unlock(&imx219->mutex);
++
++      return ret;
++
++err_unlock:
++      pm_runtime_put(&client->dev);
++      mutex_unlock(&imx219->mutex);
++
++      return ret;
++}
++
++/* Power/clock management functions */
++static int imx219_power_on(struct device *dev)
++{
++      struct v4l2_subdev *sd = dev_get_drvdata(dev);
++      struct imx219 *imx219 = to_imx219(sd);
++      int ret;
++
++      ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, imx219->supplies);
++      if (ret) {
++              dev_err(dev, "%s: failed to enable regulators\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = clk_prepare_enable(imx219->xclk);
++      if (ret) {
++              dev_err(dev, "%s: failed to enable clock\n", __func__);
++              goto reg_off;
++      }
++
++      gpiod_set_value_cansleep(imx219->reset_gpio, 1);
++      usleep_range(IMX219_XCLR_MIN_DELAY_US,
++                      IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
++
++      return 0;
++
++reg_off:
++      regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++
++      return ret;
++}
++
++static int imx219_power_off(struct device *dev)
++{
++      struct v4l2_subdev *sd = dev_get_drvdata(dev);
++      struct imx219 *imx219 = to_imx219(sd);
++
++      gpiod_set_value_cansleep(imx219->reset_gpio, 0);
++      regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++      clk_disable_unprepare(imx219->xclk);
++
++      return 0;
++}
++
++static int imx219_get_regulators(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      unsigned int i;
++
++      for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
++              imx219->supplies[i].supply = imx219_supply_name[i];
++
++      return devm_regulator_bulk_get(&client->dev,
++                      IMX219_NUM_SUPPLIES, imx219->supplies);
++}
++
++/* Verify chip ID */
++static int imx219_identify_module(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      int ret;
++      u32 val;
++
++      ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
++                            IMX219_REG_VALUE_16BIT, &val);
++      if (ret) {
++              dev_err(&client->dev, "failed to read chip id %x\n",
++                      IMX219_CHIP_ID);
++              return ret;
++      }
++
++      if (val != IMX219_CHIP_ID) {
++              dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++                      IMX219_CHIP_ID, val);
++              return -EIO;
++      }
++
++      dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++              __func__, IMX219_CHIP_ID);
++
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx219_core_ops = {
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx219_video_ops = {
++      .g_frame_interval = imx219_g_frame_interval,
++      .s_frame_interval = imx219_s_frame_interval,
++      .s_stream = imx219_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
++      .enum_mbus_code = imx219_enum_mbus_code,
++      .get_fmt = imx219_get_pad_format,
++      .set_fmt = imx219_set_pad_format,
++      .get_selection = imx219_get_selection,
++      .enum_frame_size = imx219_enum_frame_size,
++      .enum_frame_interval = imx219_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops imx219_subdev_ops = {
++      .core = &imx219_core_ops,
++      .video = &imx219_video_ops,
++      .pad = &imx219_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
++      .open = imx219_open,
++};
++
++/* Initialize control handlers */
++static int imx219_init_controls(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      struct v4l2_ctrl_handler *ctrl_hdlr;
++      unsigned int height = imx219->mode->height;
++      struct v4l2_fwnode_device_properties props;
++      int exposure_max, exposure_def, hblank;
++      int i, ret;
++
++      ctrl_hdlr = &imx219->ctrl_handler;
++      ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
++      if (ret)
++              return ret;
++
++      mutex_init(&imx219->mutex);
++      ctrl_hdlr->lock = &imx219->mutex;
++
++      /* By default, PIXEL_RATE is read only */
++      imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                      V4L2_CID_PIXEL_RATE, IMX219_PIXEL_RATE,
++                      IMX219_PIXEL_RATE, 1, IMX219_PIXEL_RATE);
++
++      imx219->link_freq =
++              v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_LINK_FREQ,
++                      ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, imx219_link_freq_menu);
++      if (imx219->link_freq)
++              imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++      /* Initial vblank/hblank/exposure parameters based on current mode */
++      imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                      V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
++                                      IMX219_VTS_MAX - height, 1,
++                                      imx219->mode->vts_def - height);
++      hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
++      imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                      V4L2_CID_HBLANK, hblank, hblank, 1, hblank);
++      if (imx219->hblank)
++              imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      exposure_max = imx219->mode->vts_def - 4;
++      exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++              exposure_max : IMX219_EXPOSURE_DEFAULT;
++      imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                              V4L2_CID_EXPOSURE, IMX219_EXPOSURE_MIN, exposure_max,
++                              IMX219_EXPOSURE_STEP, exposure_def);
++
++      v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++                      IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
++                      IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
++
++      v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++                      IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
++                      IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
++
++      imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                        V4L2_CID_HFLIP, 0, 1, 1, 0);
++      if (imx219->hflip)
++              imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                        V4L2_CID_VFLIP, 0, 1, 1, 0);
++      if (imx219->vflip)
++              imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_TEST_PATTERN,
++                      ARRAY_SIZE(imx219_test_pattern_menu) - 1,
++                      0, 0, imx219_test_pattern_menu);
++      for (i = 0; i < 4; i++) {
++              /*
++               * The assumption is that
++               * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++               * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
++               * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++               */
++              v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                              V4L2_CID_TEST_PATTERN_RED + i,
++                              IMX219_TESTP_COLOUR_MIN,
++                              IMX219_TESTP_COLOUR_MAX,
++                              IMX219_TESTP_COLOUR_STEP,
++                              IMX219_TESTP_COLOUR_MAX);
++              /* The "Solid color" pattern is white by default */
++      }
++
++      if (ctrl_hdlr->error) {
++              ret = ctrl_hdlr->error;
++              dev_err(&client->dev, "%s control init failed (%d)\n",
++                      __func__, ret);
++              goto error;
++      }
++
++      ret = v4l2_fwnode_device_parse(&client->dev, &props);
++      if (ret)
++              goto error;
++
++      ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx219_ctrl_ops, &props);
++      if (ret)
++              goto error;
++
++      imx219->sd.ctrl_handler = ctrl_hdlr;
++
++      return 0;
++
++error:
++      v4l2_ctrl_handler_free(ctrl_hdlr);
++      mutex_destroy(&imx219->mutex);
++
++      return ret;
++}
++
++static void imx219_free_controls(struct imx219 *imx219)
++{
++      v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
++      mutex_destroy(&imx219->mutex);
++}
++
++static int imx219_check_hwcfg(struct device *dev)
++{
++      struct fwnode_handle *endpoint;
++      struct v4l2_fwnode_endpoint ep_cfg = {
++              .bus_type = V4L2_MBUS_CSI2_DPHY
++      };
++      int ret = -EINVAL;
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++              dev_err(dev, "could not parse endpoint\n");
++              goto error_out;
++      }
++
++      /* Check the number of MIPI CSI2 data lanes */
++      if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++              dev_err(dev, "only 2 data lanes are currently supported\n");
++              goto error_out;
++      }
++
++      /* Check the link frequency set in device tree */
++      if (!ep_cfg.nr_of_link_frequencies) {
++              dev_err(dev, "link-frequency property not found in DT\n");
++              goto error_out;
++      }
++
++      if (ep_cfg.nr_of_link_frequencies != 1 ||
++          ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
++              dev_err(dev, "Link frequency not supported: %lld\n",
++                      ep_cfg.link_frequencies[0]);
++              goto error_out;
++      }
++
++      ret = 0;
++
++error_out:
++      v4l2_fwnode_endpoint_free(&ep_cfg);
++      fwnode_handle_put(endpoint);
++
++      return ret;
++}
++
++static int imx219_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct imx219 *imx219;
++      int ret;
++
++      imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
++      if (!imx219)
++              return -ENOMEM;
++
++      v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
++
++      /* Check the hardware configuration in device tree */
++      if (imx219_check_hwcfg(dev))
++              return -EINVAL;
++
++      /* Get system clock (xclk) */
++      imx219->xclk = devm_clk_get(dev, NULL);
++      if (IS_ERR(imx219->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(imx219->xclk);
++      }
++
++      imx219->xclk_freq = clk_get_rate(imx219->xclk);
++      if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
++              dev_err(dev, "xclk frequency not supported: %d Hz\n",
++                      imx219->xclk_freq);
++              return -EINVAL;
++      }
++
++      ret = imx219_get_regulators(imx219);
++      if (ret) {
++              dev_err(dev, "failed to get regulators\n");
++              return ret;
++      }
++
++      /* Request optional enable pin */
++      imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
++      usleep_range(IMX219_XCLR_MIN_DELAY_US,
++                      IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
++
++      /*
++       * The sensor must be powered for imx219_identify_module()
++       * to be able to read the CHIP_ID register
++       */
++      ret = imx219_power_on(dev);
++      if (ret)
++              return ret;
++
++      ret = imx219_identify_module(imx219);
++      if (ret)
++              goto error_power_off;
++
++      /* Set default mode to max resolution */
++      imx219->mode = &supported_modes[0];
++      imx219->frame_interval.numerator = 1;
++      imx219->frame_interval.denominator = supported_modes[0].fps;
++
++      /* sensor doesn't enter LP-11 state upon power up until and unless
++       * streaming is started, so upon power up switch the modes to:
++       * streaming -> standby
++       */
++      ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++                      IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++      if (ret < 0)
++              goto error_power_off;
++      usleep_range(100, 110);
++
++      /* put sensor back to standby mode */
++      ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++                      IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++      if (ret < 0)
++              goto error_power_off;
++      usleep_range(100, 110);
++
++      ret = imx219_init_controls(imx219);
++      if (ret)
++              goto error_power_off;
++
++      /* Initialize subdev */
++      imx219->sd.internal_ops = &imx219_internal_ops;
++      imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
++      imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++      /* Initialize source pad */
++      imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++      /* Initialize default format */
++      imx219_set_default_format(imx219);
++
++      ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
++      if (ret) {
++              dev_err(dev, "failed to init entity pads: %d\n", ret);
++              goto error_handler_free;
++      }
++
++      ret = v4l2_async_register_subdev_sensor(&imx219->sd);
++      if (ret < 0) {
++              dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++              goto error_media_entity;
++      }
++
++      /* Enable runtime PM and turn off the device */
++      pm_runtime_set_active(dev);
++      pm_runtime_enable(dev);
++      pm_runtime_idle(dev);
++
++      return 0;
++
++error_media_entity:
++      media_entity_cleanup(&imx219->sd.entity);
++
++error_handler_free:
++      imx219_free_controls(imx219);
++
++error_power_off:
++      imx219_power_off(dev);
++
++      return ret;
++}
++
++static void imx219_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx219 *imx219 = to_imx219(sd);
++
++      v4l2_async_unregister_subdev(sd);
++      media_entity_cleanup(&sd->entity);
++      imx219_free_controls(imx219);
++
++      pm_runtime_disable(&client->dev);
++      if (!pm_runtime_status_suspended(&client->dev))
++              imx219_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id imx219_dt_ids[] = {
++      { .compatible = "sony,imx219" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx219_dt_ids);
++
++static const struct dev_pm_ops imx219_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
++      SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
++};
++
++static struct i2c_driver imx219_i2c_driver = {
++      .driver = {
++              .name = "imx219",
++              .of_match_table = imx219_dt_ids,
++              .pm = &imx219_pm_ops,
++      },
++      .probe = imx219_probe,
++      .remove = imx219_remove,
++};
++
++module_i2c_driver(imx219_i2c_driver);
++
++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
++MODULE_DESCRIPTION("Sony IMX219 sensor driver");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c
+@@ -0,0 +1,1921 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++#define OV13850_XCLK_MIN  6000000
++#define OV13850_XCLK_MAX 54000000
++
++#define OV13850_LINK_FREQ_500MHZ         500000000LL
++
++/**
++ *OV13850 PLL
++ *
++ *PLL1:
++ *
++ *REF_CLK -> /PREDIVP[0] -> /PREDIV[2:0] -> *DIVP[9:8,7:0] -> /DIVM[3:0] -> /DIV_MIPI[1:0]  -> PCLK
++ *(6-64M)     0x030A         0x0300          0x0301,0x302      0x0303        0x0304
++ *                                                                      `-> MIPI_PHY_CLK
++ *
++ *
++ *PLL2:
++ *                           000: 1
++ *                           001: 1.5
++ *                           010: 2
++ *                           011: 2.5
++ *                           100: 3
++ *                           101: 4
++ *            0: /1          110: 6
++ *            1: /2          111: 8
++ *REF_CLK -> /PREDIVP[3] -> /PREDIV[2:0] -> /DIVP[9:0] -> /DIVDAC[3:0] -> DAC_CLK =
++ *(6~64M)    0x3611
++ *                                                     -> /DIVSP[3:0] -> /DIVS[2:0] -> SCLK
++ *
++ *                                                     -> /(1+DIVSRAM[3:0]) -> SRAM_CLK
++ */
++
++// PREDIVP
++#define OV13850_REG_PLL1_PREDIVP        0x030a
++#define OV13850_PREDIVP_1   0
++#define OV13850_PREDIVP_2   1
++
++// PREDIV
++#define OV13850_REG_PLL1_PREDIV         0x0300
++#define OV13850_PREDIV_1    0
++#define OV13850_PREDIV_1_5  1
++#define OV13850_PREDIV_2    2
++#define OV13850_PREDIV_2_5  3
++#define OV13850_PREDIV_3    4
++#define OV13850_PREDIV_4    5
++#define OV13850_PREDIV_6    6
++#define OV13850_PREDIV_8    7
++
++// DIVP
++#define OV13850_REG_PLL1_DIVP_H         0x0301
++#define OV13850_REG_PLL1_DIVP_L         0x0302
++#define OV13850_REG_PLL1_DIVP           OV13850_REG_PLL1_DIVP_H
++
++// DIVM
++#define OV13850_REG_PLL1_DIVM           0x0303
++#define OV13850_DIVM(n)     ((n)-1) // n=1~16
++
++// DIV_MIPI
++#define OV13850_REG_PLL1_DIV_MIPI       0x0304
++#define OV13850_DIV_MIPI_4  0
++#define OV13850_DIV_MIPI_5  1
++#define OV13850_DIV_MIPI_6  2
++#define OV13850_DIV_MIPI_8  3
++
++// system control
++#define OV13850_STREAM_CTRL             0x0100
++#define OV13850_REG_MIPI_SC             0x300f
++#define OV13850_MIPI_SC_8_BIT           0x0
++#define OV13850_MIPI_SC_10_BIT          0x1
++#define OV13850_MIPI_SC_12_BIT          0x2
++#define OV13850_GET_MIPI_SC_MIPI_BIT(v)         ((v) & 0x3)
++#define OV13850_REG_MIPI_SC_CTRL0       0x3012
++#define OV13850_GET_MIPI_SC_CTRL0_LANE_NUM(v)   ((v)>>4 & 0xf)
++
++// timing
++#define OV13850_REG_H_CROP_START_H      0x3800
++#define OV13850_REG_H_CROP_START_L      0x3801
++#define OV13850_REG_H_CROP_START        OV13850_REG_H_CROP_START_H
++#define OV13850_REG_V_CROP_START_H      0x3802
++#define OV13850_REG_V_CROP_START_L      0x3803
++#define OV13850_REG_V_CROP_START        OV13850_REG_V_CROP_START_H
++
++#define OV13850_REG_H_CROP_END_H        0x3804
++#define OV13850_REG_H_CROP_END_L        0x3805
++#define OV13850_REG_H_CROP_END          OV13850_REG_H_CROP_END_H
++#define OV13850_REG_V_CROP_END_H        0x3806
++#define OV13850_REG_V_CROP_END_L        0x3807
++#define OV13850_REG_V_CROP_END          OV13850_REG_V_CROP_END_H
++
++#define OV13850_REG_H_OUTPUT_SIZE_H     0x3808
++#define OV13850_REG_H_OUTPUT_SIZE_L     0x3809
++#define OV13850_REG_H_OUTPUT_SIZE       OV13850_REG_H_OUTPUT_SIZE_H
++#define OV13850_REG_V_OUTPUT_SIZE_H     0x380a
++#define OV13850_REG_V_OUTPUT_SIZE_L     0x380b
++#define OV13850_REG_V_OUTPUT_SIZE       OV13850_REG_V_OUTPUT_SIZE_H
++
++#define OV13850_REG_TIMING_HTS_H        0x380c
++#define OV13850_REG_TIMING_HTS_L        0x380d
++#define OV13850_REG_TIMING_HTS          OV13850_REG_TIMING_HTS_H
++#define OV13850_REG_TIMING_VTS_H        0x380e
++#define OV13850_REG_TIMING_VTS_L        0x380f
++#define OV13850_REG_TIMING_VTS          OV13850_REG_TIMING_VTS_H
++
++
++#define OV13850_REG_H_WIN_OFF_H         0x3810
++#define OV13850_REG_H_WIN_OFF_L         0x3811
++#define OV13850_REG_V_WIN_OFF_H         0x3812
++#define OV13850_REG_V_WIN_OFF_L         0x3813
++
++#define OV13850_REG_H_INC               0x3814
++#define OV13850_REG_V_INC               0x3815
++
++enum ov13850_mode_id {
++      OV13850_MODE_1080P_1920_1080 = 0,
++      OV13850_NUM_MODES,
++};
++
++enum ov13850_frame_rate {
++      OV13850_15_FPS = 0,
++      OV13850_30_FPS,
++      OV13850_60_FPS,
++      OV13850_NUM_FRAMERATES,
++};
++
++static const int ov13850_framerates[] = {
++      [OV13850_15_FPS] = 15,
++      [OV13850_30_FPS] = 30,
++      [OV13850_60_FPS] = 60,
++};
++
++struct ov13850_pixfmt {
++      u32 code;
++      u32 colorspace;
++};
++
++static const struct ov13850_pixfmt ov13850_formats[] = {
++      { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, },
++};
++
++/* regulator supplies */
++static const char * const ov13850_supply_name[] = {
++      "DOVDD", /* Digital I/O (1.8V) supply */
++      "AVDD",  /* Analog (2.8V) supply */
++      "DVDD",  /* Digital Core (1.5V) supply */
++};
++
++#define OV13850_NUM_SUPPLIES ARRAY_SIZE(ov13850_supply_name)
++
++/*
++ * Image size under 1280 * 960 are SUBSAMPLING
++ * Image size upper 1280 * 960 are SCALING
++ */
++enum ov13850_downsize_mode {
++      SUBSAMPLING,
++      SCALING,
++};
++
++struct reg_value {
++      u16 reg_addr;
++      u8 val;
++      u8 mask;
++      u32 delay_ms;
++};
++
++struct ov13850_mode_info {
++      enum ov13850_mode_id id;
++      enum ov13850_downsize_mode dn_mode;
++      u32 hact;
++      u32 htot;
++      u32 vact;
++      u32 vtot;
++      const struct reg_value *reg_data;
++      u32 reg_data_size;
++      u32 max_fps;
++};
++
++struct ov13850_ctrls {
++      struct v4l2_ctrl_handler handler;
++      struct v4l2_ctrl *pixel_rate;
++      struct {
++              struct v4l2_ctrl *exposure;
++      };
++      struct {
++              struct v4l2_ctrl *auto_wb;
++              struct v4l2_ctrl *blue_balance;
++              struct v4l2_ctrl *red_balance;
++      };
++      struct {
++              struct v4l2_ctrl *anal_gain;
++      };
++      struct v4l2_ctrl *brightness;
++      struct v4l2_ctrl *light_freq;
++      struct v4l2_ctrl *link_freq;
++      struct v4l2_ctrl *saturation;
++      struct v4l2_ctrl *contrast;
++      struct v4l2_ctrl *hue;
++      struct v4l2_ctrl *test_pattern;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vflip;
++};
++
++struct ov13850_dev {
++      struct i2c_client *i2c_client;
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++      struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++      struct clk *xclk; /* system clock to OV13850 */
++      u32 xclk_freq;
++
++      struct regulator_bulk_data supplies[OV13850_NUM_SUPPLIES];
++      struct gpio_desc *reset_gpio;
++      struct gpio_desc *pwdn_gpio;
++      bool   upside_down;
++
++      /* lock to protect all members below */
++      struct mutex lock;
++
++      int power_count;
++
++      struct v4l2_mbus_framefmt fmt;
++      bool pending_fmt_change;
++
++      const struct ov13850_mode_info *current_mode;
++      const struct ov13850_mode_info *last_mode;
++      enum ov13850_frame_rate current_fr;
++      struct v4l2_fract frame_interval;
++
++      struct ov13850_ctrls ctrls;
++
++      u32 prev_sysclk, prev_hts;
++      u32 ae_low, ae_high, ae_target;
++
++      bool pending_mode_change;
++      bool streaming;
++};
++
++static inline struct ov13850_dev *to_ov13850_dev(struct v4l2_subdev *sd)
++{
++      return container_of(sd, struct ov13850_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++      return &container_of(ctrl->handler, struct ov13850_dev,
++                      ctrls.handler)->sd;
++}
++
++/* ov13850 initial register */
++static const struct reg_value ov13850_init_setting_30fps_1080P[] = {
++
++};
++
++static const struct reg_value ov13850_setting_1080P_1920_1080[] = {
++//;XVCLK=24Mhz, SCLK=4x120Mhz, MIPI 640Mbps, DACCLK=240Mhz
++/*
++ * using quarter size to scale down
++ */
++      {0x0103, 0x01, 0, 0}, // ; software reset
++
++      {0x0300, 0x01, 0, 0}, //; PLL
++      {0x0301, 0x00, 0, 0}, //; PLL1_DIVP_hi
++      {0x0302, 0x28, 0, 0}, //; PLL1_DIVP_lo
++      {0x0303, 0x00, 0, 0}, // ; PLL
++      {0x030a, 0x00, 0, 0}, // ; PLL
++      //{0xffff, 20, 0, 0},
++      {0x300f, 0x11, 0, 0}, // SFC modified, MIPI_SRC, [1:0] 00-8bit, 01-10bit, 10-12bit
++      {0x3010, 0x01, 0, 0}, // ; MIPI PHY
++      {0x3011, 0x76, 0, 0}, // ; MIPI PHY
++      {0x3012, 0x41, 0, 0}, // ; MIPI 4 lane
++      {0x3013, 0x12, 0, 0}, // ; MIPI control
++      {0x3014, 0x11, 0, 0}, // ; MIPI control
++      {0x301f, 0x03, 0, 0}, //
++      {0x3106, 0x00, 0, 0}, //
++      {0x3210, 0x47, 0, 0}, //
++      {0x3500, 0x00, 0, 0}, // ; exposure HH
++      {0x3501, 0x67, 0, 0}, // ; exposure H
++      {0x3502, 0x80, 0, 0}, // ; exposure L
++      {0x3506, 0x00, 0, 0}, // ; short exposure HH
++      {0x3507, 0x02, 0, 0}, // ; short exposure H
++      {0x3508, 0x00, 0, 0}, // ; shour exposure L
++      {0x3509, 0x10, 0, 0},//00},//8},
++      {0x350a, 0x00, 0, 0}, // ; gain H
++      {0x350b, 0x10, 0, 0}, // ; gain L
++      {0x350e, 0x00, 0, 0}, // ; short gain H
++      {0x350f, 0x10, 0, 0}, // ; short gain L
++      {0x3600, 0x40, 0, 0}, // ; analog control
++      {0x3601, 0xfc, 0, 0}, // ; analog control
++      {0x3602, 0x02, 0, 0}, // ; analog control
++      {0x3603, 0x48, 0, 0}, // ; analog control
++      {0x3604, 0xa5, 0, 0}, // ; analog control
++      {0x3605, 0x9f, 0, 0}, // ; analog control
++      {0x3607, 0x00, 0, 0}, // ; analog control
++      {0x360a, 0x40, 0, 0}, // ; analog control
++      {0x360b, 0x91, 0, 0}, // ; analog control
++      {0x360c, 0x49, 0, 0}, // ; analog control
++      {0x360f, 0x8a, 0, 0}, //
++      {0x3611, 0x10, 0, 0}, // ; PLL2
++      //{0x3612, 0x23, 0, 0}, // ; PLL2
++      {0x3612, 0x13, 0, 0}, // ; PLL2
++      //{0x3613, 0x33, 0, 0}, // ; PLL2
++      {0x3613, 0x22, 0, 0}, // ; PLL2
++      //{0xffff, 50, 0, 0},
++      {0x3614, 0x28, 0, 0}, //[7:0] PLL2_DIVP lo
++      {0x3615, 0x08, 0, 0}, //[7:6] Debug mode, [5:4] N_pump clock div, [3:2] P_pump clock div, [1:0] PLL2_DIVP hi
++      {0x3641, 0x02, 0, 0},
++      {0x3660, 0x82, 0, 0},
++      {0x3668, 0x54, 0, 0},
++      {0x3669, 0x40, 0, 0},
++      {0x3667, 0xa0, 0, 0},
++      {0x3702, 0x40, 0, 0},
++      {0x3703, 0x44, 0, 0},
++      {0x3704, 0x2c, 0, 0},
++      {0x3705, 0x24, 0, 0},
++      {0x3706, 0x50, 0, 0},
++      {0x3707, 0x44, 0, 0},
++      {0x3708, 0x3c, 0, 0},
++      {0x3709, 0x1f, 0, 0},
++      {0x370a, 0x26, 0, 0},
++      {0x370b, 0x3c, 0, 0},
++      {0x3720, 0x66, 0, 0},
++      {0x3722, 0x84, 0, 0},
++      {0x3728, 0x40, 0, 0},
++      {0x372a, 0x00, 0, 0},
++      {0x372f, 0x90, 0, 0},
++      {0x3710, 0x28, 0, 0},
++      {0x3716, 0x03, 0, 0},
++      {0x3718, 0x10, 0, 0},
++      {0x3719, 0x08, 0, 0},
++      {0x371c, 0xfc, 0, 0},
++      {0x3760, 0x13, 0, 0},
++      {0x3761, 0x34, 0, 0},
++      {0x3767, 0x24, 0, 0},
++      {0x3768, 0x06, 0, 0},
++      {0x3769, 0x45, 0, 0},
++      {0x376c, 0x23, 0, 0},
++      {0x3d84, 0x00, 0, 0}, // ; OTP program disable
++      {0x3d85, 0x17, 0, 0}, // ; OTP power up load data enable, power load setting enable, software load setting
++      {0x3d8c, 0x73, 0, 0}, // ; OTP start address H
++      {0x3d8d, 0xbf, 0, 0}, // ; OTP start address L
++      {0x3800, 0x00, 0, 0}, // ; H crop start H
++      {0x3801, 0x08, 0, 0}, // ; H crop start L
++      {0x3802, 0x00, 0, 0}, // ; V crop start H
++      {0x3803, 0x04, 0, 0}, // ; V crop start L
++      {0x3804, 0x10, 0, 0}, // ; H crop end H
++      {0x3805, 0x97, 0, 0}, // ; H crop end L
++      {0x3806, 0x0c, 0, 0}, // ; V crop end H
++      {0x3807, 0x4b, 0, 0}, // ; V crop end L
++      {0x3808, 0x08, 0, 0}, // ; H output size H
++      {0x3809, 0x40, 0, 0}, // ; H output size L
++      {0x380a, 0x06, 0, 0}, // ; V output size H
++      {0x380b, 0x20, 0, 0}, // ; V output size L
++      {0x380c, 0x25, 0, 0}, // ; HTS H
++      {0x380d, 0x80, 0, 0}, // ; HTS L
++      {0x380e, 0x06, 0, 0}, // ; VTS H
++      {0x380f, 0x80, 0, 0}, // ; VTS L
++      {0x3810, 0x00, 0, 0}, // ; H win off H
++      {0x3811, 0x04, 0, 0}, // ; H win off L
++      {0x3812, 0x00, 0, 0}, // ; V win off H
++      {0x3813, 0x02, 0, 0}, // ; V win off L
++      {0x3814, 0x31, 0, 0}, // ; H inc
++      {0x3815, 0x31, 0, 0}, // ; V inc
++      {0x3820, 0x02, 0, 0}, // ; V flip off, V bin on
++      {0x3821, 0x05, 0, 0}, // ; H mirror on, H bin on
++      {0x3834, 0x00, 0, 0}, //
++      {0x3835, 0x1c, 0, 0}, // ; cut_en, vts_auto, blk_col_dis
++      {0x3836, 0x08, 0, 0}, //
++      {0x3837, 0x02, 0, 0}, //
++      {0x4000, 0xf1, 0, 0},//c1}, // ; BLC offset trig en, format change trig en, gain trig en, exp trig en, median en
++      {0x4001, 0x00, 0, 0}, // ; BLC
++      {0x400b, 0x0c, 0, 0}, // ; BLC
++      {0x4011, 0x00, 0, 0}, // ; BLC
++      {0x401a, 0x00, 0, 0}, // ; BLC
++      {0x401b, 0x00, 0, 0}, // ; BLC
++      {0x401c, 0x00, 0, 0}, // ; BLC
++      {0x401d, 0x00, 0, 0}, // ; BLC
++      {0x4020, 0x00, 0, 0}, // ; BLC
++      {0x4021, 0xe4, 0, 0}, // ; BLC
++      {0x4022, 0x07, 0, 0}, // ; BLC
++      {0x4023, 0x5f, 0, 0}, // ; BLC
++      {0x4024, 0x08, 0, 0}, // ; BLC
++      {0x4025, 0x44, 0, 0}, // ; BLC
++      {0x4026, 0x08, 0, 0}, // ; BLC
++      {0x4027, 0x47, 0, 0}, // ; BLC
++      {0x4028, 0x00, 0, 0}, // ; BLC
++      {0x4029, 0x02, 0, 0}, // ; BLC
++      {0x402a, 0x04, 0, 0}, // ; BLC
++      {0x402b, 0x08, 0, 0}, // ; BLC
++      {0x402c, 0x02, 0, 0}, // ; BLC
++      {0x402d, 0x02, 0, 0}, // ; BLC
++      {0x402e, 0x0c, 0, 0}, // ; BLC
++      {0x402f, 0x08, 0, 0}, // ; BLC
++      {0x403d, 0x2c, 0, 0}, //
++      {0x403f, 0x7f, 0, 0}, //
++      {0x4500, 0x82, 0, 0}, // ; BLC
++      {0x4501, 0x38, 0, 0}, // ; BLC
++      {0x4601, 0x04, 0, 0}, //
++      {0x4602, 0x22, 0, 0}, //
++      {0x4603, 0x01, 0, 0}, //; VFIFO
++      {0x4837, 0x19, 0, 0}, //; MIPI global timing
++      {0x4d00, 0x04, 0, 0}, // ; temperature monitor
++      {0x4d01, 0x42, 0, 0}, //  ; temperature monitor
++      {0x4d02, 0xd1, 0, 0}, //  ; temperature monitor
++      {0x4d03, 0x90, 0, 0}, //  ; temperature monitor
++      {0x4d04, 0x66, 0, 0}, //  ; temperature monitor
++      {0x4d05, 0x65, 0, 0}, // ; temperature monitor
++      {0x5000, 0x0e, 0, 0}, // ; windowing enable, BPC on, WPC on, Lenc on
++      {0x5001, 0x03, 0, 0}, // ; BLC enable, MWB on
++      {0x5002, 0x07, 0, 0}, //
++      {0x5013, 0x40, 0, 0},
++      {0x501c, 0x00, 0, 0},
++      {0x501d, 0x10, 0, 0},
++      //{0x5057, 0x56, 0, 0},//add
++      {0x5056, 0x08, 0, 0},
++      {0x5058, 0x08, 0, 0},
++      {0x505a, 0x08, 0, 0},
++      {0x5242, 0x00, 0, 0},
++      {0x5243, 0xb8, 0, 0},
++      {0x5244, 0x00, 0, 0},
++      {0x5245, 0xf9, 0, 0},
++      {0x5246, 0x00, 0, 0},
++      {0x5247, 0xf6, 0, 0},
++      {0x5248, 0x00, 0, 0},
++      {0x5249, 0xa6, 0, 0},
++      {0x5300, 0xfc, 0, 0},
++      {0x5301, 0xdf, 0, 0},
++      {0x5302, 0x3f, 0, 0},
++      {0x5303, 0x08, 0, 0},
++      {0x5304, 0x0c, 0, 0},
++      {0x5305, 0x10, 0, 0},
++      {0x5306, 0x20, 0, 0},
++      {0x5307, 0x40, 0, 0},
++      {0x5308, 0x08, 0, 0},
++      {0x5309, 0x08, 0, 0},
++      {0x530a, 0x02, 0, 0},
++      {0x530b, 0x01, 0, 0},
++      {0x530c, 0x01, 0, 0},
++      {0x530d, 0x0c, 0, 0},
++      {0x530e, 0x02, 0, 0},
++      {0x530f, 0x01, 0, 0},
++      {0x5310, 0x01, 0, 0},
++      {0x5400, 0x00, 0, 0},
++      {0x5401, 0x61, 0, 0},
++      {0x5402, 0x00, 0, 0},
++      {0x5403, 0x00, 0, 0},
++      {0x5404, 0x00, 0, 0},
++      {0x5405, 0x40, 0, 0},
++      {0x540c, 0x05, 0, 0},
++      {0x5b00, 0x00, 0, 0},
++      {0x5b01, 0x00, 0, 0},
++      {0x5b02, 0x01, 0, 0},
++      {0x5b03, 0xff, 0, 0},
++      {0x5b04, 0x02, 0, 0},
++      {0x5b05, 0x6c, 0, 0},
++      {0x5b09, 0x02, 0, 0}, //
++      //{0x5e00, 0x00, 0, 0}, // ; test pattern disable
++      //{0x5e00, 0x80, 0, 0}, // ; test pattern enable
++      {0x5e10, 0x1c, 0, 0}, // ; ISP test disable
++
++      //{0x0300, 0x01, 0, 0},// ; PLL
++      //{0x0302, 0x28, 0, 0},// ; PLL
++      //{0xffff,  50, 0, 0},
++      {0x3501, 0x67, 0, 0},// ; Exposure H
++      {0x370a, 0x26, 0, 0},//
++      {0x372a, 0x00, 0, 0},
++      {0x372f, 0x90, 0, 0},
++      {0x3801, 0x08, 0, 0}, //; H crop start L
++      {0x3803, 0x04, 0, 0}, //; V crop start L
++      {0x3805, 0x97, 0, 0}, //; H crop end L
++      {0x3807, 0x4b, 0, 0}, //; V crop end L
++      {0x3808, 0x08, 0, 0}, //; H output size H
++      {0x3809, 0x40, 0, 0}, //; H output size L
++      {0x380a, 0x06, 0, 0}, //; V output size H
++      {0x380b, 0x20, 0, 0}, //; V output size L
++      {0x380c, 0x25, 0, 0}, //; HTS H
++      {0x380d, 0x80, 0, 0}, //; HTS L
++      {0x380e, 0x0a, 0, 0},//6}, //; VTS H
++      {0x380f, 0x80, 0, 0}, //; VTS L
++      {0x3813, 0x02, 0, 0}, //; V win off
++      {0x3814, 0x31, 0, 0}, //; H inc
++      {0x3815, 0x31, 0, 0}, //; V inc
++      {0x3820, 0x02, 0, 0}, //; V flip off, V bin on
++      {0x3821, 0x05, 0, 0}, //; H mirror on, H bin on
++      {0x3836, 0x08, 0, 0}, //
++      {0x3837, 0x02, 0, 0}, //
++      {0x4020, 0x00, 0, 0}, //
++      {0x4021, 0xe4, 0, 0}, //
++      {0x4022, 0x07, 0, 0}, //
++      {0x4023, 0x5f, 0, 0}, //
++      {0x4024, 0x08, 0, 0}, //
++      {0x4025, 0x44, 0, 0}, //
++      {0x4026, 0x08, 0, 0}, //
++      {0x4027, 0x47, 0, 0}, //
++      {0x4603, 0x01, 0, 0}, //; VFIFO
++      {0x4837, 0x19, 0, 0}, //; MIPI global timing
++      {0x4802, 0x42, 0, 0},  //default 0x00
++      {0x481a, 0x00, 0, 0},
++      {0x481b, 0x1c, 0, 0},   //default 0x3c  prepare
++      {0x4826, 0x12, 0, 0},   //default 0x32  trail
++      {0x5401, 0x61, 0, 0}, //
++      {0x5405, 0x40, 0, 0}, //
++
++      //{0xffff, 200, 0, 0},
++      //{0xffff, 200, 0, 0},
++      //{0xffff, 200, 0, 0},
++
++      //{0x0100, 0x01, 0, 0}, //; wake up, streaming
++};
++
++/* power-on sensor init reg table */
++static const struct ov13850_mode_info ov13850_mode_init_data = {
++      OV13850_MODE_1080P_1920_1080, SCALING,
++      1920, 0x6e0, 1080, 0x470,
++      ov13850_init_setting_30fps_1080P,
++      ARRAY_SIZE(ov13850_init_setting_30fps_1080P),
++      OV13850_30_FPS,
++};
++
++static const struct ov13850_mode_info
++ov13850_mode_data[OV13850_NUM_MODES] = {
++      {OV13850_MODE_1080P_1920_1080, SCALING,
++      1920, 0x6e0, 1080, 0x470,
++      ov13850_setting_1080P_1920_1080,
++      ARRAY_SIZE(ov13850_setting_1080P_1920_1080),
++      OV13850_30_FPS},
++};
++
++static int ov13850_write_reg(struct ov13850_dev *sensor, u16 reg, u8 val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg;
++      u8 buf[3];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++      buf[2] = val;
++
++      msg.addr = client->addr;
++      msg.flags = client->flags;
++      msg.buf = buf;
++      msg.len = sizeof(buf);
++
++      ret = i2c_transfer(client->adapter, &msg, 1);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++                      __func__, reg, val);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int ov13850_read_reg(struct ov13850_dev *sensor, u16 reg, u8 *val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg[2];
++      u8 buf[2];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++
++      msg[0].addr = client->addr;
++      msg[0].flags = client->flags;
++      msg[0].buf = buf;
++      msg[0].len = sizeof(buf);
++
++      msg[1].addr = client->addr;
++      msg[1].flags = client->flags | I2C_M_RD;
++      msg[1].buf = buf;
++      msg[1].len = 1;
++
++      ret = i2c_transfer(client->adapter, msg, 2);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x\n",
++                      __func__, reg);
++              return ret;
++      }
++
++      *val = buf[0];
++      return 0;
++}
++
++static int ov13850_read_reg16(struct ov13850_dev *sensor, u16 reg, u16 *val)
++{
++      u8 hi, lo;
++      int ret;
++
++      ret = ov13850_read_reg(sensor, reg, &hi);
++      if (ret)
++              return ret;
++      ret = ov13850_read_reg(sensor, reg + 1, &lo);
++      if (ret)
++              return ret;
++
++      *val = ((u16)hi << 8) | (u16)lo;
++      return 0;
++}
++
++static int ov13850_write_reg16(struct ov13850_dev *sensor, u16 reg, u16 val)
++{
++      int ret;
++
++      ret = ov13850_write_reg(sensor, reg, val >> 8);
++      if (ret)
++              return ret;
++
++      return ov13850_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int ov13850_mod_reg(struct ov13850_dev *sensor, u16 reg,
++                      u8 mask, u8 val)
++{
++      u8 readval;
++      int ret;
++
++      ret = ov13850_read_reg(sensor, reg, &readval);
++      if (ret)
++              return ret;
++
++      readval &= ~mask;
++      val &= mask;
++      val |= readval;
++
++      return ov13850_write_reg(sensor, reg, val);
++}
++
++static int ov13850_set_timings(struct ov13850_dev *sensor,
++                      const struct ov13850_mode_info *mode)
++{
++      int ret;
++
++      ret = ov13850_write_reg16(sensor, OV13850_REG_H_OUTPUT_SIZE, mode->hact);
++      if (ret < 0)
++              return ret;
++
++      ret = ov13850_write_reg16(sensor, OV13850_REG_V_OUTPUT_SIZE, mode->vact);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int ov13850_load_regs(struct ov13850_dev *sensor,
++                      const struct ov13850_mode_info *mode)
++{
++      const struct reg_value *regs = mode->reg_data;
++      unsigned int i;
++      u32 delay_ms;
++      u16 reg_addr;
++      u8 mask, val;
++      int ret = 0;
++
++      st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id);
++      for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++              delay_ms = regs->delay_ms;
++              reg_addr = regs->reg_addr;
++              val = regs->val;
++              mask = regs->mask;
++
++              if (mask)
++                      ret = ov13850_mod_reg(sensor, reg_addr, mask, val);
++              else
++                      ret = ov13850_write_reg(sensor, reg_addr, val);
++              if (ret)
++                      break;
++
++              if (delay_ms)
++                      usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++      }
++
++      return ov13850_set_timings(sensor, mode);
++}
++
++
++
++static int ov13850_get_gain(struct ov13850_dev *sensor)
++{
++      u32 gain = 0;
++      return gain;
++}
++
++static int ov13850_set_gain(struct ov13850_dev *sensor, int gain)
++{
++      return 0;
++}
++
++static int ov13850_set_stream_mipi(struct ov13850_dev *sensor, bool on)
++{
++      return 0;
++}
++
++static int ov13850_get_sysclk(struct ov13850_dev *sensor)
++{
++      return 0;
++}
++
++static int ov13850_set_night_mode(struct ov13850_dev *sensor)
++{
++      return 0;
++}
++
++static int ov13850_get_hts(struct ov13850_dev *sensor)
++{
++      /* read HTS from register settings */
++      u16 hts;
++      int ret;
++
++      ret = ov13850_read_reg16(sensor, OV13850_REG_TIMING_HTS, &hts);
++      if (ret)
++              return ret;
++      return hts;
++}
++
++static int ov13850_set_hts(struct ov13850_dev *sensor, int hts)
++{
++      return ov13850_write_reg16(sensor, OV13850_REG_TIMING_HTS, hts);
++}
++
++
++static int ov13850_get_vts(struct ov13850_dev *sensor)
++{
++      u16 vts;
++      int ret;
++
++      ret = ov13850_read_reg16(sensor, OV13850_REG_TIMING_VTS, &vts);
++      if (ret)
++              return ret;
++      return vts;
++}
++
++static int ov13850_set_vts(struct ov13850_dev *sensor, int vts)
++{
++      return ov13850_write_reg16(sensor, OV13850_REG_TIMING_VTS, vts);
++}
++
++static int ov13850_get_light_freq(struct ov13850_dev *sensor)
++{
++      return 0;
++}
++
++static int ov13850_set_bandingfilter(struct ov13850_dev *sensor)
++{
++      return 0;
++}
++
++static int ov13850_set_ae_target(struct ov13850_dev *sensor, int target)
++{
++      return 0;
++}
++
++static int ov13850_get_binning(struct ov13850_dev *sensor)
++{
++      return 0;
++}
++
++static int ov13850_set_binning(struct ov13850_dev *sensor, bool enable)
++{
++      return 0;
++}
++
++static const struct ov13850_mode_info *
++ov13850_find_mode(struct ov13850_dev *sensor, enum ov13850_frame_rate fr,
++              int width, int height, bool nearest)
++{
++      const struct ov13850_mode_info *mode;
++
++      mode = v4l2_find_nearest_size(ov13850_mode_data,
++                              ARRAY_SIZE(ov13850_mode_data),
++                              hact, vact,
++                              width, height);
++
++      if (!mode ||
++              (!nearest && (mode->hact != width || mode->vact != height)))
++              return NULL;
++
++      /* Check to see if the current mode exceeds the max frame rate */
++      if (ov13850_framerates[fr] > ov13850_framerates[mode->max_fps])
++              return NULL;
++
++      return mode;
++}
++
++static u64 ov13850_calc_pixel_rate(struct ov13850_dev *sensor)
++{
++      u64 rate;
++
++      rate = sensor->current_mode->vact * sensor->current_mode->hact;
++      rate *= ov13850_framerates[sensor->current_fr];
++
++      return rate;
++}
++
++/*
++ * After trying the various combinations, reading various
++ * documentations spread around the net, and from the various
++ * feedback, the clock tree is probably as follows:
++ *
++ *   +--------------+
++ *   |  Ext. Clock  |
++ *   +-+------------+
++ *     |  +----------+
++ *     +->|   PLL1   | - reg 0x030a, bit0 for the pre-dividerp
++ *        +-+--------+ - reg 0x0300, bits 0-2 for the pre-divider
++ *        +-+--------+ - reg 0x0301~0x0302, for the multiplier
++ *          |  +--------------+
++ *          +->| MIPI Divider |  - reg 0x0303, bits 0-3 for the pre-divider
++ *               | +---------> MIPI PHY CLK
++ *               |    +-----+
++ *               | +->| PLL1_DIV_MIPI | - reg 0x0304, bits 0-1 for the divider
++ *                 |    +----------------> PCLK
++ *               |    +-----+
++ *
++ *   +--------------+
++ *   |  Ext. Clock  |
++ *   +-+------------+
++ *     |  +----------+
++ *     +->|   PLL2  | - reg 0x0311, bit0 for the pre-dividerp
++ *        +-+--------+ - reg 0x030b, bits 0-2 for the pre-divider
++ *        +-+--------+ - reg 0x030c~0x030d, for the multiplier
++ *          |  +--------------+
++ *          +->| SCLK Divider |  - reg 0x030F, bits 0-3 for the pre-divider
++ *               +-+--------+    - reg 0x030E, bits 0-2 for the divider
++ *               |    +---------> SCLK
++ *
++ *          |       +-----+
++ *          +->| DAC Divider | - reg 0x0312, bits 0-3 for the divider
++ *                    |    +----------------> DACCLK
++ **
++ */
++
++/*
++ * ov13850_set_mipi_pclk() - Calculate the clock tree configuration values
++ *                    for the MIPI CSI-2 output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ *    'Bandwidth Per Lane' is calculated as:
++ *    bpl = HTOT * VTOT * FPS * bpp / num_lanes;
++ *
++ * This function use the requested bandwidth to calculate:
++ *
++ * - mipi_pclk   = bpl / 2; ( / 2 is for CSI-2 DDR)
++ * - mipi_phy_clk   = mipi_pclk * PLL1_DIV_MIPI;
++ *
++ * with these fixed parameters:
++ *    PLL1_PREDIVP    = 1;
++ *    PLL1_PREDIV     = 1; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
++ *    PLL1_DIVM       = 1;
++ *    PLL1_DIV_MIPI   = 4;
++ *
++ * FIXME: this have been tested with 10-bit raw and 2 lanes setup only.
++ * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
++ * above formula for setups with 1 lane or image formats with different bpp.
++ *
++ * FIXME: this deviates from the sensor manual documentation which is quite
++ * thin on the MIPI clock tree generation part.
++ */
++
++
++
++static int ov13850_set_mipi_pclk(struct ov13850_dev *sensor,
++                              unsigned long rate)
++{
++
++      return 0;
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int ov13850_set_mode_direct(struct ov13850_dev *sensor,
++                              const struct ov13850_mode_info *mode)
++{
++      if (!mode->reg_data)
++              return -EINVAL;
++
++      /* Write capture setting */
++      return ov13850_load_regs(sensor, mode);
++}
++
++static int ov13850_set_mode(struct ov13850_dev *sensor)
++{
++      const struct ov13850_mode_info *mode = sensor->current_mode;
++      const struct ov13850_mode_info *orig_mode = sensor->last_mode;
++      int ret = 0;
++
++      ret = ov13850_set_mode_direct(sensor, mode);
++      if (ret < 0)
++              return ret;
++
++      /*
++       * we support have 10 bits raw RGB(mipi)
++       */
++      if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++              ret = ov13850_set_mipi_pclk(sensor, 0);
++
++      if (ret < 0)
++              return 0;
++
++      sensor->pending_mode_change = false;
++      sensor->last_mode = mode;
++      return 0;
++}
++
++static int ov13850_set_framefmt(struct ov13850_dev *sensor,
++                             struct v4l2_mbus_framefmt *format);
++
++/* restore the last set video mode after chip power-on */
++static int ov13850_restore_mode(struct ov13850_dev *sensor)
++{
++      int ret;
++
++      /* first load the initial register values */
++      ret = ov13850_load_regs(sensor, &ov13850_mode_init_data);
++      if (ret < 0)
++              return ret;
++      sensor->last_mode = &ov13850_mode_init_data;
++
++      /* now restore the last capture mode */
++      ret = ov13850_set_mode(sensor);
++      if (ret < 0)
++              return ret;
++
++      return ov13850_set_framefmt(sensor, &sensor->fmt);
++}
++
++static void ov13850_power(struct ov13850_dev *sensor, bool enable)
++{
++      if (!sensor->pwdn_gpio)
++              return;
++      if (enable) {
++              gpiod_set_value_cansleep(sensor->pwdn_gpio, 0);
++              gpiod_set_value_cansleep(sensor->pwdn_gpio, 1);
++      } else {
++              gpiod_set_value_cansleep(sensor->pwdn_gpio, 0);
++      }
++
++      mdelay(100);
++}
++
++static void ov13850_reset(struct ov13850_dev *sensor)
++{
++      if (!sensor->reset_gpio)
++              return;
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++      gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++      mdelay(100);
++}
++
++static int ov13850_set_power_on(struct ov13850_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      int ret;
++
++      ret = clk_prepare_enable(sensor->xclk);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable clock\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = regulator_bulk_enable(OV13850_NUM_SUPPLIES,
++                              sensor->supplies);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable regulators\n",
++                      __func__);
++              goto xclk_off;
++      }
++
++      ov13850_reset(sensor);
++      ov13850_power(sensor, true);
++
++      return 0;
++
++xclk_off:
++      clk_disable_unprepare(sensor->xclk);
++      return ret;
++}
++
++static void ov13850_set_power_off(struct ov13850_dev *sensor)
++{
++      ov13850_power(sensor, false);
++      regulator_bulk_disable(OV13850_NUM_SUPPLIES, sensor->supplies);
++      clk_disable_unprepare(sensor->xclk);
++}
++
++static int ov13850_set_power_mipi(struct ov13850_dev *sensor, bool on)
++{
++      return 0;
++}
++
++static int ov13850_set_power(struct ov13850_dev *sensor, bool on)
++{
++      int ret = 0;
++      u16 chip_id;
++
++      if (on) {
++              ret = ov13850_set_power_on(sensor);
++              if (ret)
++                      return ret;
++
++#ifdef UNUSED_CODE
++              ret = ov13850_read_reg16(sensor, OV13850_REG_CHIP_ID, &chip_id);
++              if (ret) {
++                      dev_err(&sensor->i2c_client->dev, "%s: failed to read chip identifier\n",
++                              __func__);
++                      ret = -ENODEV;
++                      goto power_off;
++              }
++
++              if (chip_id != OV13850_CHIP_ID) {
++                      dev_err(&sensor->i2c_client->dev,
++                                      "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++                                      __func__, OV13850_CHIP_ID, chip_id);
++                      ret = -ENXIO;
++                      goto power_off;
++              }
++              dev_err(&sensor->i2c_client->dev, "%s: chip identifier, got 0x%x\n",
++                      __func__, chip_id);
++#endif
++
++              ret = ov13850_restore_mode(sensor);
++              if (ret)
++                      goto power_off;
++      }
++
++      if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++              ret = ov13850_set_power_mipi(sensor, on);
++      if (ret)
++              goto power_off;
++
++      if (!on)
++              ov13850_set_power_off(sensor);
++
++      return 0;
++
++power_off:
++      ov13850_set_power_off(sensor);
++      return ret;
++}
++
++static int ov13850_s_power(struct v4l2_subdev *sd, int on)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      int ret = 0;
++
++      mutex_lock(&sensor->lock);
++
++      /*
++       * If the power count is modified from 0 to != 0 or from != 0 to 0,
++       * update the power state.
++       */
++      if (sensor->power_count == !on) {
++              ret = ov13850_set_power(sensor, !!on);
++              if (ret)
++                      goto out;
++      }
++
++      /* Update the power count. */
++      sensor->power_count += on ? 1 : -1;
++      WARN_ON(sensor->power_count < 0);
++out:
++      mutex_unlock(&sensor->lock);
++
++      if (on && !ret && sensor->power_count == 1) {
++              /* restore controls */
++              ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++      }
++
++      return ret;
++}
++
++static int ov13850_try_frame_interval(struct ov13850_dev *sensor,
++                              struct v4l2_fract *fi,
++                              u32 width, u32 height)
++{
++      const struct ov13850_mode_info *mode;
++      enum ov13850_frame_rate rate = OV13850_15_FPS;
++      int minfps, maxfps, best_fps, fps;
++      int i;
++
++      minfps = ov13850_framerates[OV13850_15_FPS];
++      maxfps = ov13850_framerates[OV13850_NUM_FRAMERATES - 1];
++
++      if (fi->numerator == 0) {
++              fi->denominator = maxfps;
++              fi->numerator = 1;
++              rate = OV13850_60_FPS;
++              goto find_mode;
++      }
++
++      fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++                      minfps, maxfps);
++
++      best_fps = minfps;
++      for (i = 0; i < ARRAY_SIZE(ov13850_framerates); i++) {
++              int curr_fps = ov13850_framerates[i];
++
++              if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++                      best_fps = curr_fps;
++                      rate = i;
++              }
++      }
++      st_info(ST_SENSOR, "best_fps = %d, fps = %d\n", best_fps, fps);
++
++      fi->numerator = 1;
++      fi->denominator = best_fps;
++
++find_mode:
++      mode = ov13850_find_mode(sensor, rate, width, height, false);
++      return mode ? rate : -EINVAL;
++}
++
++static int ov13850_enum_mbus_code(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_mbus_code_enum *code)
++{
++      if (code->pad != 0)
++              return -EINVAL;
++
++      if (code->index >= ARRAY_SIZE(ov13850_formats))
++              return -EINVAL;
++
++      code->code = ov13850_formats[code->index].code;
++      return 0;
++}
++
++static int ov13850_get_fmt(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *format)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      struct v4l2_mbus_framefmt *fmt;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++                                              format->pad);
++      else
++              fmt = &sensor->fmt;
++
++      format->format = *fmt;
++
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int ov13850_try_fmt_internal(struct v4l2_subdev *sd,
++                              struct v4l2_mbus_framefmt *fmt,
++                              enum ov13850_frame_rate fr,
++                              const struct ov13850_mode_info **new_mode)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      const struct ov13850_mode_info *mode;
++      int i;
++
++      mode = ov13850_find_mode(sensor, fr, fmt->width, fmt->height, true);
++      if (!mode)
++              return -EINVAL;
++      fmt->width = mode->hact;
++      fmt->height = mode->vact;
++
++      if (new_mode)
++              *new_mode = mode;
++
++      for (i = 0; i < ARRAY_SIZE(ov13850_formats); i++)
++              if (ov13850_formats[i].code == fmt->code)
++                      break;
++      if (i >= ARRAY_SIZE(ov13850_formats))
++              i = 0;
++
++      fmt->code = ov13850_formats[i].code;
++      fmt->colorspace = ov13850_formats[i].colorspace;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++
++      return 0;
++}
++
++static int ov13850_set_fmt(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *format)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      const struct ov13850_mode_info *new_mode;
++      struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++      struct v4l2_mbus_framefmt *fmt;
++      int ret;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      ret = ov13850_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++      if (ret)
++              goto out;
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(sd, state, 0);
++      else
++              fmt = &sensor->fmt;
++
++      if (mbus_fmt->code != sensor->fmt.code)
++              sensor->pending_fmt_change = true;
++
++      *fmt = *mbus_fmt;
++
++      if (new_mode != sensor->current_mode) {
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++
++      if (new_mode->max_fps < sensor->current_fr) {
++              sensor->current_fr = new_mode->max_fps;
++              sensor->frame_interval.numerator = 1;
++              sensor->frame_interval.denominator =
++                      ov13850_framerates[sensor->current_fr];
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++
++      __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                              ov13850_calc_pixel_rate(sensor));
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int ov13850_set_framefmt(struct ov13850_dev *sensor,
++                             struct v4l2_mbus_framefmt *format)
++{
++      u8 fmt;
++
++      switch (format->code) {
++      /* Raw, BGBG... / GRGR... */
++      case MEDIA_BUS_FMT_SBGGR8_1X8:
++      case MEDIA_BUS_FMT_SGBRG8_1X8:
++      case MEDIA_BUS_FMT_SGRBG8_1X8:
++      case MEDIA_BUS_FMT_SRGGB8_1X8:
++              fmt = 0x0;
++              break;
++      case MEDIA_BUS_FMT_SBGGR10_1X10:
++      case MEDIA_BUS_FMT_SGBRG10_1X10:
++      case MEDIA_BUS_FMT_SGRBG10_1X10:
++      case MEDIA_BUS_FMT_SRGGB10_1X10:
++              fmt = 0x1;
++      case MEDIA_BUS_FMT_SBGGR12_1X12:
++      case MEDIA_BUS_FMT_SGBRG12_1X12:
++      case MEDIA_BUS_FMT_SGRBG12_1X12:
++      case MEDIA_BUS_FMT_SRGGB12_1X12:
++              fmt = 0x2;
++      default:
++              return -EINVAL;
++      }
++
++      return ov13850_mod_reg(sensor, OV13850_REG_MIPI_SC,
++                      BIT(1) | BIT(0), fmt);
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int ov13850_set_ctrl_hue(struct ov13850_dev *sensor, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int ov13850_set_ctrl_contrast(struct ov13850_dev *sensor, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int ov13850_set_ctrl_saturation(struct ov13850_dev *sensor, int value)
++{
++      int ret  = 0;
++
++      return ret;
++}
++
++static int ov13850_set_ctrl_white_balance(struct ov13850_dev *sensor, int awb)
++{
++      struct ov13850_ctrls *ctrls = &sensor->ctrls;
++      int ret = 0;
++
++      return ret;
++}
++
++static int ov13850_set_ctrl_exposure(struct ov13850_dev *sensor,
++                              enum v4l2_exposure_auto_type auto_exposure)
++{
++      struct ov13850_ctrls *ctrls = &sensor->ctrls;
++      int ret = 0;
++
++      return ret;
++}
++
++static const s64 link_freq_menu_items[] = {
++      OV13850_LINK_FREQ_500MHZ
++};
++
++static const char * const test_pattern_menu[] = {
++      "Disabled",
++      "Color bars",
++      "Color bars w/ rolling bar",
++      "Color squares",
++      "Color squares w/ rolling bar",
++};
++
++static int ov13850_set_ctrl_test_pattern(struct ov13850_dev *sensor, int value)
++{
++      return 0;
++}
++
++static int ov13850_set_ctrl_light_freq(struct ov13850_dev *sensor, int value)
++{
++      return 0;
++}
++
++static int ov13850_set_ctrl_hflip(struct ov13850_dev *sensor, int value)
++{
++      return 0;
++}
++
++static int ov13850_set_ctrl_vflip(struct ov13850_dev *sensor, int value)
++{
++      return 0;
++}
++
++static int ov13850_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      int val;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              val = ov13850_get_gain(sensor);
++              break;
++      }
++
++      return 0;
++}
++
++static int ov13850_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      int ret;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      /*
++       * If the device is not powered up by the host driver do
++       * not apply any controls to H/W at this time. Instead
++       * the controls will be restored right after power-up.
++       */
++      if (sensor->power_count == 0)
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = ov13850_set_gain(sensor, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE:
++              ret = ov13850_set_ctrl_exposure(sensor, V4L2_EXPOSURE_MANUAL);
++              break;
++      case V4L2_CID_AUTO_WHITE_BALANCE:
++              ret = ov13850_set_ctrl_white_balance(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HUE:
++              ret = ov13850_set_ctrl_hue(sensor, ctrl->val);
++              break;
++      case V4L2_CID_CONTRAST:
++              ret = ov13850_set_ctrl_contrast(sensor, ctrl->val);
++              break;
++      case V4L2_CID_SATURATION:
++              ret = ov13850_set_ctrl_saturation(sensor, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = ov13850_set_ctrl_test_pattern(sensor, ctrl->val);
++              break;
++      case V4L2_CID_POWER_LINE_FREQUENCY:
++              ret = ov13850_set_ctrl_light_freq(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HFLIP:
++              ret = ov13850_set_ctrl_hflip(sensor, ctrl->val);
++              break;
++      case V4L2_CID_VFLIP:
++              ret = ov13850_set_ctrl_vflip(sensor, ctrl->val);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops ov13850_ctrl_ops = {
++      .g_volatile_ctrl = ov13850_g_volatile_ctrl,
++      .s_ctrl = ov13850_s_ctrl,
++};
++
++static int ov13850_init_controls(struct ov13850_dev *sensor)
++{
++      const struct v4l2_ctrl_ops *ops = &ov13850_ctrl_ops;
++      struct ov13850_ctrls *ctrls = &sensor->ctrls;
++      struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++      int ret;
++
++      v4l2_ctrl_handler_init(hdl, 32);
++
++      /* we can use our own mutex for the ctrl lock */
++      hdl->lock = &sensor->lock;
++
++      /* Clock related controls */
++      ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++                                      0, INT_MAX, 1,
++                                      ov13850_calc_pixel_rate(sensor));
++
++      /* Auto/manual white balance */
++      ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++                                      V4L2_CID_AUTO_WHITE_BALANCE,
++                                      0, 1, 1, 0);
++      ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++                                              0, 4095, 1, 1024);
++      ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++                                              0, 4095, 1, 1024);
++
++      ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++                                      4, 0xfff8, 1, 0x4c00);
++      ctrls->anal_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
++                                      0x10, 0xfff8, 1, 0x0080);
++      ctrls->test_pattern =
++              v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++                                      ARRAY_SIZE(test_pattern_menu) - 1,
++                                      0, 0, test_pattern_menu);
++      ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++                                      0, 1, 1, 0);
++      ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++                                      0, 1, 1, 0);
++      ctrls->light_freq =
++              v4l2_ctrl_new_std_menu(hdl, ops,
++                                      V4L2_CID_POWER_LINE_FREQUENCY,
++                                      V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++                                      V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++      ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
++                                      0, 0, link_freq_menu_items);
++      if (hdl->error) {
++              ret = hdl->error;
++              goto free_ctrls;
++      }
++
++      ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      // ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++      // ctrls->anal_gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++      v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++
++      sensor->sd.ctrl_handler = hdl;
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(hdl);
++      return ret;
++}
++
++static int ov13850_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      if (fse->pad != 0)
++              return -EINVAL;
++      if (fse->index >= OV13850_NUM_MODES)
++              return -EINVAL;
++
++      fse->min_width =
++              ov13850_mode_data[fse->index].hact;
++      fse->max_width = fse->min_width;
++      fse->min_height =
++              ov13850_mode_data[fse->index].vact;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static int ov13850_enum_frame_interval(
++      struct v4l2_subdev *sd,
++      struct v4l2_subdev_state *state,
++      struct v4l2_subdev_frame_interval_enum *fie)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      struct v4l2_fract tpf;
++      int ret;
++
++      if (fie->pad != 0)
++              return -EINVAL;
++      if (fie->index >= OV13850_NUM_FRAMERATES)
++              return -EINVAL;
++
++      tpf.numerator = 1;
++      tpf.denominator = ov13850_framerates[fie->index];
++
++/*    ret = ov13850_try_frame_interval(sensor, &tpf,
++ *                                    fie->width, fie->height);
++ *    if (ret < 0)
++ *            return -EINVAL;
++ */
++      fie->interval = tpf;
++
++      return 0;
++}
++
++static int ov13850_g_frame_interval(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_frame_interval *fi)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++
++      mutex_lock(&sensor->lock);
++      fi->interval = sensor->frame_interval;
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int ov13850_s_frame_interval(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_frame_interval *fi)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      const struct ov13850_mode_info *mode;
++      int frame_rate, ret = 0;
++
++      if (fi->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      mode = sensor->current_mode;
++
++      frame_rate = ov13850_try_frame_interval(sensor, &fi->interval,
++                                      mode->hact, mode->vact);
++      if (frame_rate < 0) {
++              /* Always return a valid frame interval value */
++              fi->interval = sensor->frame_interval;
++              goto out;
++      }
++
++      mode = ov13850_find_mode(sensor, frame_rate, mode->hact,
++                              mode->vact, true);
++      if (!mode) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      if (mode != sensor->current_mode ||
++              frame_rate != sensor->current_fr) {
++              sensor->current_fr = frame_rate;
++              sensor->frame_interval = fi->interval;
++              sensor->current_mode = mode;
++              sensor->pending_mode_change = true;
++
++              __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                                      ov13850_calc_pixel_rate(sensor));
++      }
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int ov13850_stream_start(struct ov13850_dev *sensor, int enable)
++{
++      int ret;
++
++      if (enable) {           //stream on
++              mdelay(1000);
++              ret = ov13850_write_reg(sensor, OV13850_STREAM_CTRL, enable);
++      } else {                        //stream off
++              ret = ov13850_write_reg(sensor, OV13850_STREAM_CTRL, enable);
++              mdelay(100);
++      }
++
++      return ret;
++}
++
++static int ov13850_s_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++      int ret = 0;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming == !enable) {
++              if (enable && sensor->pending_mode_change) {
++                      ret = ov13850_set_mode(sensor);
++                      if (ret)
++                              goto out;
++              }
++
++              if (enable && sensor->pending_fmt_change) {
++                      ret = ov13850_set_framefmt(sensor, &sensor->fmt);
++                      if (ret)
++                              goto out;
++                      sensor->pending_fmt_change = false;
++              }
++
++              if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++                      ret = ov13850_set_stream_mipi(sensor, enable);
++
++              ret = ov13850_stream_start(sensor, enable);
++
++              if (!ret)
++                      sensor->streaming = enable;
++      }
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static const struct v4l2_subdev_core_ops ov13850_core_ops = {
++      .s_power = ov13850_s_power,
++      .log_status = v4l2_ctrl_subdev_log_status,
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops ov13850_video_ops = {
++      .g_frame_interval = ov13850_g_frame_interval,
++      .s_frame_interval = ov13850_s_frame_interval,
++      .s_stream = ov13850_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov13850_pad_ops = {
++      .enum_mbus_code = ov13850_enum_mbus_code,
++      .get_fmt = ov13850_get_fmt,
++      .set_fmt = ov13850_set_fmt,
++      .enum_frame_size = ov13850_enum_frame_size,
++      .enum_frame_interval = ov13850_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops ov13850_subdev_ops = {
++      .core = &ov13850_core_ops,
++      .video = &ov13850_video_ops,
++      .pad = &ov13850_pad_ops,
++};
++
++static int ov13850_get_regulators(struct ov13850_dev *sensor)
++{
++      int i;
++
++      for (i = 0; i < OV13850_NUM_SUPPLIES; i++)
++              sensor->supplies[i].supply = ov13850_supply_name[i];
++
++      return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++                                      OV13850_NUM_SUPPLIES,
++                                      sensor->supplies);
++}
++
++static int ov13850_check_chip_id(struct ov13850_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      int ret = 0;
++      u16 chip_id;
++
++      ret = ov13850_set_power_on(sensor);
++      if (ret)
++              return ret;
++
++#ifdef UNUSED_CODE
++      ret = ov13850_read_reg16(sensor, OV13850_REG_CHIP_ID, &chip_id);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to read chip identifier\n",
++                      __func__);
++              goto power_off;
++      }
++
++      if (chip_id != OV13850_CHIP_ID) {
++              dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x,  got 0x%x\n",
++                      __func__, OV13850_CHIP_ID, chip_id);
++              ret = -ENXIO;
++      }
++      dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++              __func__, chip_id);
++#endif
++
++power_off:
++      ov13850_set_power_off(sensor);
++      return ret;
++}
++
++static int ov13850_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct fwnode_handle *endpoint;
++      struct ov13850_dev *sensor;
++      struct v4l2_mbus_framefmt *fmt;
++      u32 rotation;
++      int ret;
++      u8 chip_id_high, chip_id_low;
++
++      sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++      if (!sensor)
++              return -ENOMEM;
++
++      sensor->i2c_client = client;
++
++      fmt = &sensor->fmt;
++      fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++      fmt->width = 1920;
++      fmt->height = 1080;
++      fmt->field = V4L2_FIELD_NONE;
++      sensor->frame_interval.numerator = 1;
++      sensor->frame_interval.denominator = ov13850_framerates[OV13850_30_FPS];
++      sensor->current_fr = OV13850_30_FPS;
++      sensor->current_mode =
++              &ov13850_mode_data[OV13850_MODE_1080P_1920_1080];
++      sensor->last_mode = sensor->current_mode;
++
++      sensor->ae_target = 52;
++
++      /* optional indication of physical rotation of sensor */
++      ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++                                      &rotation);
++      if (!ret) {
++              switch (rotation) {
++              case 180:
++                      sensor->upside_down = true;
++                      fallthrough;
++              case 0:
++                      break;
++              default:
++                      dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++                              rotation);
++              }
++      }
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++                                              NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++      fwnode_handle_put(endpoint);
++      if (ret) {
++              dev_err(dev, "Could not parse endpoint\n");
++              return ret;
++      }
++
++      if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
++              sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
++              sensor->ep.bus_type != V4L2_MBUS_BT656) {
++              dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++              return -EINVAL;
++      }
++
++      /* get system clock (xclk) */
++      sensor->xclk = devm_clk_get(dev, "xclk");
++      if (IS_ERR(sensor->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(sensor->xclk);
++      }
++
++      sensor->xclk_freq = clk_get_rate(sensor->xclk);
++      if (sensor->xclk_freq < OV13850_XCLK_MIN ||
++              sensor->xclk_freq > OV13850_XCLK_MAX) {
++              dev_err(dev, "xclk frequency out of range: %d Hz\n",
++                      sensor->xclk_freq);
++              return -EINVAL;
++      }
++
++      /* request optional power down pin */
++      sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++                                              GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->pwdn_gpio))
++              return PTR_ERR(sensor->pwdn_gpio);
++
++      /* request optional reset pin */
++      sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++                                              GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->reset_gpio))
++              return PTR_ERR(sensor->reset_gpio);
++
++      v4l2_i2c_subdev_init(&sensor->sd, client, &ov13850_subdev_ops);
++
++      sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++                      V4L2_SUBDEV_FL_HAS_EVENTS;
++      sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++      sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++      ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++      if (ret)
++              return ret;
++
++      ret = ov13850_get_regulators(sensor);
++      if (ret)
++              return ret;
++
++      mutex_init(&sensor->lock);
++
++      ret = ov13850_check_chip_id(sensor);
++      if (ret)
++              goto entity_cleanup;
++
++      ret = ov13850_init_controls(sensor);
++      if (ret)
++              goto entity_cleanup;
++
++      ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++      if (ret)
++              goto free_ctrls;
++
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++entity_cleanup:
++      media_entity_cleanup(&sensor->sd.entity);
++      mutex_destroy(&sensor->lock);
++      return ret;
++}
++
++static int ov13850_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct ov13850_dev *sensor = to_ov13850_dev(sd);
++
++      v4l2_async_unregister_subdev(&sensor->sd);
++      media_entity_cleanup(&sensor->sd.entity);
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++      mutex_destroy(&sensor->lock);
++
++      return 0;
++}
++
++static const struct i2c_device_id ov13850_id[] = {
++      {"ov13850", 0},
++      {},
++};
++MODULE_DEVICE_TABLE(i2c, ov13850_id);
++
++static const struct of_device_id ov13850_dt_ids[] = {
++      { .compatible = "ovti,ov13850" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov13850_dt_ids);
++
++static struct i2c_driver ov13850_i2c_driver = {
++      .driver = {
++              .name  = "ov13850",
++              .of_match_table = ov13850_dt_ids,
++      },
++      .id_table = ov13850_id,
++      .probe_new = ov13850_probe,
++      .remove   = ov13850_remove,
++};
++
++module_i2c_driver(ov13850_i2c_driver);
++
++MODULE_DESCRIPTION("OV13850 MIPI Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c
+@@ -0,0 +1,2975 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++
++#define OV4689_LANES    4
++
++#define OV4689_LINK_FREQ_500MHZ         500000000LL
++
++/* min/typical/max system clock (xclk) frequencies */
++#define OV4689_XCLK_MIN  6000000
++#define OV4689_XCLK_MAX 64000000
++
++#define OV4689_CHIP_ID        (0x4688)
++
++#define OV4689_CHIP_ID_HIGH_BYTE        0x300a   // max should be 0x46
++#define OV4689_CHIP_ID_LOW_BYTE         0x300b   // max should be 0x88
++#define OV4689_REG_CHIP_ID              0x300a
++
++#define OV4689_REG_H_OUTPUT_SIZE      0x3808
++#define OV4689_REG_V_OUTPUT_SIZE      0x380a
++#define OV4689_REG_TIMING_HTS         0x380c
++#define OV4689_REG_TIMING_VTS         0x380e
++
++#define OV4689_REG_EXPOSURE_HI          0x3500
++#define OV4689_REG_EXPOSURE_MED         0x3501
++#define OV4689_REG_EXPOSURE_LO          0x3502
++#define OV4689_REG_GAIN_H               0x3507
++#define OV4689_REG_GAIN_M               0x3508
++#define OV4689_REG_GAIN_L               0x3509
++#define OV4689_REG_TEST_PATTERN         0x5040
++#define OV4689_REG_TIMING_TC_REG20      0x3820
++#define OV4689_REG_TIMING_TC_REG21      0x3821
++
++#define OV4689_REG_AWB_R_GAIN           0x500C
++#define OV4689_REG_AWB_B_GAIN           0x5010
++#define OV4689_REG_STREAM_ON            0x0100
++
++#define OV4689_REG_MIPI_SC_CTRL_HI    0x3018
++#define OV4689_REG_MIPI_SC_CTRL_LOW   0x3019
++
++enum ov4689_mode_id {
++      //OV4689_MODE_720P_1280_720 = 0,
++      OV4689_MODE_1080P_1920_1080 = 0,
++      //OV4689_MODE_4M_2688_1520,
++      OV4689_NUM_MODES,
++};
++
++enum ov4689_frame_rate {
++      OV4689_15_FPS = 0,
++      OV4689_30_FPS,
++      OV4689_45_FPS,
++      OV4689_60_FPS,
++      OV4689_90_FPS,
++      OV4689_120_FPS,
++      OV4689_150_FPS,
++      OV4689_180_FPS,
++      OV4689_330_FPS,
++      OV4689_NUM_FRAMERATES,
++};
++
++enum ov4689_format_mux {
++      OV4689_FMT_MUX_RAW,
++};
++
++static const int ov4689_framerates[] = {
++      [OV4689_15_FPS] = 15,
++      [OV4689_30_FPS] = 30,
++      [OV4689_45_FPS] = 45,
++      [OV4689_60_FPS] = 60,
++      [OV4689_90_FPS] = 90,
++      [OV4689_120_FPS] = 120,
++      [OV4689_150_FPS] = 150,
++      [OV4689_180_FPS] = 180,
++      [OV4689_330_FPS] = 330,
++};
++
++/* regulator supplies */
++static const char * const ov4689_supply_name[] = {
++      "DOVDD", /* Digital I/O (1.8V) supply */
++      "AVDD",  /* Analog (2.8V) supply */
++      "DVDD",  /* Digital Core (1.5V) supply */
++};
++
++#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_name)
++
++/*
++ * Image size under 1280 * 960 are SUBSAMPLING
++ * Image size upper 1280 * 960 are SCALING
++ */
++enum ov4689_downsize_mode {
++      SUBSAMPLING,
++      SCALING,
++};
++
++struct reg_value {
++      u16 reg_addr;
++      u8 val;
++      u8 mask;
++      u32 delay_ms;
++};
++
++struct ov4689_mode_info {
++      enum ov4689_mode_id id;
++      enum ov4689_downsize_mode dn_mode;
++      u32 hact;
++      u32 htot;
++      u32 vact;
++      u32 vtot;
++      const struct reg_value *reg_data;
++      u32 reg_data_size;
++      u32 max_fps;
++};
++
++struct ov4689_ctrls {
++      struct v4l2_ctrl_handler handler;
++      struct v4l2_ctrl *pixel_rate;
++      struct {
++              struct v4l2_ctrl *exposure;
++      };
++      struct {
++              struct v4l2_ctrl *auto_wb;
++              struct v4l2_ctrl *blue_balance;
++              struct v4l2_ctrl *red_balance;
++      };
++      struct {
++              struct v4l2_ctrl *anal_gain;
++      };
++      struct v4l2_ctrl *brightness;
++      struct v4l2_ctrl *light_freq;
++      struct v4l2_ctrl *link_freq;
++      struct v4l2_ctrl *saturation;
++      struct v4l2_ctrl *contrast;
++      struct v4l2_ctrl *hue;
++      struct v4l2_ctrl *test_pattern;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vflip;
++};
++
++struct ov4689_dev {
++      struct i2c_client *i2c_client;
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++      struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++      struct clk *xclk; /* system clock to OV4689 */
++      u32 xclk_freq;
++
++      struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
++      struct gpio_desc *reset_gpio;
++      struct gpio_desc *pwdn_gpio;
++      bool   upside_down;
++
++      /* lock to protect all members below */
++      struct mutex lock;
++
++      struct v4l2_mbus_framefmt fmt;
++
++      const struct ov4689_mode_info *current_mode;
++      const struct ov4689_mode_info *last_mode;
++      enum ov4689_frame_rate current_fr;
++      struct v4l2_fract frame_interval;
++
++      struct ov4689_ctrls ctrls;
++
++      bool pending_mode_change;
++      int streaming;
++};
++
++static inline struct ov4689_dev *to_ov4689_dev(struct v4l2_subdev *sd)
++{
++      return container_of(sd, struct ov4689_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++      return &container_of(ctrl->handler, struct ov4689_dev,
++                      ctrls.handler)->sd;
++}
++
++/* ov4689 initial register */
++static const struct reg_value ov4689_init_setting_30fps_1080P[] = {
++/* ov4689_1080p_30fps_4d */
++      {0x0103, 0x01, 0, 0},
++      {0x3638, 0x00, 0, 0},
++      {0x0300, 0x02, 0, 0},
++      {0x0302, 0x32, 0, 0},
++      {0x0303, 0x00, 0, 0},
++      {0x0304, 0x03, 0, 0},
++      {0x030b, 0x00, 0, 0},
++      {0x030d, 0x1e, 0, 0},
++      {0x030e, 0x04, 0, 0},
++      {0x030f, 0x01, 0, 0},
++      {0x0312, 0x01, 0, 0},
++      {0x031e, 0x00, 0, 0},
++      {0x3000, 0x20, 0, 0},
++      {0x3002, 0x00, 0, 0},
++      {0x3020, 0x93, 0, 0},
++      {0x3021, 0x03, 0, 0},
++      {0x3022, 0x01, 0, 0},
++      {0x3031, 0x0a, 0, 0},
++      {0x3305, 0xf1, 0, 0},
++      {0x3307, 0x04, 0, 0},
++      {0x3309, 0x29, 0, 0},
++      {0x3500, 0x00, 0, 0},
++      {0x3501, 0x4c, 0, 0},
++      {0x3502, 0x00, 0, 0},
++      {0x3503, 0x04, 0, 0},
++      {0x3504, 0x00, 0, 0},
++      {0x3505, 0x00, 0, 0},
++      {0x3506, 0x00, 0, 0},
++      {0x3507, 0x00, 0, 0},
++      {0x3508, 0x00, 0, 0},
++      {0x3509, 0x80, 0, 0},
++      {0x350a, 0x00, 0, 0},
++      {0x350b, 0x00, 0, 0},
++      {0x350c, 0x00, 0, 0},
++      {0x350d, 0x00, 0, 0},
++      {0x350e, 0x00, 0, 0},
++      {0x350f, 0x80, 0, 0},
++      {0x3510, 0x00, 0, 0},
++      {0x3511, 0x00, 0, 0},
++      {0x3512, 0x00, 0, 0},
++      {0x3513, 0x00, 0, 0},
++      {0x3514, 0x00, 0, 0},
++      {0x3515, 0x80, 0, 0},
++      {0x3516, 0x00, 0, 0},
++      {0x3517, 0x00, 0, 0},
++      {0x3518, 0x00, 0, 0},
++      {0x3519, 0x00, 0, 0},
++      {0x351a, 0x00, 0, 0},
++      {0x351b, 0x80, 0, 0},
++      {0x351c, 0x00, 0, 0},
++      {0x351d, 0x00, 0, 0},
++      {0x351e, 0x00, 0, 0},
++      {0x351f, 0x00, 0, 0},
++      {0x3520, 0x00, 0, 0},
++      {0x3521, 0x80, 0, 0},
++      {0x3522, 0x08, 0, 0},
++      {0x3524, 0x08, 0, 0},
++      {0x3526, 0x08, 0, 0},
++      {0x3528, 0x08, 0, 0},
++      {0x352a, 0x08, 0, 0},
++      {0x3602, 0x00, 0, 0},
++      {0x3603, 0x40, 0, 0},
++      {0x3604, 0x02, 0, 0},
++      {0x3605, 0x00, 0, 0},
++      {0x3606, 0x00, 0, 0},
++      {0x3607, 0x00, 0, 0},
++      {0x3609, 0x12, 0, 0},
++      {0x360a, 0x40, 0, 0},
++      {0x360c, 0x08, 0, 0},
++      {0x360f, 0xe5, 0, 0},
++      {0x3608, 0x8f, 0, 0},
++      {0x3611, 0x00, 0, 0},
++      {0x3613, 0xf7, 0, 0},
++      {0x3616, 0x58, 0, 0},
++      {0x3619, 0x99, 0, 0},
++      {0x361b, 0x60, 0, 0},
++      {0x361c, 0x7a, 0, 0},
++      {0x361e, 0x79, 0, 0},
++      {0x361f, 0x02, 0, 0},
++      {0x3632, 0x00, 0, 0},
++      {0x3633, 0x10, 0, 0},
++      {0x3634, 0x10, 0, 0},
++      {0x3635, 0x10, 0, 0},
++      {0x3636, 0x15, 0, 0},
++      {0x3646, 0x86, 0, 0},
++      {0x364a, 0x0b, 0, 0},
++      {0x3700, 0x17, 0, 0},
++      {0x3701, 0x22, 0, 0},
++      {0x3703, 0x10, 0, 0},
++      {0x370a, 0x37, 0, 0},
++      {0x3705, 0x00, 0, 0},
++      {0x3706, 0x63, 0, 0},
++      {0x3709, 0x3c, 0, 0},
++      {0x370b, 0x01, 0, 0},
++      {0x370c, 0x30, 0, 0},
++      {0x3710, 0x24, 0, 0},
++      {0x3711, 0x0c, 0, 0},
++      {0x3716, 0x00, 0, 0},
++      {0x3720, 0x28, 0, 0},
++      {0x3729, 0x7b, 0, 0},
++      {0x372a, 0x84, 0, 0},
++      {0x372b, 0xbd, 0, 0},
++      {0x372c, 0xbc, 0, 0},
++      {0x372e, 0x52, 0, 0},
++      {0x373c, 0x0e, 0, 0},
++      {0x373e, 0x33, 0, 0},
++      {0x3743, 0x10, 0, 0},
++      {0x3744, 0x88, 0, 0},
++      {0x3745, 0xc0, 0, 0},
++      {0x374a, 0x43, 0, 0},
++      {0x374c, 0x00, 0, 0},
++      {0x374e, 0x23, 0, 0},
++      {0x3751, 0x7b, 0, 0},
++      {0x3752, 0x84, 0, 0},
++      {0x3753, 0xbd, 0, 0},
++      {0x3754, 0xbc, 0, 0},
++      {0x3756, 0x52, 0, 0},
++      {0x375c, 0x00, 0, 0},
++      {0x3760, 0x00, 0, 0},
++      {0x3761, 0x00, 0, 0},
++      {0x3762, 0x00, 0, 0},
++      {0x3763, 0x00, 0, 0},
++      {0x3764, 0x00, 0, 0},
++      {0x3767, 0x04, 0, 0},
++      {0x3768, 0x04, 0, 0},
++      {0x3769, 0x08, 0, 0},
++      {0x376a, 0x08, 0, 0},
++      {0x376b, 0x20, 0, 0},
++      {0x376c, 0x00, 0, 0},
++      {0x376d, 0x00, 0, 0},
++      {0x376e, 0x00, 0, 0},
++      {0x3773, 0x00, 0, 0},
++      {0x3774, 0x51, 0, 0},
++      {0x3776, 0xbd, 0, 0},
++      {0x3777, 0xbd, 0, 0},
++      {0x3781, 0x18, 0, 0},
++      {0x3783, 0x25, 0, 0},
++      {0x3798, 0x1b, 0, 0},
++      {0x3800, 0x01, 0, 0},
++      {0x3801, 0x88, 0, 0},
++      {0x3802, 0x00, 0, 0},
++      {0x3803, 0xe0, 0, 0},
++      {0x3804, 0x09, 0, 0},
++      {0x3805, 0x17, 0, 0},
++      {0x3806, 0x05, 0, 0},
++      {0x3807, 0x1f, 0, 0},
++      {0x3808, 0x07, 0, 0},
++      {0x3809, 0x80, 0, 0},
++      {0x380a, 0x04, 0, 0},
++      {0x380b, 0x38, 0, 0},
++      {0x380c, 0x0d, 0, 0},
++      {0x380d, 0x70, 0, 0},
++      {0x380e, 0x04, 0, 0},
++      {0x380f, 0x8A, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x08, 0, 0},
++      {0x3812, 0x00, 0, 0},
++      {0x3813, 0x04, 0, 0},
++      {0x3814, 0x01, 0, 0},
++      {0x3815, 0x01, 0, 0},
++      {0x3819, 0x01, 0, 0},
++      {0x3820, 0x06, 0, 0},
++      {0x3821, 0x00, 0, 0},
++      {0x3829, 0x00, 0, 0},
++      {0x382a, 0x01, 0, 0},
++      {0x382b, 0x01, 0, 0},
++      {0x382d, 0x7f, 0, 0},
++      {0x3830, 0x04, 0, 0},
++      {0x3836, 0x01, 0, 0},
++      {0x3837, 0x00, 0, 0},
++      {0x3841, 0x02, 0, 0},
++      {0x3846, 0x08, 0, 0},
++      {0x3847, 0x07, 0, 0},
++      {0x3d85, 0x36, 0, 0},
++      {0x3d8c, 0x71, 0, 0},
++      {0x3d8d, 0xcb, 0, 0},
++      {0x3f0a, 0x00, 0, 0},
++      {0x4000, 0xf1, 0, 0},
++      {0x4001, 0x40, 0, 0},
++      {0x4002, 0x04, 0, 0},
++      {0x4003, 0x14, 0, 0},
++      {0x400e, 0x00, 0, 0},
++      {0x4011, 0x00, 0, 0},
++      {0x401a, 0x00, 0, 0},
++      {0x401b, 0x00, 0, 0},
++      {0x401c, 0x00, 0, 0},
++      {0x401d, 0x00, 0, 0},
++      {0x401f, 0x00, 0, 0},
++      {0x4020, 0x00, 0, 0},
++      {0x4021, 0x10, 0, 0},
++      {0x4022, 0x06, 0, 0},
++      {0x4023, 0x13, 0, 0},
++      {0x4024, 0x07, 0, 0},
++      {0x4025, 0x40, 0, 0},
++      {0x4026, 0x07, 0, 0},
++      {0x4027, 0x50, 0, 0},
++      {0x4028, 0x00, 0, 0},
++      {0x4029, 0x02, 0, 0},
++      {0x402a, 0x06, 0, 0},
++      {0x402b, 0x04, 0, 0},
++      {0x402c, 0x02, 0, 0},
++      {0x402d, 0x02, 0, 0},
++      {0x402e, 0x0e, 0, 0},
++      {0x402f, 0x04, 0, 0},
++      {0x4302, 0xff, 0, 0},
++      {0x4303, 0xff, 0, 0},
++      {0x4304, 0x00, 0, 0},
++      {0x4305, 0x00, 0, 0},
++      {0x4306, 0x00, 0, 0},
++      {0x4308, 0x02, 0, 0},
++      {0x4500, 0x6c, 0, 0},
++      {0x4501, 0xc4, 0, 0},
++      {0x4502, 0x40, 0, 0},
++      {0x4503, 0x01, 0, 0},
++      {0x4601, 0x77, 0, 0},
++      {0x4800, 0x04, 0, 0},
++      {0x4813, 0x08, 0, 0},
++      {0x481f, 0x40, 0, 0},
++      {0x4829, 0x78, 0, 0},
++      {0x4837, 0x10, 0, 0},
++      {0x4b00, 0x2a, 0, 0},
++      {0x4b0d, 0x00, 0, 0},
++      {0x4d00, 0x04, 0, 0},
++      {0x4d01, 0x42, 0, 0},
++      {0x4d02, 0xd1, 0, 0},
++      {0x4d03, 0x93, 0, 0},
++      {0x4d04, 0xf5, 0, 0},
++      {0x4d05, 0xc1, 0, 0},
++      {0x5000, 0xf3, 0, 0},
++      {0x5001, 0x11, 0, 0},
++      {0x5004, 0x00, 0, 0},
++      {0x500a, 0x00, 0, 0},
++      {0x500b, 0x00, 0, 0},
++      {0x5032, 0x00, 0, 0},
++      {0x5040, 0x00, 0, 0},
++      {0x5050, 0x0c, 0, 0},
++      {0x5500, 0x00, 0, 0},
++      {0x5501, 0x10, 0, 0},
++      {0x5502, 0x01, 0, 0},
++      {0x5503, 0x0f, 0, 0},
++      {0x8000, 0x00, 0, 0},
++      {0x8001, 0x00, 0, 0},
++      {0x8002, 0x00, 0, 0},
++      {0x8003, 0x00, 0, 0},
++      {0x8004, 0x00, 0, 0},
++      {0x8005, 0x00, 0, 0},
++      {0x8006, 0x00, 0, 0},
++      {0x8007, 0x00, 0, 0},
++      {0x8008, 0x00, 0, 0},
++      {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_VGA_640_480[] = {
++      //@@ RES_640x480_2x_Bin_330fps_816Mbps
++      //OV4689_AM01B_640x480_24M_2lane_816Mbps_330fps_20140210.txt
++      {0x0103, 0x01, 0, 0},
++      {0x3638, 0x00, 0, 0},
++      {0x0300, 0x00, 0, 0}, // 00
++      {0x0302, 0x22, 0, 0}, // 816Mbps 5a ; 64 ; 5a ; 78  ; 78 ; 2a
++      {0x0303, 0x00, 0, 0}, // 03 ; 01 ; 02 ;
++      {0x0304, 0x03, 0, 0},
++      {0x030b, 0x00, 0, 0},
++      {0x030d, 0x1e, 0, 0},
++      {0x030e, 0x04, 0, 0},
++      {0x030f, 0x01, 0, 0},
++      {0x0312, 0x01, 0, 0},
++      {0x031e, 0x00, 0, 0},
++      {0x3000, 0x20, 0, 0},
++      {0x3002, 0x00, 0, 0},
++      {0x3020, 0x93, 0, 0},
++      {0x3021, 0x03, 0, 0},
++      {0x3022, 0x01, 0, 0},
++      {0x3031, 0x0a, 0, 0},
++      {0x303f, 0x0c, 0, 0},
++      {0x3305, 0xf1, 0, 0},
++      {0x3307, 0x04, 0, 0},
++      {0x3309, 0x29, 0, 0},
++      {0x3500, 0x00, 0, 0},
++      {0x3501, 0x4c, 0, 0},
++      {0x3502, 0x00, 0, 0},
++      {0x3503, 0x04, 0, 0},
++      {0x3504, 0x00, 0, 0},
++      {0x3505, 0x00, 0, 0},
++      {0x3506, 0x00, 0, 0},
++      {0x3507, 0x00, 0, 0},
++      {0x3508, 0x00, 0, 0},
++      {0x3509, 0x80, 0, 0}, // 8X
++      {0x350a, 0x00, 0, 0},
++      {0x350b, 0x00, 0, 0},
++      {0x350c, 0x00, 0, 0},
++      {0x350d, 0x00, 0, 0},
++      {0x350e, 0x00, 0, 0},
++      {0x350f, 0x80, 0, 0},
++      {0x3510, 0x00, 0, 0},
++      {0x3511, 0x00, 0, 0},
++      {0x3512, 0x00, 0, 0},
++      {0x3513, 0x00, 0, 0},
++      {0x3514, 0x00, 0, 0},
++      {0x3515, 0x80, 0, 0},
++      {0x3516, 0x00, 0, 0},
++      {0x3517, 0x00, 0, 0},
++      {0x3518, 0x00, 0, 0},
++      {0x3519, 0x00, 0, 0},
++      {0x351a, 0x00, 0, 0},
++      {0x351b, 0x80, 0, 0},
++      {0x351c, 0x00, 0, 0},
++      {0x351d, 0x00, 0, 0},
++      {0x351e, 0x00, 0, 0},
++      {0x351f, 0x00, 0, 0},
++      {0x3520, 0x00, 0, 0},
++      {0x3521, 0x80, 0, 0},
++      {0x3522, 0x08, 0, 0},
++      {0x3524, 0x08, 0, 0},
++      {0x3526, 0x08, 0, 0},
++      {0x3528, 0x08, 0, 0},
++      {0x352a, 0x08, 0, 0},
++      {0x3602, 0x00, 0, 0},
++      {0x3603, 0x40, 0, 0},
++      {0x3604, 0x02, 0, 0},
++      {0x3605, 0x00, 0, 0},
++      {0x3606, 0x00, 0, 0},
++      {0x3607, 0x00, 0, 0},
++      {0x3609, 0x12, 0, 0},
++      {0x360a, 0x40, 0, 0},
++      {0x360c, 0x08, 0, 0},
++      {0x360f, 0xe5, 0, 0},
++      {0x3608, 0x8f, 0, 0},
++      {0x3611, 0x00, 0, 0},
++      {0x3613, 0xf7, 0, 0},
++      {0x3616, 0x58, 0, 0},
++      {0x3619, 0x99, 0, 0},
++      {0x361b, 0x60, 0, 0},
++      {0x361c, 0x7a, 0, 0},
++      {0x361e, 0x79, 0, 0},
++      {0x361f, 0x02, 0, 0},
++      {0x3632, 0x05, 0, 0},
++      {0x3633, 0x10, 0, 0},
++      {0x3634, 0x10, 0, 0},
++      {0x3635, 0x10, 0, 0},
++      {0x3636, 0x15, 0, 0},
++      {0x3646, 0x86, 0, 0},
++      {0x364a, 0x0b, 0, 0},
++      {0x3700, 0x17, 0, 0},
++      {0x3701, 0x22, 0, 0},
++      {0x3703, 0x10, 0, 0},
++      {0x370a, 0x37, 0, 0},
++      {0x3705, 0x00, 0, 0},
++      {0x3706, 0x63, 0, 0},
++      {0x3709, 0x3c, 0, 0},
++      {0x370b, 0x01, 0, 0},
++      {0x370c, 0x30, 0, 0},
++      {0x3710, 0x24, 0, 0},
++      {0x3711, 0x0c, 0, 0},
++      {0x3716, 0x00, 0, 0},
++      {0x3720, 0x28, 0, 0},
++      {0x3729, 0x7b, 0, 0},
++      {0x372a, 0x84, 0, 0},
++      {0x372b, 0xbd, 0, 0},
++      {0x372c, 0xbc, 0, 0},
++      {0x372e, 0x52, 0, 0},
++      {0x373c, 0x0e, 0, 0},
++      {0x373e, 0x33, 0, 0},
++      {0x3743, 0x10, 0, 0},
++      {0x3744, 0x88, 0, 0},
++      {0x3745, 0xc0, 0, 0},
++      {0x374a, 0x43, 0, 0},
++      {0x374c, 0x00, 0, 0},
++      {0x374e, 0x23, 0, 0},
++      {0x3751, 0x7b, 0, 0},
++      {0x3752, 0x84, 0, 0},
++      {0x3753, 0xbd, 0, 0},
++      {0x3754, 0xbc, 0, 0},
++      {0x3756, 0x52, 0, 0},
++      {0x375c, 0x00, 0, 0},
++      {0x3760, 0x00, 0, 0},
++      {0x3761, 0x00, 0, 0},
++      {0x3762, 0x00, 0, 0},
++      {0x3763, 0x00, 0, 0},
++      {0x3764, 0x00, 0, 0},
++      {0x3767, 0x04, 0, 0},
++      {0x3768, 0x04, 0, 0},
++      {0x3769, 0x08, 0, 0},
++      {0x376a, 0x08, 0, 0},
++      {0x376b, 0x40, 0, 0},
++      {0x376c, 0x00, 0, 0},
++      {0x376d, 0x00, 0, 0},
++      {0x376e, 0x00, 0, 0},
++      {0x3773, 0x00, 0, 0},
++      {0x3774, 0x51, 0, 0},
++      {0x3776, 0xbd, 0, 0},
++      {0x3777, 0xbd, 0, 0},
++      {0x3781, 0x18, 0, 0},
++      {0x3783, 0x25, 0, 0},
++      {0x3798, 0x1b, 0, 0},
++      {0x3800, 0x00, 0, 0},
++      {0x3801, 0x48, 0, 0},
++      {0x3802, 0x00, 0, 0},
++      {0x3803, 0x2C, 0, 0},
++      {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x57, 0, 0},
++      {0x3806, 0x05, 0, 0},
++      {0x3807, 0xD3, 0, 0},
++      {0x3808, 0x02, 0, 0},
++      {0x3809, 0x80, 0, 0},
++      {0x380a, 0x01, 0, 0},
++      {0x380b, 0xe0, 0, 0},
++
++      {0x380c, 0x02, 0, 0}, // 0a ; 03
++      {0x380d, 0x04, 0, 0}, // 1c ; 5C
++
++      {0x380e, 0x03, 0, 0},
++      {0x380f, 0x05, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x04, 0, 0},
++      {0x3812, 0x00, 0, 0},
++      {0x3813, 0x02, 0, 0},
++      {0x3814, 0x03, 0, 0},
++      {0x3815, 0x01, 0, 0},
++      {0x3819, 0x01, 0, 0},
++      {0x3820, 0x06, 0, 0},
++      {0x3821, 0x00, 0, 0},
++      {0x3829, 0x00, 0, 0},
++      {0x382a, 0x03, 0, 0},
++      {0x382b, 0x01, 0, 0},
++      {0x382d, 0x7f, 0, 0},
++      {0x3830, 0x08, 0, 0},
++      {0x3836, 0x02, 0, 0},
++      {0x3837, 0x00, 0, 0},
++      {0x3841, 0x02, 0, 0},
++      {0x3846, 0x08, 0, 0},
++      {0x3847, 0x07, 0, 0},
++      {0x3d85, 0x36, 0, 0},
++      {0x3d8c, 0x71, 0, 0},
++      {0x3d8d, 0xcb, 0, 0},
++      {0x3f0a, 0x00, 0, 0},
++      {0x4000, 0x71, 0, 0},
++      {0x4001, 0x50, 0, 0},
++      {0x4002, 0x04, 0, 0},
++      {0x4003, 0x14, 0, 0},
++      {0x400e, 0x00, 0, 0},
++      {0x4011, 0x00, 0, 0},
++      {0x401a, 0x00, 0, 0},
++      {0x401b, 0x00, 0, 0},
++      {0x401c, 0x00, 0, 0},
++      {0x401d, 0x00, 0, 0},
++      {0x401f, 0x00, 0, 0},
++      {0x4020, 0x00, 0, 0},
++      {0x4021, 0x10, 0, 0},
++      {0x4022, 0x03, 0, 0},
++      {0x4023, 0x93, 0, 0},
++      {0x4024, 0x04, 0, 0},
++      {0x4025, 0xC0, 0, 0},
++      {0x4026, 0x04, 0, 0},
++      {0x4027, 0xD0, 0, 0},
++      {0x4028, 0x00, 0, 0},
++      {0x4029, 0x02, 0, 0},
++      {0x402a, 0x06, 0, 0},
++      {0x402b, 0x04, 0, 0},
++      {0x402c, 0x02, 0, 0},
++      {0x402d, 0x02, 0, 0},
++      {0x402e, 0x0e, 0, 0},
++      {0x402f, 0x04, 0, 0},
++      {0x4302, 0xff, 0, 0},
++      {0x4303, 0xff, 0, 0},
++      {0x4304, 0x00, 0, 0},
++      {0x4305, 0x00, 0, 0},
++      {0x4306, 0x00, 0, 0},
++      {0x4308, 0x02, 0, 0},
++      {0x4500, 0x6c, 0, 0},
++      {0x4501, 0xc4, 0, 0},
++      {0x4502, 0x44, 0, 0},
++      {0x4503, 0x01, 0, 0},
++      {0x4600, 0x00, 0, 0},
++      {0x4601, 0x4F, 0, 0},
++      {0x4800, 0x04, 0, 0},
++      {0x4813, 0x08, 0, 0},
++      {0x481f, 0x40, 0, 0},
++      {0x4829, 0x78, 0, 0},
++      {0x4837, 0x10, 0, 0}, // 20 ; 10
++      {0x4b00, 0x2a, 0, 0},
++      {0x4b0d, 0x00, 0, 0},
++      {0x4d00, 0x04, 0, 0},
++      {0x4d01, 0x42, 0, 0},
++      {0x4d02, 0xd1, 0, 0},
++      {0x4d03, 0x93, 0, 0},
++      {0x4d04, 0xf5, 0, 0},
++      {0x4d05, 0xc1, 0, 0},
++      {0x5000, 0xf3, 0, 0},
++      {0x5001, 0x11, 0, 0},
++      {0x5004, 0x00, 0, 0},
++      {0x500a, 0x00, 0, 0},
++      {0x500b, 0x00, 0, 0},
++      {0x5032, 0x00, 0, 0},
++      {0x5040, 0x00, 0, 0},
++      {0x5050, 0x3c, 0, 0},
++      {0x5500, 0x00, 0, 0},
++      {0x5501, 0x10, 0, 0},
++      {0x5502, 0x01, 0, 0},
++      {0x5503, 0x0f, 0, 0},
++      {0x8000, 0x00, 0, 0},
++      {0x8001, 0x00, 0, 0},
++      {0x8002, 0x00, 0, 0},
++      {0x8003, 0x00, 0, 0},
++      {0x8004, 0x00, 0, 0},
++      {0x8005, 0x00, 0, 0},
++      {0x8006, 0x00, 0, 0},
++      {0x8007, 0x00, 0, 0},
++      {0x8008, 0x00, 0, 0},
++      {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_720P_1280_720[] = {
++      //@@ RES_1280x720_2x_Bin_150fps_816Mbps
++      //OV4689_AM01B_1280x720_24M_2lane_816Mbps_150fps_20140210.txt
++      {0x0103, 0x01, 0, 0},
++      {0x3638, 0x00, 0, 0},
++      {0x0300, 0x00, 0, 0}, // 00
++      {0x0302, 0x22, 0, 0}, // 816Mbps 5a ; 64 ; 5a ; 78  ; 78 ; 2a
++      {0x0303, 0x00, 0, 0}, // 03 ; 01 ; 02 ;
++      {0x0304, 0x03, 0, 0},
++      {0x030b, 0x00, 0, 0},
++      {0x030d, 0x1e, 0, 0},
++      {0x030e, 0x04, 0, 0},
++      {0x030f, 0x01, 0, 0},
++      {0x0312, 0x01, 0, 0},
++      {0x031e, 0x00, 0, 0},
++      {0x3000, 0x20, 0, 0},
++      {0x3002, 0x00, 0, 0},
++      {0x3020, 0x93, 0, 0},
++      {0x3021, 0x03, 0, 0},
++      {0x3022, 0x01, 0, 0},
++      {0x3031, 0x0a, 0, 0},
++      {0x303f, 0x0c, 0, 0},
++      {0x3305, 0xf1, 0, 0},
++      {0x3307, 0x04, 0, 0},
++      {0x3309, 0x29, 0, 0},
++      {0x3500, 0x00, 0, 0},
++      {0x3501, 0x30, 0, 0},
++      {0x3502, 0x00, 0, 0},
++      {0x3503, 0x04, 0, 0},
++      {0x3504, 0x00, 0, 0},
++      {0x3505, 0x00, 0, 0},
++      {0x3506, 0x00, 0, 0},
++      {0x3507, 0x00, 0, 0},
++      {0x3508, 0x07, 0, 0},
++      {0x3509, 0x78, 0, 0}, // 8X
++      {0x350a, 0x00, 0, 0},
++      {0x350b, 0x00, 0, 0},
++      {0x350c, 0x00, 0, 0},
++      {0x350d, 0x00, 0, 0},
++      {0x350e, 0x00, 0, 0},
++      {0x350f, 0x80, 0, 0},
++      {0x3510, 0x00, 0, 0},
++      {0x3511, 0x00, 0, 0},
++      {0x3512, 0x00, 0, 0},
++      {0x3513, 0x00, 0, 0},
++      {0x3514, 0x00, 0, 0},
++      {0x3515, 0x80, 0, 0},
++      {0x3516, 0x00, 0, 0},
++      {0x3517, 0x00, 0, 0},
++      {0x3518, 0x00, 0, 0},
++      {0x3519, 0x00, 0, 0},
++      {0x351a, 0x00, 0, 0},
++      {0x351b, 0x80, 0, 0},
++      {0x351c, 0x00, 0, 0},
++      {0x351d, 0x00, 0, 0},
++      {0x351e, 0x00, 0, 0},
++      {0x351f, 0x00, 0, 0},
++      {0x3520, 0x00, 0, 0},
++      {0x3521, 0x80, 0, 0},
++      {0x3522, 0x08, 0, 0},
++      {0x3524, 0x08, 0, 0},
++      {0x3526, 0x08, 0, 0},
++      {0x3528, 0x08, 0, 0},
++      {0x352a, 0x08, 0, 0},
++      {0x3602, 0x00, 0, 0},
++      {0x3603, 0x40, 0, 0},
++      {0x3604, 0x02, 0, 0},
++      {0x3605, 0x00, 0, 0},
++      {0x3606, 0x00, 0, 0},
++      {0x3607, 0x00, 0, 0},
++      {0x3609, 0x12, 0, 0},
++      {0x360a, 0x40, 0, 0},
++      {0x360c, 0x08, 0, 0},
++      {0x360f, 0xe5, 0, 0},
++      {0x3608, 0x8f, 0, 0},
++      {0x3611, 0x00, 0, 0},
++      {0x3613, 0xf7, 0, 0},
++      {0x3616, 0x58, 0, 0},
++      {0x3619, 0x99, 0, 0},
++      {0x361b, 0x60, 0, 0},
++      {0x361c, 0x7a, 0, 0},
++      {0x361e, 0x79, 0, 0},
++      {0x361f, 0x02, 0, 0},
++      {0x3632, 0x05, 0, 0},
++      {0x3633, 0x10, 0, 0},
++      {0x3634, 0x10, 0, 0},
++      {0x3635, 0x10, 0, 0},
++      {0x3636, 0x15, 0, 0},
++      {0x3646, 0x86, 0, 0},
++      {0x364a, 0x0b, 0, 0},
++      {0x3700, 0x17, 0, 0},
++      {0x3701, 0x22, 0, 0},
++      {0x3703, 0x10, 0, 0},
++      {0x370a, 0x37, 0, 0},
++      {0x3705, 0x00, 0, 0},
++      {0x3706, 0x63, 0, 0},
++      {0x3709, 0x3c, 0, 0},
++      {0x370b, 0x01, 0, 0},
++      {0x370c, 0x30, 0, 0},
++      {0x3710, 0x24, 0, 0},
++      {0x3711, 0x0c, 0, 0},
++      {0x3716, 0x00, 0, 0},
++      {0x3720, 0x28, 0, 0},
++      {0x3729, 0x7b, 0, 0},
++      {0x372a, 0x84, 0, 0},
++      {0x372b, 0xbd, 0, 0},
++      {0x372c, 0xbc, 0, 0},
++      {0x372e, 0x52, 0, 0},
++      {0x373c, 0x0e, 0, 0},
++      {0x373e, 0x33, 0, 0},
++      {0x3743, 0x10, 0, 0},
++      {0x3744, 0x88, 0, 0},
++      {0x3745, 0xc0, 0, 0},
++      {0x374a, 0x43, 0, 0},
++      {0x374c, 0x00, 0, 0},
++      {0x374e, 0x23, 0, 0},
++      {0x3751, 0x7b, 0, 0},
++      {0x3752, 0x84, 0, 0},
++      {0x3753, 0xbd, 0, 0},
++      {0x3754, 0xbc, 0, 0},
++      {0x3756, 0x52, 0, 0},
++      {0x375c, 0x00, 0, 0},
++      {0x3760, 0x00, 0, 0},
++      {0x3761, 0x00, 0, 0},
++      {0x3762, 0x00, 0, 0},
++      {0x3763, 0x00, 0, 0},
++      {0x3764, 0x00, 0, 0},
++      {0x3767, 0x04, 0, 0},
++      {0x3768, 0x04, 0, 0},
++      {0x3769, 0x08, 0, 0},
++      {0x376a, 0x08, 0, 0},
++      {0x376b, 0x40, 0, 0},
++      {0x376c, 0x00, 0, 0},
++      {0x376d, 0x00, 0, 0},
++      {0x376e, 0x00, 0, 0},
++      {0x3773, 0x00, 0, 0},
++      {0x3774, 0x51, 0, 0},
++      {0x3776, 0xbd, 0, 0},
++      {0x3777, 0xbd, 0, 0},
++      {0x3781, 0x18, 0, 0},
++      {0x3783, 0x25, 0, 0},
++      {0x3798, 0x1b, 0, 0},
++      {0x3800, 0x00, 0, 0},
++      {0x3801, 0x48, 0, 0},
++      {0x3802, 0x00, 0, 0},
++      {0x3803, 0x2C, 0, 0},
++      {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x57, 0, 0},
++      {0x3806, 0x05, 0, 0},
++      {0x3807, 0xD3, 0, 0},
++      {0x3808, 0x05, 0, 0},
++      {0x3809, 0x00, 0, 0},
++      {0x380a, 0x02, 0, 0},
++      {0x380b, 0xD0, 0, 0},
++#ifndef UNUSED_CODE
++      {0x380c, 0x04, 0, 0}, // 0a ; 03
++      {0x380d, 0x08, 0, 0}, // 1c ; 5C
++#else
++      {0x380c, 0x05, 0, 0}, // 120fps
++      {0x380d, 0x0A, 0, 0},
++#endif
++      {0x380e, 0x03, 0, 0},
++      {0x380f, 0x05, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x04, 0, 0},
++      {0x3812, 0x00, 0, 0},
++      {0x3813, 0x02, 0, 0},
++      {0x3814, 0x03, 0, 0},
++      {0x3815, 0x01, 0, 0},
++      {0x3819, 0x01, 0, 0},
++      {0x3820, 0x06, 0, 0},
++      {0x3821, 0x00, 0, 0},
++      {0x3829, 0x00, 0, 0},
++      {0x382a, 0x03, 0, 0},
++      {0x382b, 0x01, 0, 0},
++      {0x382d, 0x7f, 0, 0},
++      {0x3830, 0x08, 0, 0},
++      {0x3836, 0x02, 0, 0},
++      {0x3837, 0x00, 0, 0},
++      {0x3841, 0x02, 0, 0},
++      {0x3846, 0x08, 0, 0},
++      {0x3847, 0x07, 0, 0},
++      {0x3d85, 0x36, 0, 0},
++      {0x3d8c, 0x71, 0, 0},
++      {0x3d8d, 0xcb, 0, 0},
++      {0x3f0a, 0x00, 0, 0},
++      {0x4000, 0x71, 0, 0},
++      {0x4001, 0x50, 0, 0},
++      {0x4002, 0x04, 0, 0},
++      {0x4003, 0x14, 0, 0},
++      {0x400e, 0x00, 0, 0},
++      {0x4011, 0x00, 0, 0},
++      {0x401a, 0x00, 0, 0},
++      {0x401b, 0x00, 0, 0},
++      {0x401c, 0x00, 0, 0},
++      {0x401d, 0x00, 0, 0},
++      {0x401f, 0x00, 0, 0},
++      {0x4020, 0x00, 0, 0},
++      {0x4021, 0x10, 0, 0},
++      {0x4022, 0x03, 0, 0},
++      {0x4023, 0x93, 0, 0},
++      {0x4024, 0x04, 0, 0},
++      {0x4025, 0xC0, 0, 0},
++      {0x4026, 0x04, 0, 0},
++      {0x4027, 0xD0, 0, 0},
++      {0x4028, 0x00, 0, 0},
++      {0x4029, 0x02, 0, 0},
++      {0x402a, 0x06, 0, 0},
++      {0x402b, 0x04, 0, 0},
++      {0x402c, 0x02, 0, 0},
++      {0x402d, 0x02, 0, 0},
++      {0x402e, 0x0e, 0, 0},
++      {0x402f, 0x04, 0, 0},
++      {0x4302, 0xff, 0, 0},
++      {0x4303, 0xff, 0, 0},
++      {0x4304, 0x00, 0, 0},
++      {0x4305, 0x00, 0, 0},
++      {0x4306, 0x00, 0, 0},
++      {0x4308, 0x02, 0, 0},
++      {0x4500, 0x6c, 0, 0},
++      {0x4501, 0xc4, 0, 0},
++      {0x4502, 0x44, 0, 0},
++      {0x4503, 0x01, 0, 0},
++      {0x4600, 0x00, 0, 0},
++      {0x4601, 0x4F, 0, 0},
++      {0x4800, 0x04, 0, 0},
++      {0x4813, 0x08, 0, 0},
++      {0x481f, 0x40, 0, 0},
++      {0x4829, 0x78, 0, 0},
++      {0x4837, 0x10, 0, 0}, // 20 ; 10
++      {0x4b00, 0x2a, 0, 0},
++      {0x4b0d, 0x00, 0, 0},
++      {0x4d00, 0x04, 0, 0},
++      {0x4d01, 0x42, 0, 0},
++      {0x4d02, 0xd1, 0, 0},
++      {0x4d03, 0x93, 0, 0},
++      {0x4d04, 0xf5, 0, 0},
++      {0x4d05, 0xc1, 0, 0},
++      {0x5000, 0xf3, 0, 0},
++      {0x5001, 0x11, 0, 0},
++      {0x5004, 0x00, 0, 0},
++      {0x500a, 0x00, 0, 0},
++      {0x500b, 0x00, 0, 0},
++      {0x5032, 0x00, 0, 0},
++      {0x5040, 0x00, 0, 0},
++      {0x5050, 0x3c, 0, 0},
++      {0x5500, 0x00, 0, 0},
++      {0x5501, 0x10, 0, 0},
++      {0x5502, 0x01, 0, 0},
++      {0x5503, 0x0f, 0, 0},
++      {0x8000, 0x00, 0, 0},
++      {0x8001, 0x00, 0, 0},
++      {0x8002, 0x00, 0, 0},
++      {0x8003, 0x00, 0, 0},
++      {0x8004, 0x00, 0, 0},
++      {0x8005, 0x00, 0, 0},
++      {0x8006, 0x00, 0, 0},
++      {0x8007, 0x00, 0, 0},
++      {0x8008, 0x00, 0, 0},
++      {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_1080P_1920_1080[] = {
++      //@@ RES_1920x1080_60fps_816Mbps 2lanes
++      {0x0103, 0x01, 0, 0},
++      {0x3638, 0x00, 0, 0},
++      {0x0300, 0x00, 0, 0},  // clk
++      {0x0302, 0x22, 0, 0},
++      {0x0303, 0x00, 0, 0},
++      {0x0304, 0x03, 0, 0},
++      {0x030b, 0x00, 0, 0},
++      {0x030d, 0x1e, 0, 0},
++      {0x030e, 0x04, 0, 0},
++      {0x030f, 0x01, 0, 0},
++      {0x0312, 0x01, 0, 0},
++      {0x031e, 0x00, 0, 0},
++      {0x3000, 0x20, 0, 0},
++      {0x3002, 0x00, 0, 0},
++      {0x3020, 0x93, 0, 0},
++      {0x3021, 0x03, 0, 0},
++      {0x3022, 0x01, 0, 0},
++      {0x3031, 0x0a, 0, 0},
++      {0x303f, 0x0c, 0, 0},
++      {0x3305, 0xf1, 0, 0},
++      {0x3307, 0x04, 0, 0},
++      {0x3309, 0x29, 0, 0},
++      {0x3500, 0x00, 0, 0},  // AEC
++      {0x3501, 0x4c, 0, 0},
++      {0x3502, 0x00, 0, 0},
++      {0x3503, 0x04, 0, 0},
++      {0x3504, 0x00, 0, 0},
++      {0x3505, 0x00, 0, 0},
++      {0x3506, 0x00, 0, 0},
++      {0x3507, 0x00, 0, 0},
++      {0x3508, 0x00, 0, 0},
++      {0x3509, 0x80, 0, 0},
++      {0x350a, 0x00, 0, 0},
++      {0x350b, 0x00, 0, 0},
++      {0x350c, 0x00, 0, 0},
++      {0x350d, 0x00, 0, 0},
++      {0x350e, 0x00, 0, 0},
++      {0x350f, 0x80, 0, 0},
++      {0x3510, 0x00, 0, 0},
++      {0x3511, 0x00, 0, 0},
++      {0x3512, 0x00, 0, 0},
++      {0x3513, 0x00, 0, 0},
++      {0x3514, 0x00, 0, 0},
++      {0x3515, 0x80, 0, 0},
++      {0x3516, 0x00, 0, 0},
++      {0x3517, 0x00, 0, 0},
++      {0x3518, 0x00, 0, 0},
++      {0x3519, 0x00, 0, 0},
++      {0x351a, 0x00, 0, 0},
++      {0x351b, 0x80, 0, 0},
++      {0x351c, 0x00, 0, 0},
++      {0x351d, 0x00, 0, 0},
++      {0x351e, 0x00, 0, 0},
++      {0x351f, 0x00, 0, 0},
++      {0x3520, 0x00, 0, 0},
++      {0x3521, 0x80, 0, 0},
++      {0x3522, 0x08, 0, 0},
++      {0x3524, 0x08, 0, 0},
++      {0x3526, 0x08, 0, 0},
++      {0x3528, 0x08, 0, 0},
++      {0x352a, 0x08, 0, 0},
++      {0x3602, 0x00, 0, 0},
++      {0x3603, 0x40, 0, 0},
++      {0x3604, 0x02, 0, 0},
++      {0x3605, 0x00, 0, 0},
++      {0x3606, 0x00, 0, 0},
++      {0x3607, 0x00, 0, 0},
++      {0x3609, 0x12, 0, 0},
++      {0x360a, 0x40, 0, 0},
++      {0x360c, 0x08, 0, 0},
++      {0x360f, 0xe5, 0, 0},
++      {0x3608, 0x8f, 0, 0},
++      {0x3611, 0x00, 0, 0},
++      {0x3613, 0xf7, 0, 0},
++      {0x3616, 0x58, 0, 0},
++      {0x3619, 0x99, 0, 0},
++      {0x361b, 0x60, 0, 0},
++      {0x361c, 0x7a, 0, 0},
++      {0x361e, 0x79, 0, 0},
++      {0x361f, 0x02, 0, 0},
++      {0x3632, 0x00, 0, 0},
++      {0x3633, 0x10, 0, 0},
++      {0x3634, 0x10, 0, 0},
++      {0x3635, 0x10, 0, 0},
++      {0x3636, 0x15, 0, 0},
++      {0x3646, 0x86, 0, 0},
++      {0x364a, 0x0b, 0, 0},
++      {0x3700, 0x17, 0, 0},
++      {0x3701, 0x22, 0, 0},
++      {0x3703, 0x10, 0, 0},
++      {0x370a, 0x37, 0, 0},
++      {0x3705, 0x00, 0, 0},
++      {0x3706, 0x63, 0, 0},
++      {0x3709, 0x3c, 0, 0},
++      {0x370b, 0x01, 0, 0},
++      {0x370c, 0x30, 0, 0},
++      {0x3710, 0x24, 0, 0},
++      {0x3711, 0x0c, 0, 0},
++      {0x3716, 0x00, 0, 0},
++      {0x3720, 0x28, 0, 0},
++      {0x3729, 0x7b, 0, 0},
++      {0x372a, 0x84, 0, 0},
++      {0x372b, 0xbd, 0, 0},
++      {0x372c, 0xbc, 0, 0},
++      {0x372e, 0x52, 0, 0},
++      {0x373c, 0x0e, 0, 0},
++      {0x373e, 0x33, 0, 0},
++      {0x3743, 0x10, 0, 0},
++      {0x3744, 0x88, 0, 0},
++      {0x3745, 0xc0, 0, 0},
++      {0x374a, 0x43, 0, 0},
++      {0x374c, 0x00, 0, 0},
++      {0x374e, 0x23, 0, 0},
++      {0x3751, 0x7b, 0, 0},
++      {0x3752, 0x84, 0, 0},
++      {0x3753, 0xbd, 0, 0},
++      {0x3754, 0xbc, 0, 0},
++      {0x3756, 0x52, 0, 0},
++      {0x375c, 0x00, 0, 0},
++      {0x3760, 0x00, 0, 0},
++      {0x3761, 0x00, 0, 0},
++      {0x3762, 0x00, 0, 0},
++      {0x3763, 0x00, 0, 0},
++      {0x3764, 0x00, 0, 0},
++      {0x3767, 0x04, 0, 0},
++      {0x3768, 0x04, 0, 0},
++      {0x3769, 0x08, 0, 0},
++      {0x376a, 0x08, 0, 0},
++      {0x376b, 0x20, 0, 0},
++      {0x376c, 0x00, 0, 0},
++      {0x376d, 0x00, 0, 0},
++      {0x376e, 0x00, 0, 0},
++      {0x3773, 0x00, 0, 0},
++      {0x3774, 0x51, 0, 0},
++      {0x3776, 0xbd, 0, 0},
++      {0x3777, 0xbd, 0, 0},
++      {0x3781, 0x18, 0, 0},
++      {0x3783, 0x25, 0, 0},
++      {0x3798, 0x1b, 0, 0},
++      {0x3800, 0x01, 0, 0},   // timings
++      {0x3801, 0x88, 0, 0},
++      {0x3802, 0x00, 0, 0},
++      {0x3803, 0xe0, 0, 0},
++      {0x3804, 0x09, 0, 0},
++      {0x3805, 0x17, 0, 0},
++      {0x3806, 0x05, 0, 0},
++      {0x3807, 0x1f, 0, 0},
++      {0x3808, 0x07, 0, 0},
++      {0x3809, 0x80, 0, 0},
++      {0x380a, 0x04, 0, 0},
++      {0x380b, 0x38, 0, 0},
++      {0x380c, 0x06, 0, 0},
++      {0x380d, 0xe0, 0, 0},
++      {0x380e, 0x04, 0, 0},
++      {0x380f, 0x70, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x08, 0, 0},
++      {0x3812, 0x00, 0, 0},
++      {0x3813, 0x04, 0, 0},
++      {0x3814, 0x01, 0, 0},
++      {0x3815, 0x01, 0, 0},
++      {0x3819, 0x01, 0, 0},
++      {0x3820, 0x06, 0, 0},
++      {0x3821, 0x00, 0, 0},
++      {0x3829, 0x00, 0, 0},
++      {0x382a, 0x01, 0, 0},
++      {0x382b, 0x01, 0, 0},
++      {0x382d, 0x7f, 0, 0},
++      {0x3830, 0x04, 0, 0},
++      {0x3836, 0x01, 0, 0},
++      {0x3837, 0x00, 0, 0},
++      {0x3841, 0x02, 0, 0},
++      {0x3846, 0x08, 0, 0},
++      {0x3847, 0x07, 0, 0},
++      {0x3d85, 0x36, 0, 0},
++      {0x3d8c, 0x71, 0, 0},
++      {0x3d8d, 0xcb, 0, 0},
++      {0x3f0a, 0x00, 0, 0},
++      {0x4000, 0xf1, 0, 0},
++      {0x4001, 0x40, 0, 0},
++      {0x4002, 0x04, 0, 0},
++      {0x4003, 0x14, 0, 0},
++      {0x400e, 0x00, 0, 0},
++      {0x4011, 0x00, 0, 0},
++      {0x401a, 0x00, 0, 0},
++      {0x401b, 0x00, 0, 0},
++      {0x401c, 0x00, 0, 0},
++      {0x401d, 0x00, 0, 0},
++      {0x401f, 0x00, 0, 0},
++      {0x4020, 0x00, 0, 0},
++      {0x4021, 0x10, 0, 0},
++      {0x4022, 0x06, 0, 0},
++      {0x4023, 0x13, 0, 0},
++      {0x4024, 0x07, 0, 0},
++      {0x4025, 0x40, 0, 0},
++      {0x4026, 0x07, 0, 0},
++      {0x4027, 0x50, 0, 0},
++      {0x4028, 0x00, 0, 0},
++      {0x4029, 0x02, 0, 0},
++      {0x402a, 0x06, 0, 0},
++      {0x402b, 0x04, 0, 0},
++      {0x402c, 0x02, 0, 0},
++      {0x402d, 0x02, 0, 0},
++      {0x402e, 0x0e, 0, 0},
++      {0x402f, 0x04, 0, 0},
++      {0x4302, 0xff, 0, 0},
++      {0x4303, 0xff, 0, 0},
++      {0x4304, 0x00, 0, 0},
++      {0x4305, 0x00, 0, 0},
++      {0x4306, 0x00, 0, 0},
++      {0x4308, 0x02, 0, 0},
++      {0x4500, 0x6c, 0, 0},
++      {0x4501, 0xc4, 0, 0},
++      {0x4502, 0x40, 0, 0},
++      {0x4503, 0x01, 0, 0},
++      {0x4601, 0x77, 0, 0},
++      {0x4800, 0x04, 0, 0},
++      {0x4813, 0x08, 0, 0},
++      {0x481f, 0x40, 0, 0},
++      {0x4829, 0x78, 0, 0},
++      {0x4837, 0x10, 0, 0},
++      {0x4b00, 0x2a, 0, 0},
++      {0x4b0d, 0x00, 0, 0},
++      {0x4d00, 0x04, 0, 0},
++      {0x4d01, 0x42, 0, 0},
++      {0x4d02, 0xd1, 0, 0},
++      {0x4d03, 0x93, 0, 0},
++      {0x4d04, 0xf5, 0, 0},
++      {0x4d05, 0xc1, 0, 0},
++      {0x5000, 0xf3, 0, 0},
++      {0x5001, 0x11, 0, 0},
++      {0x5004, 0x00, 0, 0},
++      {0x500a, 0x00, 0, 0},
++      {0x500b, 0x00, 0, 0},
++      {0x5032, 0x00, 0, 0},
++      {0x5040, 0x00, 0, 0},
++      {0x5050, 0x0c, 0, 0},
++      {0x5500, 0x00, 0, 0},
++      {0x5501, 0x10, 0, 0},
++      {0x5502, 0x01, 0, 0},
++      {0x5503, 0x0f, 0, 0},
++      {0x8000, 0x00, 0, 0},
++      {0x8001, 0x00, 0, 0},
++      {0x8002, 0x00, 0, 0},
++      {0x8003, 0x00, 0, 0},
++      {0x8004, 0x00, 0, 0},
++      {0x8005, 0x00, 0, 0},
++      {0x8006, 0x00, 0, 0},
++      {0x8007, 0x00, 0, 0},
++      {0x8008, 0x00, 0, 0},
++      {0x3638, 0x00, 0, 0},
++};
++
++static const struct reg_value ov4689_setting_4M_2688_1520[] = {
++      //@@ 0 10 RES_2688x1520_default(60fps)
++      //102 2630 960
++      {0x0103, 0x01, 0, 0},
++      {0x3638, 0x00, 0, 0},
++      {0x0300, 0x00, 0, 0},
++      {0x0302, 0x22, 0, 0}, // 2a ;1008Mbps,23 ;; 840Mbps
++      {0x0304, 0x03, 0, 0},
++      {0x030b, 0x00, 0, 0},
++      {0x030d, 0x1e, 0, 0},
++      {0x030e, 0x04, 0, 0},
++      {0x030f, 0x01, 0, 0},
++      {0x0312, 0x01, 0, 0},
++      {0x031e, 0x00, 0, 0},
++      {0x3000, 0x20, 0, 0},
++      {0x3002, 0x00, 0, 0},
++      {0x3020, 0x93, 0, 0},
++      {0x3021, 0x03, 0, 0},
++      {0x3022, 0x01, 0, 0},
++      {0x3031, 0x0a, 0, 0},
++      {0x303f, 0x0c, 0, 0},
++      {0x3305, 0xf1, 0, 0},
++      {0x3307, 0x04, 0, 0},
++      {0x3309, 0x29, 0, 0},
++      {0x3500, 0x00, 0, 0},
++      {0x3501, 0x60, 0, 0},
++      {0x3502, 0x00, 0, 0},
++      {0x3503, 0x04, 0, 0},
++      {0x3504, 0x00, 0, 0},
++      {0x3505, 0x00, 0, 0},
++      {0x3506, 0x00, 0, 0},
++      {0x3507, 0x00, 0, 0},
++      {0x3508, 0x00, 0, 0},
++      {0x3509, 0x80, 0, 0},
++      {0x350a, 0x00, 0, 0},
++      {0x350b, 0x00, 0, 0},
++      {0x350c, 0x00, 0, 0},
++      {0x350d, 0x00, 0, 0},
++      {0x350e, 0x00, 0, 0},
++      {0x350f, 0x80, 0, 0},
++      {0x3510, 0x00, 0, 0},
++      {0x3511, 0x00, 0, 0},
++      {0x3512, 0x00, 0, 0},
++      {0x3513, 0x00, 0, 0},
++      {0x3514, 0x00, 0, 0},
++      {0x3515, 0x80, 0, 0},
++      {0x3516, 0x00, 0, 0},
++      {0x3517, 0x00, 0, 0},
++      {0x3518, 0x00, 0, 0},
++      {0x3519, 0x00, 0, 0},
++      {0x351a, 0x00, 0, 0},
++      {0x351b, 0x80, 0, 0},
++      {0x351c, 0x00, 0, 0},
++      {0x351d, 0x00, 0, 0},
++      {0x351e, 0x00, 0, 0},
++      {0x351f, 0x00, 0, 0},
++      {0x3520, 0x00, 0, 0},
++      {0x3521, 0x80, 0, 0},
++      {0x3522, 0x08, 0, 0},
++      {0x3524, 0x08, 0, 0},
++      {0x3526, 0x08, 0, 0},
++      {0x3528, 0x08, 0, 0},
++      {0x352a, 0x08, 0, 0},
++      {0x3602, 0x00, 0, 0},
++      {0x3603, 0x40, 0, 0},
++      {0x3604, 0x02, 0, 0},
++      {0x3605, 0x00, 0, 0},
++      {0x3606, 0x00, 0, 0},
++      {0x3607, 0x00, 0, 0},
++      {0x3609, 0x12, 0, 0},
++      {0x360a, 0x40, 0, 0},
++      {0x360c, 0x08, 0, 0},
++      {0x360f, 0xe5, 0, 0},
++      {0x3608, 0x8f, 0, 0},
++      {0x3611, 0x00, 0, 0},
++      {0x3613, 0xf7, 0, 0},
++      {0x3616, 0x58, 0, 0},
++      {0x3619, 0x99, 0, 0},
++      {0x361b, 0x60, 0, 0},
++      {0x361c, 0x7a, 0, 0},
++      {0x361e, 0x79, 0, 0},
++      {0x361f, 0x02, 0, 0},
++      {0x3632, 0x00, 0, 0},
++      {0x3633, 0x10, 0, 0},
++      {0x3634, 0x10, 0, 0},
++      {0x3635, 0x10, 0, 0},
++      {0x3636, 0x15, 0, 0},
++      {0x3646, 0x86, 0, 0},
++      {0x364a, 0x0b, 0, 0},
++      {0x3700, 0x17, 0, 0},
++      {0x3701, 0x22, 0, 0},
++      {0x3703, 0x10, 0, 0},
++      {0x370a, 0x37, 0, 0},
++      {0x3705, 0x00, 0, 0},
++      {0x3706, 0x63, 0, 0},
++      {0x3709, 0x3c, 0, 0},
++      {0x370b, 0x01, 0, 0},
++      {0x370c, 0x30, 0, 0},
++      {0x3710, 0x24, 0, 0},
++      {0x3711, 0x0c, 0, 0},
++      {0x3716, 0x00, 0, 0},
++      {0x3720, 0x28, 0, 0},
++      {0x3729, 0x7b, 0, 0},
++      {0x372a, 0x84, 0, 0},
++      {0x372b, 0xbd, 0, 0},
++      {0x372c, 0xbc, 0, 0},
++      {0x372e, 0x52, 0, 0},
++      {0x373c, 0x0e, 0, 0},
++      {0x373e, 0x33, 0, 0},
++      {0x3743, 0x10, 0, 0},
++      {0x3744, 0x88, 0, 0},
++      {0x3745, 0xc0, 0, 0},
++      {0x374a, 0x43, 0, 0},
++      {0x374c, 0x00, 0, 0},
++      {0x374e, 0x23, 0, 0},
++      {0x3751, 0x7b, 0, 0},
++      {0x3752, 0x84, 0, 0},
++      {0x3753, 0xbd, 0, 0},
++      {0x3754, 0xbc, 0, 0},
++      {0x3756, 0x52, 0, 0},
++      {0x375c, 0x00, 0, 0},
++      {0x3760, 0x00, 0, 0},
++      {0x3761, 0x00, 0, 0},
++      {0x3762, 0x00, 0, 0},
++      {0x3763, 0x00, 0, 0},
++      {0x3764, 0x00, 0, 0},
++      {0x3767, 0x04, 0, 0},
++      {0x3768, 0x04, 0, 0},
++      {0x3769, 0x08, 0, 0},
++      {0x376a, 0x08, 0, 0},
++      {0x376b, 0x20, 0, 0},
++      {0x376c, 0x00, 0, 0},
++      {0x376d, 0x00, 0, 0},
++      {0x376e, 0x00, 0, 0},
++      {0x3773, 0x00, 0, 0},
++      {0x3774, 0x51, 0, 0},
++      {0x3776, 0xbd, 0, 0},
++      {0x3777, 0xbd, 0, 0},
++      {0x3781, 0x18, 0, 0},
++      {0x3783, 0x25, 0, 0},
++      {0x3798, 0x1b, 0, 0},
++      {0x3800, 0x00, 0, 0},
++      {0x3801, 0x08, 0, 0},
++      {0x3802, 0x00, 0, 0},
++      {0x3803, 0x04, 0, 0},
++      {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x97, 0, 0},
++      {0x3806, 0x05, 0, 0},
++      {0x3807, 0xfb, 0, 0},
++      {0x3808, 0x0a, 0, 0},
++      {0x3809, 0x80, 0, 0},
++      {0x380a, 0x05, 0, 0},
++      {0x380b, 0xf0, 0, 0},
++      {0x380c, 0x03, 0, 0},
++      {0x380d, 0x5c, 0, 0},
++      {0x380e, 0x06, 0, 0},
++      {0x380f, 0x12, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x08, 0, 0},
++      {0x3812, 0x00, 0, 0},
++      {0x3813, 0x04, 0, 0},
++      {0x3814, 0x01, 0, 0},
++      {0x3815, 0x01, 0, 0},
++      {0x3819, 0x01, 0, 0},
++      {0x3820, 0x00, 0, 0},
++      {0x3821, 0x06, 0, 0},
++      {0x3829, 0x00, 0, 0},
++      {0x382a, 0x01, 0, 0},
++      {0x382b, 0x01, 0, 0},
++      {0x382d, 0x7f, 0, 0},
++      {0x3830, 0x04, 0, 0},
++      {0x3836, 0x01, 0, 0},
++      {0x3837, 0x00, 0, 0},
++      {0x3841, 0x02, 0, 0},
++      {0x3846, 0x08, 0, 0},
++      {0x3847, 0x07, 0, 0},
++      {0x3d85, 0x36, 0, 0},
++      {0x3d8c, 0x71, 0, 0},
++      {0x3d8d, 0xcb, 0, 0},
++      {0x3f0a, 0x00, 0, 0},
++      {0x4000, 0x71, 0, 0},
++      {0x4001, 0x40, 0, 0},
++      {0x4002, 0x04, 0, 0},
++      {0x4003, 0x14, 0, 0},
++      {0x400e, 0x00, 0, 0},
++      {0x4011, 0x00, 0, 0},
++      {0x401a, 0x00, 0, 0},
++      {0x401b, 0x00, 0, 0},
++      {0x401c, 0x00, 0, 0},
++      {0x401d, 0x00, 0, 0},
++      {0x401f, 0x00, 0, 0},
++      {0x4020, 0x00, 0, 0},
++      {0x4021, 0x10, 0, 0},
++      {0x4022, 0x07, 0, 0},
++      {0x4023, 0xcf, 0, 0},
++      {0x4024, 0x09, 0, 0},
++      {0x4025, 0x60, 0, 0},
++      {0x4026, 0x09, 0, 0},
++      {0x4027, 0x6f, 0, 0},
++      {0x4028, 0x00, 0, 0},
++      {0x4029, 0x02, 0, 0},
++      {0x402a, 0x06, 0, 0},
++      {0x402b, 0x04, 0, 0},
++      {0x402c, 0x02, 0, 0},
++      {0x402d, 0x02, 0, 0},
++      {0x402e, 0x0e, 0, 0},
++      {0x402f, 0x04, 0, 0},
++      {0x4302, 0xff, 0, 0},
++      {0x4303, 0xff, 0, 0},
++      {0x4304, 0x00, 0, 0},
++      {0x4305, 0x00, 0, 0},
++      {0x4306, 0x00, 0, 0},
++      {0x4308, 0x02, 0, 0},
++      {0x4500, 0x6c, 0, 0},
++      {0x4501, 0xc4, 0, 0},
++      {0x4502, 0x40, 0, 0},
++      {0x4503, 0x01, 0, 0},
++      {0x4601, 0x04, 0, 0},
++      {0x4800, 0x04, 0, 0},
++      {0x4813, 0x08, 0, 0},
++      {0x481f, 0x40, 0, 0},
++      {0x4829, 0x78, 0, 0},
++      {0x4837, 0x14, 0, 0}, // 10
++      {0x4b00, 0x2a, 0, 0},
++      {0x4b0d, 0x00, 0, 0},
++      {0x4d00, 0x04, 0, 0},
++      {0x4d01, 0x42, 0, 0},
++      {0x4d02, 0xd1, 0, 0},
++      {0x4d03, 0x93, 0, 0},
++      {0x4d04, 0xf5, 0, 0},
++      {0x4d05, 0xc1, 0, 0},
++      {0x5000, 0xf3, 0, 0},
++      {0x5001, 0x11, 0, 0},
++      {0x5004, 0x00, 0, 0},
++      {0x500a, 0x00, 0, 0},
++      {0x500b, 0x00, 0, 0},
++      {0x5032, 0x00, 0, 0},
++      {0x5040, 0x00, 0, 0},
++      {0x5050, 0x0c, 0, 0},
++      {0x5500, 0x00, 0, 0},
++      {0x5501, 0x10, 0, 0},
++      {0x5502, 0x01, 0, 0},
++      {0x5503, 0x0f, 0, 0},
++      {0x8000, 0x00, 0, 0},
++      {0x8001, 0x00, 0, 0},
++      {0x8002, 0x00, 0, 0},
++      {0x8003, 0x00, 0, 0},
++      {0x8004, 0x00, 0, 0},
++      {0x8005, 0x00, 0, 0},
++      {0x8006, 0x00, 0, 0},
++      {0x8007, 0x00, 0, 0},
++      {0x8008, 0x00, 0, 0},
++      {0x3638, 0x00, 0, 0},
++//    {0x0100, 0x01, 0, 0},
++
++//    {0x0100, 0x00, 0, 0},
++      {0x380c, 0x0A, 0, 0}, // 05
++      {0x380d, 0x0A, 0, 0}, // 10
++      {0x380e, 0x06, 0, 0},
++      {0x380f, 0x12, 0, 0},
++//    {0x0100, 0x01, 0, 0},
++      {0x3105, 0x31, 0, 0},
++      {0x301a, 0xf9, 0, 0},
++      {0x3508, 0x07, 0, 0},
++      {0x484b, 0x05, 0, 0},
++      {0x4805, 0x03, 0, 0},
++      {0x3601, 0x01, 0, 0},
++      {0x3745, 0xc0, 0, 0},
++      {0x3798, 0x1b, 0, 0},
++//    {0x0100, 0x01, 0, 0},
++      {0xffff, 0x0a, 0, 0},
++      {0x3105, 0x11, 0, 0},
++      {0x301a, 0xf1, 0, 0},
++      {0x4805, 0x00, 0, 0},
++      {0x301a, 0xf0, 0, 0},
++      {0x3208, 0x00, 0, 0},
++      {0x302a, 0x00, 0, 0},
++      {0x302a, 0x00, 0, 0},
++      {0x302a, 0x00, 0, 0},
++      {0x302a, 0x00, 0, 0},
++      {0x302a, 0x00, 0, 0},
++      {0x3601, 0x00, 0, 0},
++      {0x3638, 0x00, 0, 0},
++      {0x3208, 0x10, 0, 0},
++      {0x3208, 0xa0, 0, 0},
++};
++
++/* power-on sensor init reg table */
++static const struct ov4689_mode_info ov4689_mode_init_data = {
++
++};
++
++static const struct ov4689_mode_info
++ov4689_mode_data[OV4689_NUM_MODES] = {
++      // {OV4689_MODE_720P_1280_720, SUBSAMPLING,
++      //  1280, 0x408, 720, 0x305,
++      //  ov4689_setting_720P_1280_720,
++      //  ARRAY_SIZE(ov4689_setting_720P_1280_720),
++      //  OV4689_150_FPS},
++      // {OV4689_MODE_1080P_1920_1080, SCALING,
++      //  1920, 0x6e0, 1080, 0x470,
++      //  ov4689_setting_1080P_1920_1080,
++      //  ARRAY_SIZE(ov4689_setting_1080P_1920_1080),
++      //  OV4689_60_FPS},
++      // {OV4689_MODE_4M_2688_1520, SCALING,
++      //  2688, 0xa0a, 1520, 0x612,
++      //  ov4689_setting_4M_2688_1520,
++      //  ARRAY_SIZE(ov4689_setting_4M_2688_1520),
++      //  OV4689_60_FPS},
++
++      {OV4689_MODE_1080P_1920_1080, SCALING,
++       1920, 0x6e0, 1080, 0x470,
++       ov4689_init_setting_30fps_1080P,
++       ARRAY_SIZE(ov4689_init_setting_30fps_1080P),
++       OV4689_60_FPS},
++};
++
++static int ov4689_write_reg(struct ov4689_dev *sensor, u16 reg, u8 val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg;
++      u8 buf[3];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++      buf[2] = val;
++
++      msg.addr = client->addr;
++      msg.flags = client->flags;
++      msg.buf = buf;
++      msg.len = sizeof(buf);
++
++      ret = i2c_transfer(client->adapter, &msg, 1);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++                      __func__, reg, val);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int ov4689_read_reg(struct ov4689_dev *sensor, u16 reg, u8 *val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg[2];
++      u8 buf[2];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++
++      msg[0].addr = client->addr;
++      msg[0].flags = client->flags;
++      msg[0].buf = buf;
++      msg[0].len = sizeof(buf);
++
++      msg[1].addr = client->addr;
++      msg[1].flags = client->flags | I2C_M_RD;
++      msg[1].buf = buf;
++      msg[1].len = 1;
++
++      ret = i2c_transfer(client->adapter, msg, 2);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x\n",
++                      __func__, reg);
++              return ret;
++      }
++
++      *val = buf[0];
++      return 0;
++}
++
++static int ov4689_read_reg16(struct ov4689_dev *sensor, u16 reg, u16 *val)
++{
++      u8 hi, lo;
++      int ret;
++
++      ret = ov4689_read_reg(sensor, reg, &hi);
++      if (ret)
++              return ret;
++      ret = ov4689_read_reg(sensor, reg + 1, &lo);
++      if (ret)
++              return ret;
++
++      *val = ((u16)hi << 8) | (u16)lo;
++      return 0;
++}
++
++static int ov4689_write_reg16(struct ov4689_dev *sensor, u16 reg, u16 val)
++{
++      int ret;
++
++      ret = ov4689_write_reg(sensor, reg, val >> 8);
++      if (ret)
++              return ret;
++
++      return ov4689_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int ov4689_mod_reg(struct ov4689_dev *sensor, u16 reg,
++                      u8 mask, u8 val)
++{
++      u8 readval;
++      int ret;
++
++      ret = ov4689_read_reg(sensor, reg, &readval);
++      if (ret)
++              return ret;
++
++      readval &= ~mask;
++      val &= mask;
++      val |= readval;
++
++      return ov4689_write_reg(sensor, reg, val);
++}
++
++static int ov4689_set_timings(struct ov4689_dev *sensor,
++                      const struct ov4689_mode_info *mode)
++{
++      return 0;
++}
++
++static int ov4689_load_regs(struct ov4689_dev *sensor,
++                      const struct ov4689_mode_info *mode)
++{
++      const struct reg_value *regs = mode->reg_data;
++      unsigned int i;
++      u32 delay_ms;
++      u16 reg_addr;
++      u8 mask, val;
++      int ret = 0;
++
++      st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id);
++      for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++              delay_ms = regs->delay_ms;
++              reg_addr = regs->reg_addr;
++              val = regs->val;
++              mask = regs->mask;
++
++              if (mask)
++                      ret = ov4689_mod_reg(sensor, reg_addr, mask, val);
++              else
++                      ret = ov4689_write_reg(sensor, reg_addr, val);
++              if (ret)
++                      break;
++
++              if (delay_ms)
++                      usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++      }
++
++      return ov4689_set_timings(sensor, mode);
++}
++
++#ifdef UNUSED_CODE
++static int ov4689_get_exposure(struct ov4689_dev *sensor)
++{
++      int exp, ret;
++      u8 temp;
++
++      ret = ov4689_read_reg(sensor, OV4689_REG_EXPOSURE_HI, &temp);
++      if (ret)
++              return ret;
++      exp = ((int)temp & 0x0f) << 16;
++      ret = ov4689_read_reg(sensor, OV4689_REG_EXPOSURE_MED, &temp);
++      if (ret)
++              return ret;
++      exp |= ((int)temp << 8);
++      ret = ov4689_read_reg(sensor, OV4689_REG_EXPOSURE_LO, &temp);
++      if (ret)
++              return ret;
++      exp |= (int)temp;
++
++      return exp >> 4;
++}
++#endif
++
++static int ov4689_set_exposure(struct ov4689_dev *sensor, u32 exposure)
++{
++      int ret;
++
++      st_info(ST_SENSOR, "%s, exposure = 0x%x\n", __func__, exposure);
++      exposure <<= 4;
++
++      ret = ov4689_write_reg(sensor,
++                      OV4689_REG_EXPOSURE_LO,
++                      exposure & 0xff);
++      if (ret)
++              return ret;
++      ret = ov4689_write_reg(sensor,
++                      OV4689_REG_EXPOSURE_MED,
++                      (exposure >> 8) & 0xff);
++      if (ret)
++              return ret;
++      return ov4689_write_reg(sensor,
++                              OV4689_REG_EXPOSURE_HI,
++                              (exposure >> 16) & 0x0f);
++}
++
++static int ov4689_get_gain(struct ov4689_dev *sensor)
++{
++      u32 gain = 0;
++      u8 val;
++
++      ov4689_read_reg(sensor, OV4689_REG_GAIN_H, &val);
++      gain = (val & 0x3) << 16;
++      ov4689_read_reg(sensor, OV4689_REG_GAIN_M, &val);
++      gain |= val << 8;
++      ov4689_read_reg(sensor, OV4689_REG_GAIN_L, &val);
++      gain |= val;
++
++      return gain;
++}
++
++static int ov4689_set_gain(struct ov4689_dev *sensor, int gain)
++{
++      ov4689_write_reg(sensor, OV4689_REG_GAIN_H,
++                              (gain >> 16) & 0x3);
++      ov4689_write_reg(sensor, OV4689_REG_GAIN_M,
++                              (gain >> 8) & 0xff);
++      ov4689_write_reg(sensor, OV4689_REG_GAIN_L,
++                              gain & 0xff);
++      return 0;
++}
++
++#ifdef UNUSED_CODE
++static int ov4689_get_sysclk(struct ov4689_dev *sensor)
++{
++      return 0;
++}
++
++static int ov4689_set_night_mode(struct ov4689_dev *sensor)
++{
++      return 0;
++}
++
++static int ov4689_get_hts(struct ov4689_dev *sensor)
++{
++      /* read HTS from register settings */
++      u16 hts;
++      int ret;
++
++      ret = ov4689_read_reg16(sensor, OV4689_REG_TIMING_HTS, &hts);
++      if (ret)
++              return ret;
++      return hts;
++}
++#endif
++
++static int ov4689_get_vts(struct ov4689_dev *sensor)
++{
++      u16 vts;
++      int ret;
++
++      ret = ov4689_read_reg16(sensor, OV4689_REG_TIMING_VTS, &vts);
++      if (ret)
++              return ret;
++      return vts;
++}
++
++#ifdef UNUSED_CODE
++static int ov4689_set_vts(struct ov4689_dev *sensor, int vts)
++{
++      return ov4689_write_reg16(sensor, OV4689_REG_TIMING_VTS, vts);
++}
++
++static int ov4689_get_light_freq(struct ov4689_dev *sensor)
++{
++      return 0;
++}
++
++static int ov4689_set_bandingfilter(struct ov4689_dev *sensor)
++{
++      return 0;
++}
++
++static int ov4689_set_ae_target(struct ov4689_dev *sensor, int target)
++{
++      return 0;
++}
++
++static int ov4689_get_binning(struct ov4689_dev *sensor)
++{
++      return 0;
++}
++
++static int ov4689_set_binning(struct ov4689_dev *sensor, bool enable)
++{
++      return 0;
++}
++#endif
++
++static const struct ov4689_mode_info *
++ov4689_find_mode(struct ov4689_dev *sensor, enum ov4689_frame_rate fr,
++              int width, int height, bool nearest)
++{
++      const struct ov4689_mode_info *mode;
++
++      mode = v4l2_find_nearest_size(ov4689_mode_data,
++                              ARRAY_SIZE(ov4689_mode_data),
++                              hact, vact,
++                              width, height);
++
++      if (!mode ||
++              (!nearest && (mode->hact != width || mode->vact != height)))
++              return NULL;
++
++      /* Check to see if the current mode exceeds the max frame rate */
++      if (ov4689_framerates[fr] > ov4689_framerates[mode->max_fps])
++              return NULL;
++
++      return mode;
++}
++
++static u64 ov4689_calc_pixel_rate(struct ov4689_dev *sensor)
++{
++      u64 rate;
++
++      rate = sensor->current_mode->vact * sensor->current_mode->hact;
++      rate *= ov4689_framerates[sensor->current_fr];
++
++      return rate;
++}
++
++/*
++ * After trying the various combinations, reading various
++ * documentations spread around the net, and from the various
++ * feedback, the clock tree is probably as follows:
++ *
++ *   +--------------+
++ *   |  Ext. Clock  |
++ *   +-+------------+
++ *     |  +----------+
++ *     +->|   PLL1   | - reg 0x030a, bit0 for the pre-dividerp
++ *        +-+--------+ - reg 0x0300, bits 0-2 for the pre-divider
++ *        +-+--------+ - reg 0x0301~0x0302, for the multiplier
++ *          |  +--------------+
++ *          +->| MIPI Divider |  - reg 0x0303, bits 0-3 for the pre-divider
++ *               | +---------> MIPI PHY CLK
++ *               |    +-----+
++ *               | +->| PLL1_DIV_MIPI | - reg 0x0304, bits 0-1 for the divider
++ *                 |    +----------------> PCLK
++ *               |    +-----+
++ *
++ *   +--------------+
++ *   |  Ext. Clock  |
++ *   +-+------------+
++ *     |  +----------+
++ *     +->|   PLL2  | - reg 0x0311, bit0 for the pre-dividerp
++ *        +-+--------+ - reg 0x030b, bits 0-2 for the pre-divider
++ *        +-+--------+ - reg 0x030c~0x030d, for the multiplier
++ *          |  +--------------+
++ *          +->| SCLK Divider |  - reg 0x030F, bits 0-3 for the pre-divider
++ *               +-+--------+    - reg 0x030E, bits 0-2 for the divider
++ *               |    +---------> SCLK
++ *
++ *          |       +-----+
++ *          +->| DAC Divider | - reg 0x0312, bits 0-3 for the divider
++ *                    |    +----------------> DACCLK
++ **
++ */
++
++/*
++ * ov4689_set_mipi_pclk() - Calculate the clock tree configuration values
++ *                    for the MIPI CSI-2 output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ *    'Bandwidth Per Lane' is calculated as:
++ *    bpl = HTOT * VTOT * FPS * bpp / num_lanes;
++ *
++ * This function use the requested bandwidth to calculate:
++ *
++ * - mipi_pclk   = bpl / 2; ( / 2 is for CSI-2 DDR)
++ * - mipi_phy_clk   = mipi_pclk * PLL1_DIV_MIPI;
++ *
++ * with these fixed parameters:
++ *    PLL1_PREDIVP    = 1;
++ *    PLL1_PREDIV     = 1; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
++ *    PLL1_DIVM       = 1;
++ *    PLL1_DIV_MIPI   = 4;
++ *
++ * FIXME: this have been tested with 10-bit raw and 2 lanes setup only.
++ * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
++ * above formula for setups with 1 lane or image formats with different bpp.
++ *
++ * FIXME: this deviates from the sensor manual documentation which is quite
++ * thin on the MIPI clock tree generation part.
++ */
++
++#define PLL1_PREDIVP         1     // bypass
++#define PLL1_PREDIV          1     // bypass
++#define PLL1_DIVM            1  // bypass
++#define PLL1_DIV_MIPI        3  // div
++#define PLL1_DIV_MIPI_BASE   1  // div
++
++#define PLL1_DIVSP    1   // no use
++#define PLL1_DIVS     1   // no use
++
++#define PLL2_PREDIVP       0
++#define PLL2_PREDIV        0
++#define PLL2_DIVSP       1
++#define PLL2_DIVS        4
++#define PLL2_DIVDAC      1
++
++#define OV4689_PLL1_PREDIVP         0x030a   // bits[0]
++#define OV4689_PLL1_PREDIV          0x0300   // bits[2:0]
++#define OV4689_PLL1_MULTIPLIER      0x0301   // bits[9:8]  0x0302 bits[7:0]
++#define OV4689_PLL1_DIVM            0x0303   // bits[3:0]
++#define OV4689_PLL1_DIV_MIPI        0x0304   // bits[1:0]
++
++#define OV4689_PLL1_DIVSP           0x0305   //bits[1:0]
++#define OV4689_PLL1_DIVS            0x0306   // bits[0]
++
++#define OV4689_PLL2_PREDIVP         0x0311   // bits[0]
++#define OV4689_PLL2_PREDIV          0x030b   // bits[2:0]
++#define OV4689_PLL2_MULTIPLIER      0x030c   // bits[9:8]   0x030d bits[7:0]
++#define OV4689_PLL2_DIVSP           0x030f  // bits[3:0]
++#define OV4689_PLL2_DIVS            0x030e  // bits[2:0]
++#define OV4689_PLL2_DIVDAC          0x0312  // bits[3:0]
++
++static int ov4689_set_mipi_pclk(struct ov4689_dev *sensor,
++                              unsigned long rate)
++{
++      const struct ov4689_mode_info *mode = sensor->current_mode;
++      //const struct ov4689_mode_info *orig_mode = sensor->last_mode;
++      u8 val;
++      int ret = 0;
++      int fps = ov4689_framerates[sensor->current_fr];
++      u16 htot, val16;
++
++      htot = mode->htot * ov4689_framerates[mode->max_fps] / fps;
++
++      ret = ov4689_write_reg16(sensor, OV4689_REG_TIMING_HTS, htot);
++
++      ret = ov4689_read_reg(sensor, OV4689_REG_TIMING_HTS, &val);
++      val16 = val << 8;
++      ret = ov4689_read_reg(sensor, OV4689_REG_TIMING_HTS + 1, &val);
++      val16 |= val;
++
++      st_info(ST_SENSOR, "fps = %d, max_fps = %d\n", fps, mode->max_fps);
++      st_info(ST_SENSOR, "mode->htot = 0x%x, htot = 0x%x\n", mode->htot,
++                      htot);
++      st_info(ST_SENSOR, "reg: 0x%x = 0x%x\n", OV4689_REG_TIMING_HTS, val16);
++
++      return 0;
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int ov4689_set_mode_direct(struct ov4689_dev *sensor,
++                              const struct ov4689_mode_info *mode)
++{
++      if (!mode->reg_data)
++              return -EINVAL;
++
++      /* Write capture setting */
++      return ov4689_load_regs(sensor, mode);
++}
++
++static int ov4689_set_mode(struct ov4689_dev *sensor)
++{
++      const struct ov4689_mode_info *mode = sensor->current_mode;
++
++      int ret = 0;
++
++      ret = ov4689_set_mode_direct(sensor, mode);
++      if (ret < 0)
++              return ret;
++
++      ret = ov4689_set_mipi_pclk(sensor, 0);
++      if (ret < 0)
++              return 0;
++
++      sensor->pending_mode_change = false;
++      sensor->last_mode = mode;
++      return 0;
++}
++
++/* restore the last set video mode after chip power-on */
++static int ov4689_restore_mode(struct ov4689_dev *sensor)
++{
++      int ret;
++
++      /* first load the initial register values */
++      ret = ov4689_load_regs(sensor, &ov4689_mode_init_data);
++      if (ret < 0)
++              return ret;
++      sensor->last_mode = &ov4689_mode_init_data;
++
++      /* now restore the last capture mode */
++      ret = ov4689_set_mode(sensor);
++      if (ret < 0)
++              return ret;
++
++      return ret;
++}
++
++static void ov4689_power(struct ov4689_dev *sensor, bool enable)
++{
++      if (!sensor->pwdn_gpio)
++              return;
++      gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
++}
++
++static void ov4689_reset(struct ov4689_dev *sensor)
++{
++      if (!sensor->reset_gpio)
++              return;
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++
++      usleep_range(5000, 25000);
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++      usleep_range(1000, 2000);
++}
++
++static int ov4689_set_power_on(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      int ret;
++
++      ret = clk_prepare_enable(sensor->xclk);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable clock\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES,
++                              sensor->supplies);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable regulators\n",
++                      __func__);
++              goto xclk_off;
++      }
++
++      ov4689_reset(sensor);
++      ov4689_power(sensor, true);
++
++      return 0;
++
++xclk_off:
++      clk_disable_unprepare(sensor->xclk);
++      return ret;
++}
++
++static int ov4689_set_power_off(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++
++      ov4689_power(sensor, false);
++      regulator_bulk_disable(OV4689_NUM_SUPPLIES, sensor->supplies);
++      clk_disable_unprepare(sensor->xclk);
++
++      return 0;
++}
++
++static int ov4689_try_frame_interval(struct ov4689_dev *sensor,
++                              struct v4l2_fract *fi,
++                              u32 width, u32 height)
++{
++      const struct ov4689_mode_info *mode;
++      enum ov4689_frame_rate rate = OV4689_15_FPS;
++      int minfps, maxfps, best_fps, fps;
++      int i;
++
++      minfps = ov4689_framerates[OV4689_15_FPS];
++      maxfps = ov4689_framerates[OV4689_NUM_FRAMERATES - 1];
++
++      if (fi->numerator == 0) {
++              fi->denominator = maxfps;
++              fi->numerator = 1;
++              rate = OV4689_60_FPS;
++              goto find_mode;
++      }
++
++      fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++                      minfps, maxfps);
++
++      best_fps = minfps;
++      for (i = 0; i < ARRAY_SIZE(ov4689_framerates); i++) {
++              int curr_fps = ov4689_framerates[i];
++
++              if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++                      best_fps = curr_fps;
++                      rate = i;
++              }
++      }
++      st_info(ST_SENSOR, "best_fps = %d, fps = %d\n", best_fps, fps);
++
++      fi->numerator = 1;
++      fi->denominator = best_fps;
++
++find_mode:
++      mode = ov4689_find_mode(sensor, rate, width, height, false);
++      return mode ? rate : -EINVAL;
++}
++
++static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_mbus_code_enum *code)
++{
++      if (code->pad != 0)
++              return -EINVAL;
++
++      if (code->index)
++              return -EINVAL;
++
++      code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++      return 0;
++}
++
++static int ov4689_get_fmt(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *format)
++{
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      struct v4l2_mbus_framefmt *fmt;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++                                              format->pad);
++      else
++              fmt = &sensor->fmt;
++
++      format->format = *fmt;
++
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int ov4689_try_fmt_internal(struct v4l2_subdev *sd,
++                              struct v4l2_mbus_framefmt *fmt,
++                              enum ov4689_frame_rate fr,
++                              const struct ov4689_mode_info **new_mode)
++{
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      const struct ov4689_mode_info *mode;
++
++      mode = ov4689_find_mode(sensor, fr, fmt->width, fmt->height, true);
++      if (!mode)
++              return -EINVAL;
++      fmt->width = mode->hact;
++      fmt->height = mode->vact;
++
++      if (new_mode)
++              *new_mode = mode;
++
++      fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++
++      return 0;
++}
++
++static int ov4689_set_fmt(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *format)
++{
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      const struct ov4689_mode_info *new_mode;
++      struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++      struct v4l2_mbus_framefmt *fmt;
++      int ret;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      ret = ov4689_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++      if (ret)
++              goto out;
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(sd, state, 0);
++      else
++              fmt = &sensor->fmt;
++
++      *fmt = *mbus_fmt;
++
++      if (new_mode != sensor->current_mode) {
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++      if (new_mode->max_fps < sensor->current_fr) {
++              sensor->current_fr = new_mode->max_fps;
++              sensor->frame_interval.numerator = 1;
++              sensor->frame_interval.denominator =
++                      ov4689_framerates[sensor->current_fr];
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++
++      __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                              ov4689_calc_pixel_rate(sensor));
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int ov4689_set_ctrl_hue(struct ov4689_dev *sensor, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int ov4689_set_ctrl_contrast(struct ov4689_dev *sensor, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int ov4689_set_ctrl_saturation(struct ov4689_dev *sensor, int value)
++{
++      int ret  = 0;
++
++      return ret;
++}
++
++static int ov4689_set_ctrl_white_balance(struct ov4689_dev *sensor, int awb)
++{
++      struct ov4689_ctrls *ctrls = &sensor->ctrls;
++      int ret = 0;
++
++      if (!awb && (ctrls->red_balance->is_new
++                      || ctrls->blue_balance->is_new)) {
++              u16 red = (u16)ctrls->red_balance->val;
++              u16 blue = (u16)ctrls->blue_balance->val;
++
++              st_info(ST_SENSOR, "red = 0x%x, blue = 0x%x\n", red, blue);
++              ret = ov4689_write_reg16(sensor, OV4689_REG_AWB_R_GAIN, red);
++              if (ret)
++                      return ret;
++              ret = ov4689_write_reg16(sensor, OV4689_REG_AWB_B_GAIN, blue);
++      }
++      return ret;
++}
++
++static int ov4689_set_ctrl_exposure(struct ov4689_dev *sensor,
++                              enum v4l2_exposure_auto_type auto_exposure)
++{
++      struct ov4689_ctrls *ctrls = &sensor->ctrls;
++      bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
++      int ret = 0;
++
++      if (!auto_exp && ctrls->exposure->is_new) {
++              u16 max_exp = 0;
++
++              ret = ov4689_read_reg16(sensor, OV4689_REG_V_OUTPUT_SIZE,
++                                      &max_exp);
++
++              ret = ov4689_get_vts(sensor);
++              if (ret < 0)
++                      return ret;
++              max_exp += ret;
++              ret = 0;
++
++              st_info(ST_SENSOR, "%s, max_exp = 0x%x\n", __func__, max_exp);
++              if (ctrls->exposure->val < max_exp)
++                      ret = ov4689_set_exposure(sensor, ctrls->exposure->val);
++      }
++
++      return ret;
++}
++
++static const s64 link_freq_menu_items[] = {
++      OV4689_LINK_FREQ_500MHZ
++};
++
++static const char * const test_pattern_menu[] = {
++      "Disabled",
++      "Color bars",
++      "Color bars w/ rolling bar",
++      "Color squares",
++      "Color squares w/ rolling bar",
++};
++
++#define OV4689_TEST_ENABLE            BIT(7)
++#define OV4689_TEST_ROLLING           BIT(6)  /* rolling horizontal bar */
++#define OV4689_TEST_TRANSPARENT               BIT(5)
++#define OV4689_TEST_SQUARE_BW         BIT(4)  /* black & white squares */
++#define OV4689_TEST_BAR_STANDARD      (0 << 2)
++#define OV4689_TEST_BAR_DARKER_1      (1 << 2)
++#define OV4689_TEST_BAR_DARKER_2      (2 << 2)
++#define OV4689_TEST_BAR_DARKER_3      (3 << 2)
++#define OV4689_TEST_BAR                       (0 << 0)
++#define OV4689_TEST_RANDOM            (1 << 0)
++#define OV4689_TEST_SQUARE            (2 << 0)
++#define OV4689_TEST_BLACK             (3 << 0)
++
++static const u8 test_pattern_val[] = {
++      0,
++      OV4689_TEST_ENABLE | OV4689_TEST_BAR_STANDARD |
++              OV4689_TEST_BAR,
++      OV4689_TEST_ENABLE | OV4689_TEST_ROLLING |
++              OV4689_TEST_BAR_DARKER_1 | OV4689_TEST_BAR,
++      OV4689_TEST_ENABLE | OV4689_TEST_SQUARE,
++      OV4689_TEST_ENABLE | OV4689_TEST_ROLLING | OV4689_TEST_SQUARE,
++};
++
++static int ov4689_set_ctrl_test_pattern(struct ov4689_dev *sensor, int value)
++{
++      return ov4689_write_reg(sensor, OV4689_REG_TEST_PATTERN,
++                      test_pattern_val[value]);
++}
++
++static int ov4689_set_ctrl_light_freq(struct ov4689_dev *sensor, int value)
++{
++      return 0;
++}
++
++static int ov4689_set_ctrl_hflip(struct ov4689_dev *sensor, int value)
++{
++      /*
++       * TIMING TC REG21:
++       * - [2]:       Digital mirror
++       * - [1]:       Array mirror
++       */
++      return ov4689_mod_reg(sensor, OV4689_REG_TIMING_TC_REG21,
++                      BIT(2) | BIT(1),
++                      (!(value ^ sensor->upside_down)) ?
++                      (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov4689_set_ctrl_vflip(struct ov4689_dev *sensor, int value)
++{
++      /*
++       * TIMING TC REG20:
++       * - [2]:       Digital vflip
++       * - [1]:       Array vflip
++       */
++      return ov4689_mod_reg(sensor, OV4689_REG_TIMING_TC_REG20,
++                              BIT(2) | BIT(1),
++                              (value ^ sensor->upside_down) ?
++                              (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov4689_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      int val;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              val = ov4689_get_gain(sensor);
++              break;
++      }
++
++      pm_runtime_put(&sensor->i2c_client->dev);
++
++      return 0;
++}
++
++static int ov4689_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      int ret;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      /*
++       * If the device is not powered up by the host driver do
++       * not apply any controls to H/W at this time. Instead
++       * the controls will be restored at start streaming time.
++       */
++      if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = ov4689_set_gain(sensor, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE:
++              ret = ov4689_set_ctrl_exposure(sensor, V4L2_EXPOSURE_MANUAL);
++              break;
++      case V4L2_CID_AUTO_WHITE_BALANCE:
++              ret = ov4689_set_ctrl_white_balance(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HUE:
++              ret = ov4689_set_ctrl_hue(sensor, ctrl->val);
++              break;
++      case V4L2_CID_CONTRAST:
++              ret = ov4689_set_ctrl_contrast(sensor, ctrl->val);
++              break;
++      case V4L2_CID_SATURATION:
++              ret = ov4689_set_ctrl_saturation(sensor, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = ov4689_set_ctrl_test_pattern(sensor, ctrl->val);
++              break;
++      case V4L2_CID_POWER_LINE_FREQUENCY:
++              ret = ov4689_set_ctrl_light_freq(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HFLIP:
++              ret = ov4689_set_ctrl_hflip(sensor, ctrl->val);
++              break;
++      case V4L2_CID_VFLIP:
++              ret = ov4689_set_ctrl_vflip(sensor, ctrl->val);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      pm_runtime_put(&sensor->i2c_client->dev);
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
++      .g_volatile_ctrl = ov4689_g_volatile_ctrl,
++      .s_ctrl = ov4689_s_ctrl,
++};
++
++static int ov4689_init_controls(struct ov4689_dev *sensor)
++{
++      const struct v4l2_ctrl_ops *ops = &ov4689_ctrl_ops;
++      struct ov4689_ctrls *ctrls = &sensor->ctrls;
++      struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++      int ret;
++
++      v4l2_ctrl_handler_init(hdl, 32);
++
++      /* we can use our own mutex for the ctrl lock */
++      hdl->lock = &sensor->lock;
++
++      /* Clock related controls */
++      ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++                                      0, INT_MAX, 1,
++                                      ov4689_calc_pixel_rate(sensor));
++
++      /* Auto/manual white balance */
++      ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++                                      V4L2_CID_AUTO_WHITE_BALANCE,
++                                      0, 1, 1, 0);
++      ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++                                              0, 4095, 1, 1024);
++      ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++                                              0, 4095, 1, 1024);
++
++      ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++                                      4, 0xfff8, 1, 0x4c00);
++      ctrls->anal_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
++                                      0x10, 0xfff8, 1, 0x0080);
++      ctrls->test_pattern =
++              v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++                                      ARRAY_SIZE(test_pattern_menu) - 1,
++                                      0, 0, test_pattern_menu);
++      ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++                                      0, 1, 1, 0);
++      ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++                                      0, 1, 1, 0);
++      ctrls->light_freq =
++              v4l2_ctrl_new_std_menu(hdl, ops,
++                                      V4L2_CID_POWER_LINE_FREQUENCY,
++                                      V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++                                      V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++      ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
++                                      0, 0, link_freq_menu_items);
++      if (hdl->error) {
++              ret = hdl->error;
++              goto free_ctrls;
++      }
++
++      ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      // ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++      // ctrls->anal_gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++      v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++
++      sensor->sd.ctrl_handler = hdl;
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(hdl);
++      return ret;
++}
++
++static int ov4689_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      if (fse->pad != 0)
++              return -EINVAL;
++      if (fse->index >= OV4689_NUM_MODES)
++              return -EINVAL;
++
++      fse->min_width =
++              ov4689_mode_data[fse->index].hact;
++      fse->max_width = fse->min_width;
++      fse->min_height =
++              ov4689_mode_data[fse->index].vact;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static int ov4689_enum_frame_interval(
++      struct v4l2_subdev *sd,
++      struct v4l2_subdev_state *state,
++      struct v4l2_subdev_frame_interval_enum *fie)
++{
++      //struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      struct v4l2_fract tpf;
++      int i = 0;
++
++      if (fie->pad != 0)
++              return -EINVAL;
++      if (fie->index >= OV4689_NUM_FRAMERATES)
++              return -EINVAL;
++
++      tpf.numerator = 1;
++      tpf.denominator = ov4689_framerates[fie->index];
++
++      // ret = ov4689_try_frame_interval(sensor, &tpf,
++      //              fie->width, fie->height);
++      // if (ret < 0)
++      //      return -EINVAL;
++
++      pr_debug("fie->width = %d, fie->height = %d\n", fie->width, fie->height);
++      for (i = 0; i < OV4689_NUM_MODES; i++) {
++              if (fie->width == ov4689_mode_data[i].hact &&
++                      fie->height == ov4689_mode_data[i].vact)
++                      break;
++      }
++      if (i == OV4689_NUM_MODES)
++              return -ENOTTY;
++
++      fie->interval = tpf;
++      fie->width = ov4689_mode_data[i].hact;
++      fie->height = ov4689_mode_data[i].vact;
++
++      return 0;
++}
++
++static int ov4689_g_frame_interval(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_frame_interval *fi)
++{
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++
++      mutex_lock(&sensor->lock);
++      fi->interval = sensor->frame_interval;
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int ov4689_s_frame_interval(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_frame_interval *fi)
++{
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      const struct ov4689_mode_info *mode;
++      int frame_rate, ret = 0;
++
++      if (fi->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      mode = sensor->current_mode;
++
++      frame_rate = ov4689_try_frame_interval(sensor, &fi->interval,
++                                      mode->hact, mode->vact);
++      if (frame_rate < 0) {
++              /* Always return a valid frame interval value */
++              fi->interval = sensor->frame_interval;
++              goto out;
++      }
++
++      mode = ov4689_find_mode(sensor, frame_rate, mode->hact,
++                              mode->vact, true);
++      if (!mode) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      if (mode != sensor->current_mode ||
++              frame_rate != sensor->current_fr) {
++              sensor->current_fr = frame_rate;
++              sensor->frame_interval = fi->interval;
++              sensor->current_mode = mode;
++              sensor->pending_mode_change = true;
++
++              __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                                      ov4689_calc_pixel_rate(sensor));
++      }
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int ov4689_stream_start(struct ov4689_dev *sensor, int enable)
++{
++      return ov4689_write_reg(sensor, OV4689_REG_STREAM_ON, enable);
++}
++
++static int ov4689_s_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++      int ret = 0;
++
++      if (enable) {
++              pm_runtime_get_sync(&sensor->i2c_client->dev);
++
++              ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++              if (ret) {
++                      pm_runtime_put_sync(&sensor->i2c_client->dev);
++                      return ret;
++              }
++      }
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming == !enable) {
++              if (enable) {
++                      ret = ov4689_restore_mode(sensor);
++                      if (ret)
++                              goto out;
++              }
++
++              if (enable && sensor->pending_mode_change) {
++                      ret = ov4689_set_mode(sensor);
++                      if (ret)
++                              goto out;
++              }
++
++              if (sensor->ep.bus.mipi_csi2.num_data_lanes == 2) {
++                      ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_HI, 0x32);
++                      ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_LOW, 0x0c);
++              } else if (sensor->ep.bus.mipi_csi2.num_data_lanes == 4) {
++                      ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_HI, 0x72);
++                      ov4689_write_reg(sensor, OV4689_REG_MIPI_SC_CTRL_LOW, 0x00);
++              } else {
++                      dev_err(&sensor->i2c_client->dev, "Unsupport lane num\n");
++              }
++
++              ret = ov4689_stream_start(sensor, enable);
++              if (ret)
++                      goto out;
++      }
++      sensor->streaming += enable ? 1 : -1;
++      WARN_ON(sensor->streaming < 0);
++out:
++      mutex_unlock(&sensor->lock);
++
++      if (!enable || ret)
++              pm_runtime_put_sync(&sensor->i2c_client->dev);
++
++      return ret;
++}
++
++static const struct v4l2_subdev_core_ops ov4689_core_ops = {
++      .log_status = v4l2_ctrl_subdev_log_status,
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops ov4689_video_ops = {
++      .g_frame_interval = ov4689_g_frame_interval,
++      .s_frame_interval = ov4689_s_frame_interval,
++      .s_stream = ov4689_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
++      .enum_mbus_code = ov4689_enum_mbus_code,
++      .get_fmt = ov4689_get_fmt,
++      .set_fmt = ov4689_set_fmt,
++      .enum_frame_size = ov4689_enum_frame_size,
++      .enum_frame_interval = ov4689_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops ov4689_subdev_ops = {
++      .core = &ov4689_core_ops,
++      .video = &ov4689_video_ops,
++      .pad = &ov4689_pad_ops,
++};
++
++static int ov4689_get_regulators(struct ov4689_dev *sensor)
++{
++      int i;
++
++      for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
++              sensor->supplies[i].supply = ov4689_supply_name[i];
++
++      return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++                                      OV4689_NUM_SUPPLIES,
++                                      sensor->supplies);
++}
++
++static int ov4689_check_chip_id(struct ov4689_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      int ret = 0;
++      u16 chip_id;
++
++      ret = ov4689_read_reg16(sensor, OV4689_REG_CHIP_ID, &chip_id);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to read chip identifier\n",
++                      __func__);
++              return ret;
++      }
++
++      if (chip_id != OV4689_CHIP_ID) {
++              dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x,  got 0x%x\n",
++                      __func__, OV4689_CHIP_ID, chip_id);
++              return -ENXIO;
++      }
++      dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++              __func__, chip_id);
++
++      return 0;
++}
++
++static int ov4689_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct fwnode_handle *endpoint;
++      struct ov4689_dev *sensor;
++      struct v4l2_mbus_framefmt *fmt;
++      u32 rotation;
++      int ret;
++
++      sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++      if (!sensor)
++              return -ENOMEM;
++
++      sensor->i2c_client = client;
++
++      fmt = &sensor->fmt;
++      fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++      fmt->width = 1920;
++      fmt->height = 1080;
++      fmt->field = V4L2_FIELD_NONE;
++      sensor->frame_interval.numerator = 1;
++      sensor->frame_interval.denominator = ov4689_framerates[OV4689_30_FPS];
++      sensor->current_fr = OV4689_30_FPS;
++      sensor->current_mode =
++              &ov4689_mode_data[OV4689_MODE_1080P_1920_1080];
++      sensor->last_mode = sensor->current_mode;
++
++
++      /* optional indication of physical rotation of sensor */
++      ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++                                      &rotation);
++      if (!ret) {
++              switch (rotation) {
++              case 180:
++                      sensor->upside_down = true;
++                      fallthrough;
++              case 0:
++                      break;
++              default:
++                      dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++                              rotation);
++              }
++      }
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++                                              NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++      fwnode_handle_put(endpoint);
++      if (ret) {
++              dev_err(dev, "Could not parse endpoint\n");
++              return ret;
++      }
++
++      if (sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
++              dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++              return -EINVAL;
++      }
++
++      /* get system clock (xclk) */
++      sensor->xclk = devm_clk_get(dev, "xclk");
++      if (IS_ERR(sensor->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(sensor->xclk);
++      }
++
++      sensor->xclk_freq = clk_get_rate(sensor->xclk);
++      if (sensor->xclk_freq < OV4689_XCLK_MIN ||
++              sensor->xclk_freq > OV4689_XCLK_MAX) {
++              dev_err(dev, "xclk frequency out of range: %d Hz\n",
++                      sensor->xclk_freq);
++              return -EINVAL;
++      }
++
++      /* request optional power down pin */
++      sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++                                              GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->pwdn_gpio))
++              return PTR_ERR(sensor->pwdn_gpio);
++
++      /* request optional reset pin */
++      sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++                                              GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->reset_gpio))
++              return PTR_ERR(sensor->reset_gpio);
++
++      v4l2_i2c_subdev_init(&sensor->sd, client, &ov4689_subdev_ops);
++
++      sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++                      V4L2_SUBDEV_FL_HAS_EVENTS;
++      sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++      sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++      ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++      if (ret)
++              return ret;
++
++      ret = ov4689_get_regulators(sensor);
++      if (ret)
++              return ret;
++
++      mutex_init(&sensor->lock);
++
++      ret = ov4689_set_power_on(dev);
++      if (ret) {
++              dev_err(dev, "failed to power on\n");
++              goto entity_cleanup;
++      }
++
++      ret = ov4689_check_chip_id(sensor);
++      if (ret)
++              goto error_power_off;
++
++      ret = ov4689_init_controls(sensor);
++      if (ret)
++              goto error_power_off;
++
++      ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++      if (ret)
++              goto free_ctrls;
++
++      pm_runtime_set_active(dev);
++      pm_runtime_enable(dev);
++
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++error_power_off:
++      ov4689_set_power_off(dev);
++entity_cleanup:
++      media_entity_cleanup(&sensor->sd.entity);
++      mutex_destroy(&sensor->lock);
++      return ret;
++}
++
++static void ov4689_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct ov4689_dev *sensor = to_ov4689_dev(sd);
++
++      v4l2_async_unregister_subdev(&sensor->sd);
++      media_entity_cleanup(&sensor->sd.entity);
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++      mutex_destroy(&sensor->lock);
++
++      pm_runtime_disable(&client->dev);
++      if (!pm_runtime_status_suspended(&client->dev))
++              ov4689_set_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct i2c_device_id ov4689_id[] = {
++      { "ov4689", 0 },
++      {},
++};
++MODULE_DEVICE_TABLE(i2c, ov4689_id);
++
++static const struct of_device_id ov4689_dt_ids[] = {
++      { .compatible = "ovti,ov4689" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov4689_dt_ids);
++
++static const struct dev_pm_ops ov4689_pm_ops = {
++      SET_RUNTIME_PM_OPS(ov4689_set_power_off, ov4689_set_power_on, NULL)
++};
++
++static struct i2c_driver ov4689_i2c_driver = {
++      .driver = {
++              .name  = "ov4689",
++              .of_match_table = ov4689_dt_ids,
++              .pm = &ov4689_pm_ops,
++      },
++      .id_table = ov4689_id,
++      .probe = ov4689_probe,
++      .remove   = ov4689_remove,
++};
++
++module_i2c_driver(ov4689_i2c_driver);
++
++MODULE_DESCRIPTION("OV4689 MIPI Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/ov5640.c
+@@ -0,0 +1,3227 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
++ * Copyright (C) 2014-2017 Mentor Graphics Inc.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++/* min/typical/max system clock (xclk) frequencies */
++#define OV5640_XCLK_MIN  6000000
++#define OV5640_XCLK_MAX 54000000
++
++#define OV5640_SKIP_FRAMES      4
++
++#define OV5640_CHIP_ID                0x5640
++#define OV5640_DEFAULT_SLAVE_ID 0x3c
++
++#define OV5640_REG_SYS_RESET02                0x3002
++#define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006
++#define OV5640_REG_SYS_CTRL0          0x3008
++#define OV5640_REG_SYS_CTRL0_SW_PWDN  0x42
++#define OV5640_REG_SYS_CTRL0_SW_PWUP  0x02
++#define OV5640_REG_CHIP_ID            0x300a
++#define OV5640_REG_IO_MIPI_CTRL00     0x300e
++#define OV5640_REG_PAD_OUTPUT_ENABLE01        0x3017
++#define OV5640_REG_PAD_OUTPUT_ENABLE02        0x3018
++#define OV5640_REG_PAD_OUTPUT00               0x3019
++#define OV5640_REG_SYSTEM_CONTROL1    0x302e
++#define OV5640_REG_SC_PLL_CTRL0               0x3034
++#define OV5640_REG_SC_PLL_CTRL1               0x3035
++#define OV5640_REG_SC_PLL_CTRL2               0x3036
++#define OV5640_REG_SC_PLL_CTRL3               0x3037
++#define OV5640_REG_SLAVE_ID           0x3100
++#define OV5640_REG_SCCB_SYS_CTRL1     0x3103
++#define OV5640_REG_SYS_ROOT_DIVIDER   0x3108
++#define OV5640_REG_AWB_R_GAIN         0x3400
++#define OV5640_REG_AWB_G_GAIN         0x3402
++#define OV5640_REG_AWB_B_GAIN         0x3404
++#define OV5640_REG_AWB_MANUAL_CTRL    0x3406
++#define OV5640_REG_AEC_PK_EXPOSURE_HI 0x3500
++#define OV5640_REG_AEC_PK_EXPOSURE_MED        0x3501
++#define OV5640_REG_AEC_PK_EXPOSURE_LO 0x3502
++#define OV5640_REG_AEC_PK_MANUAL      0x3503
++#define OV5640_REG_AEC_PK_REAL_GAIN   0x350a
++#define OV5640_REG_AEC_PK_VTS         0x350c
++#define OV5640_REG_TIMING_DVPHO               0x3808
++#define OV5640_REG_TIMING_DVPVO               0x380a
++#define OV5640_REG_TIMING_HTS         0x380c
++#define OV5640_REG_TIMING_VTS         0x380e
++#define OV5640_REG_TIMING_TC_REG20    0x3820
++#define OV5640_REG_TIMING_TC_REG21    0x3821
++#define OV5640_REG_AEC_CTRL00         0x3a00
++#define OV5640_REG_AEC_B50_STEP               0x3a08
++#define OV5640_REG_AEC_B60_STEP               0x3a0a
++#define OV5640_REG_AEC_CTRL0D         0x3a0d
++#define OV5640_REG_AEC_CTRL0E         0x3a0e
++#define OV5640_REG_AEC_CTRL0F         0x3a0f
++#define OV5640_REG_AEC_CTRL10         0x3a10
++#define OV5640_REG_AEC_CTRL11         0x3a11
++#define OV5640_REG_AEC_CTRL1B         0x3a1b
++#define OV5640_REG_AEC_CTRL1E         0x3a1e
++#define OV5640_REG_AEC_CTRL1F         0x3a1f
++#define OV5640_REG_HZ5060_CTRL00      0x3c00
++#define OV5640_REG_HZ5060_CTRL01      0x3c01
++#define OV5640_REG_SIGMADELTA_CTRL0C  0x3c0c
++#define OV5640_REG_FRAME_CTRL01               0x4202
++#define OV5640_REG_FORMAT_CONTROL00   0x4300
++#define OV5640_REG_VFIFO_HSIZE                0x4602
++#define OV5640_REG_VFIFO_VSIZE                0x4604
++#define OV5640_REG_JPG_MODE_SELECT    0x4713
++#define OV5640_REG_CCIR656_CTRL00     0x4730
++#define OV5640_REG_POLARITY_CTRL00    0x4740
++#define OV5640_REG_MIPI_CTRL00                0x4800
++#define OV5640_REG_DEBUG_MODE         0x4814
++#define OV5640_REG_ISP_FORMAT_MUX_CTRL        0x501f
++#define OV5640_REG_PRE_ISP_TEST_SET1  0x503d
++#define OV5640_REG_SDE_CTRL0          0x5580
++#define OV5640_REG_SDE_CTRL1          0x5581
++#define OV5640_REG_SDE_CTRL3          0x5583
++#define OV5640_REG_SDE_CTRL4          0x5584
++#define OV5640_REG_SDE_CTRL5          0x5585
++#define OV5640_REG_AVG_READOUT                0x56a1
++
++enum ov5640_mode_id {
++      OV5640_MODE_QCIF_176_144 = 0,
++      OV5640_MODE_QVGA_320_240,
++      OV5640_MODE_VGA_640_480,
++      OV5640_MODE_NTSC_720_480,
++      OV5640_MODE_PAL_720_576,
++      OV5640_MODE_XGA_1024_768,
++      OV5640_MODE_720P_1280_720,
++      OV5640_MODE_1080P_1920_1080,
++      OV5640_MODE_QSXGA_2592_1944,
++      OV5640_NUM_MODES,
++};
++
++enum ov5640_frame_rate {
++      OV5640_15_FPS = 0,
++      OV5640_30_FPS,
++      OV5640_60_FPS,
++      OV5640_NUM_FRAMERATES,
++};
++
++enum ov5640_format_mux {
++      OV5640_FMT_MUX_YUV422 = 0,
++      OV5640_FMT_MUX_RGB,
++      OV5640_FMT_MUX_DITHER,
++      OV5640_FMT_MUX_RAW_DPC,
++      OV5640_FMT_MUX_SNR_RAW,
++      OV5640_FMT_MUX_RAW_CIP,
++};
++
++struct ov5640_pixfmt {
++      u32 code;
++      u32 colorspace;
++};
++
++static const struct ov5640_pixfmt ov5640_formats[] = {
++      { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
++      { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, },
++      { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, },
++};
++
++/*
++ * FIXME: remove this when a subdev API becomes available
++ * to set the MIPI CSI-2 virtual channel.
++ */
++static unsigned int virtual_channel;
++module_param(virtual_channel, uint, 0444);
++MODULE_PARM_DESC(virtual_channel,
++               "MIPI CSI-2 virtual channel (0..3), default 0");
++
++static const int ov5640_framerates[] = {
++      [OV5640_15_FPS] = 15,
++      [OV5640_30_FPS] = 30,
++      [OV5640_60_FPS] = 60,
++};
++
++/* regulator supplies */
++static const char * const ov5640_supply_name[] = {
++      "DOVDD", /* Digital I/O (1.8V) supply */
++      "AVDD",  /* Analog (2.8V) supply */
++      "DVDD",  /* Digital Core (1.5V) supply */
++};
++
++#define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)
++
++/*
++ * Image size under 1280 * 960 are SUBSAMPLING
++ * Image size upper 1280 * 960 are SCALING
++ */
++enum ov5640_downsize_mode {
++      SUBSAMPLING,
++      SCALING,
++};
++
++struct reg_value {
++      u16 reg_addr;
++      u8 val;
++      u8 mask;
++      u32 delay_ms;
++};
++
++struct ov5640_mode_info {
++      enum ov5640_mode_id id;
++      enum ov5640_downsize_mode dn_mode;
++      u32 hact;
++      u32 htot;
++      u32 vact;
++      u32 vtot;
++      const struct reg_value *reg_data;
++      u32 reg_data_size;
++      u32 max_fps;
++};
++
++struct ov5640_ctrls {
++      struct v4l2_ctrl_handler handler;
++      struct v4l2_ctrl *pixel_rate;
++      struct {
++              struct v4l2_ctrl *auto_exp;
++              struct v4l2_ctrl *exposure;
++      };
++      struct {
++              struct v4l2_ctrl *auto_wb;
++              struct v4l2_ctrl *blue_balance;
++              struct v4l2_ctrl *red_balance;
++      };
++      struct {
++              struct v4l2_ctrl *auto_gain;
++              struct v4l2_ctrl *gain;
++      };
++      struct v4l2_ctrl *brightness;
++      struct v4l2_ctrl *light_freq;
++      struct v4l2_ctrl *saturation;
++      struct v4l2_ctrl *contrast;
++      struct v4l2_ctrl *hue;
++      struct v4l2_ctrl *test_pattern;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vflip;
++};
++
++struct ov5640_dev {
++      struct i2c_client *i2c_client;
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++      struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++      struct clk *xclk; /* system clock to OV5640 */
++      u32 xclk_freq;
++
++      struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
++      struct gpio_desc *reset_gpio;
++      struct gpio_desc *pwdn_gpio;
++      bool   upside_down;
++
++      /* lock to protect all members below */
++      struct mutex lock;
++
++      int power_count;
++
++      struct v4l2_mbus_framefmt fmt;
++      bool pending_fmt_change;
++
++      const struct ov5640_mode_info *current_mode;
++      const struct ov5640_mode_info *last_mode;
++      enum ov5640_frame_rate current_fr;
++      struct v4l2_fract frame_interval;
++
++      struct ov5640_ctrls ctrls;
++
++      u32 prev_sysclk, prev_hts;
++      u32 ae_low, ae_high, ae_target;
++
++      bool pending_mode_change;
++      int streaming;
++};
++
++static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
++{
++      return container_of(sd, struct ov5640_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++      return &container_of(ctrl->handler, struct ov5640_dev,
++                           ctrls.handler)->sd;
++}
++
++/*
++ * FIXME: all of these register tables are likely filled with
++ * entries that set the register to their power-on default values,
++ * and which are otherwise not touched by this driver. Those entries
++ * should be identified and removed to speed register load time
++ * over i2c.
++ */
++/* YUV422 UYVY VGA@30fps */
++static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
++      {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
++      {0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
++      {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
++      {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
++      {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
++      {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
++      {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
++      {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
++      {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
++      {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
++      {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
++      {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
++      {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
++      {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
++      {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
++      {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0},
++      {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0},
++      {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
++      {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
++      {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
++      {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
++      {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
++      {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
++      {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
++      {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
++      {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
++      {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
++      {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
++      {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
++      {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
++      {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
++      {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
++      {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
++      {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
++      {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
++      {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
++      {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
++      {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
++      {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
++      {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
++      {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
++      {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
++      {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
++      {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
++      {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
++      {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
++      {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
++      {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
++      {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
++      {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
++      {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
++      {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
++      {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
++      {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
++      {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
++      {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
++      {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
++      {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
++      {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
++      {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
++      {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
++      {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
++      {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
++      {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
++      {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
++      {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
++      {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
++};
++
++static const struct reg_value ov5640_setting_VGA_640_480[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_XGA_1024_768[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_QVGA_320_240[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_QCIF_176_144[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_NTSC_720_480[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_PAL_720_576[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_720P_1280_720[] = {
++      {0x3c07, 0x07, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x31, 0, 0},
++      {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
++      {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
++      {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
++      {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
++      {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
++      {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x11, 0, 0},
++      {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
++      {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
++      {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
++      {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
++      {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
++      {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
++      {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
++      {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
++      {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
++      {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
++      {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
++      {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
++      {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
++      {0x4005, 0x1a, 0, 0},
++};
++
++static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
++      {0x3c07, 0x08, 0, 0},
++      {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
++      {0x3814, 0x11, 0, 0},
++      {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
++      {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
++      {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
++      {0x3810, 0x00, 0, 0},
++      {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
++      {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
++      {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
++      {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
++      {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
++      {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
++      {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
++      {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
++      {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 70},
++};
++
++/* power-on sensor init reg table */
++static const struct ov5640_mode_info ov5640_mode_init_data = {
++      0, SUBSAMPLING, 640, 1896, 480, 984,
++      ov5640_init_setting_30fps_VGA,
++      ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
++      OV5640_30_FPS,
++};
++
++static const struct ov5640_mode_info
++ov5640_mode_data[OV5640_NUM_MODES] = {
++      {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
++       176, 1896, 144, 984,
++       ov5640_setting_QCIF_176_144,
++       ARRAY_SIZE(ov5640_setting_QCIF_176_144),
++       OV5640_30_FPS},
++      {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
++       320, 1896, 240, 984,
++       ov5640_setting_QVGA_320_240,
++       ARRAY_SIZE(ov5640_setting_QVGA_320_240),
++       OV5640_30_FPS},
++      {OV5640_MODE_VGA_640_480, SUBSAMPLING,
++       640, 1896, 480, 1080,
++       ov5640_setting_VGA_640_480,
++       ARRAY_SIZE(ov5640_setting_VGA_640_480),
++       OV5640_60_FPS},
++      {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
++       720, 1896, 480, 984,
++       ov5640_setting_NTSC_720_480,
++       ARRAY_SIZE(ov5640_setting_NTSC_720_480),
++      OV5640_30_FPS},
++      {OV5640_MODE_PAL_720_576, SUBSAMPLING,
++       720, 1896, 576, 984,
++       ov5640_setting_PAL_720_576,
++       ARRAY_SIZE(ov5640_setting_PAL_720_576),
++       OV5640_30_FPS},
++      {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
++       1024, 1896, 768, 1080,
++       ov5640_setting_XGA_1024_768,
++       ARRAY_SIZE(ov5640_setting_XGA_1024_768),
++       OV5640_30_FPS},
++      {OV5640_MODE_720P_1280_720, SUBSAMPLING,
++       1280, 1892, 720, 740,
++       ov5640_setting_720P_1280_720,
++       ARRAY_SIZE(ov5640_setting_720P_1280_720),
++       OV5640_30_FPS},
++      {OV5640_MODE_1080P_1920_1080, SCALING,
++       1920, 2500, 1080, 1120,
++       ov5640_setting_1080P_1920_1080,
++       ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
++       OV5640_30_FPS},
++      {OV5640_MODE_QSXGA_2592_1944, SCALING,
++       2592, 2844, 1944, 1968,
++       ov5640_setting_QSXGA_2592_1944,
++       ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
++       OV5640_15_FPS},
++};
++
++static int ov5640_init_slave_id(struct ov5640_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg;
++      u8 buf[3];
++      int ret;
++
++      if (client->addr == OV5640_DEFAULT_SLAVE_ID)
++              return 0;
++
++      buf[0] = OV5640_REG_SLAVE_ID >> 8;
++      buf[1] = OV5640_REG_SLAVE_ID & 0xff;
++      buf[2] = client->addr << 1;
++
++      msg.addr = OV5640_DEFAULT_SLAVE_ID;
++      msg.flags = 0;
++      msg.buf = buf;
++      msg.len = sizeof(buf);
++
++      ret = i2c_transfer(client->adapter, &msg, 1);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: failed with %d\n", __func__, ret);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg;
++      u8 buf[3];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++      buf[2] = val;
++
++      msg.addr = client->addr;
++      msg.flags = client->flags;
++      msg.buf = buf;
++      msg.len = sizeof(buf);
++
++      ret = i2c_transfer(client->adapter, &msg, 1);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++                      __func__, reg, val);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg[2];
++      u8 buf[2];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++
++      msg[0].addr = client->addr;
++      msg[0].flags = client->flags;
++      msg[0].buf = buf;
++      msg[0].len = sizeof(buf);
++
++      msg[1].addr = client->addr;
++      msg[1].flags = client->flags | I2C_M_RD;
++      msg[1].buf = buf;
++      msg[1].len = 1;
++
++      ret = i2c_transfer(client->adapter, msg, 2);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x\n",
++                      __func__, reg);
++              return ret;
++      }
++
++      *val = buf[0];
++      return 0;
++}
++
++static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
++{
++      u8 hi, lo;
++      int ret;
++
++      ret = ov5640_read_reg(sensor, reg, &hi);
++      if (ret)
++              return ret;
++      ret = ov5640_read_reg(sensor, reg + 1, &lo);
++      if (ret)
++              return ret;
++
++      *val = ((u16)hi << 8) | (u16)lo;
++      return 0;
++}
++
++static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
++{
++      int ret;
++
++      ret = ov5640_write_reg(sensor, reg, val >> 8);
++      if (ret)
++              return ret;
++
++      return ov5640_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
++                        u8 mask, u8 val)
++{
++      u8 readval;
++      int ret;
++
++      ret = ov5640_read_reg(sensor, reg, &readval);
++      if (ret)
++              return ret;
++
++      readval &= ~mask;
++      val &= mask;
++      val |= readval;
++
++      return ov5640_write_reg(sensor, reg, val);
++}
++
++/*
++ * After trying the various combinations, reading various
++ * documentations spread around the net, and from the various
++ * feedback, the clock tree is probably as follows:
++ *
++ *   +--------------+
++ *   |  Ext. Clock  |
++ *   +-+------------+
++ *     |  +----------+
++ *     +->|   PLL1   | - reg 0x3036, for the multiplier
++ *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
++ *          |  +--------------+
++ *          +->| System Clock |  - reg 0x3035, bits 4-7
++ *             +-+------------+
++ *               |  +--------------+
++ *               +->| MIPI Divider | - reg 0x3035, bits 0-3
++ *               |  +-+------------+
++ *               |    +----------------> MIPI SCLK
++ *               |    +  +-----+
++ *               |    +->| / 2 |-------> MIPI BIT CLK
++ *               |       +-----+
++ *               |  +--------------+
++ *               +->| PLL Root Div | - reg 0x3037, bit 4
++ *                  +-+------------+
++ *                    |  +---------+
++ *                    +->| Bit Div | - reg 0x3034, bits 0-3
++ *                       +-+-------+
++ *                         |  +-------------+
++ *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
++ *                         |  +-+-----------+
++ *                         |    +---------------> SCLK
++ *                         |  +-------------+
++ *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
++ *                         |  +-+-----------+
++ *                         |    +---------------> SCLK 2X
++ *                         |  +-------------+
++ *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
++ *                            ++------------+
++ *                             +  +-----------+
++ *                             +->|   P_DIV   | - reg 0x3035, bits 0-3
++ *                                +-----+-----+
++ *                                       +------------> PCLK
++ *
++ * This is deviating from the datasheet at least for the register
++ * 0x3108, since it's said here that the PCLK would be clocked from
++ * the PLL.
++ *
++ * There seems to be also (unverified) constraints:
++ *  - the PLL pre-divider output rate should be in the 4-27MHz range
++ *  - the PLL multiplier output rate should be in the 500-1000MHz range
++ *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
++ *
++ * In the two latter cases, these constraints are met since our
++ * factors are hardcoded. If we were to change that, we would need to
++ * take this into account. The only varying parts are the PLL
++ * multiplier and the system clock divider, which are shared between
++ * all these clocks so won't cause any issue.
++ */
++
++/*
++ * This is supposed to be ranging from 1 to 8, but the value is always
++ * set to 3 in the vendor kernels.
++ */
++#define OV5640_PLL_PREDIV     3
++
++#define OV5640_PLL_MULT_MIN   4
++#define OV5640_PLL_MULT_MAX   252
++
++/*
++ * This is supposed to be ranging from 1 to 16, but the value is
++ * always set to either 1 or 2 in the vendor kernels.
++ */
++#define OV5640_SYSDIV_MIN     1
++#define OV5640_SYSDIV_MAX     16
++
++/*
++ * Hardcode these values for scaler and non-scaler modes.
++ * FIXME: to be re-calcualted for 1 data lanes setups
++ */
++#define OV5640_MIPI_DIV_PCLK  2
++#define OV5640_MIPI_DIV_SCLK  1
++
++/*
++ * This is supposed to be ranging from 1 to 2, but the value is always
++ * set to 2 in the vendor kernels.
++ */
++#define OV5640_PLL_ROOT_DIV                   2
++#define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2               BIT(4)
++
++/*
++ * We only supports 8-bit formats at the moment
++ */
++#define OV5640_BIT_DIV                                2
++#define OV5640_PLL_CTRL0_MIPI_MODE_8BIT               0x08
++
++/*
++ * This is supposed to be ranging from 1 to 8, but the value is always
++ * set to 2 in the vendor kernels.
++ */
++#define OV5640_SCLK_ROOT_DIV  2
++
++/*
++ * This is hardcoded so that the consistency is maintained between SCLK and
++ * SCLK 2x.
++ */
++#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
++
++/*
++ * This is supposed to be ranging from 1 to 8, but the value is always
++ * set to 1 in the vendor kernels.
++ */
++#define OV5640_PCLK_ROOT_DIV                  1
++#define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS    0x00
++
++static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
++                                          u8 pll_prediv, u8 pll_mult,
++                                          u8 sysdiv)
++{
++      unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult;
++
++      /* PLL1 output cannot exceed 1GHz. */
++      if (sysclk / 1000000 > 1000)
++              return 0;
++
++      return sysclk / sysdiv;
++}
++
++static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
++                                       unsigned long rate,
++                                       u8 *pll_prediv, u8 *pll_mult,
++                                       u8 *sysdiv)
++{
++      unsigned long best = ~0;
++      u8 best_sysdiv = 1, best_mult = 1;
++      u8 _sysdiv, _pll_mult;
++
++      for (_sysdiv = OV5640_SYSDIV_MIN;
++           _sysdiv <= OV5640_SYSDIV_MAX;
++           _sysdiv++) {
++              for (_pll_mult = OV5640_PLL_MULT_MIN;
++                   _pll_mult <= OV5640_PLL_MULT_MAX;
++                   _pll_mult++) {
++                      unsigned long _rate;
++
++                      /*
++                       * The PLL multiplier cannot be odd if above
++                       * 127.
++                       */
++                      if (_pll_mult > 127 && (_pll_mult % 2))
++                              continue;
++
++                      _rate = ov5640_compute_sys_clk(sensor,
++                                                     OV5640_PLL_PREDIV,
++                                                     _pll_mult, _sysdiv);
++
++                      /*
++                       * We have reached the maximum allowed PLL1 output,
++                       * increase sysdiv.
++                       */
++                      if (!_rate)
++                              break;
++
++                      /*
++                       * Prefer rates above the expected clock rate than
++                       * below, even if that means being less precise.
++                       */
++                      if (_rate < rate)
++                              continue;
++
++                      if (abs(rate - _rate) < abs(rate - best)) {
++                              best = _rate;
++                              best_sysdiv = _sysdiv;
++                              best_mult = _pll_mult;
++                      }
++
++                      if (_rate == rate)
++                              goto out;
++              }
++      }
++
++out:
++      *sysdiv = best_sysdiv;
++      *pll_prediv = OV5640_PLL_PREDIV;
++      *pll_mult = best_mult;
++
++      return best;
++}
++
++/*
++ * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
++ *                        for the MIPI CSI-2 output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ *      'Bandwidth Per Lane' is calculated as:
++ *      bpl = HTOT * VTOT * FPS * bpp / num_lanes;
++ *
++ * This function use the requested bandwidth to calculate:
++ * - sample_rate = bpl / (bpp / num_lanes);
++ *             = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
++ *
++ * - mipi_sclk   = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
++ *
++ * with these fixed parameters:
++ *    PLL_RDIV        = 2;
++ *    BIT_DIVIDER     = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
++ *    PCLK_DIV        = 1;
++ *
++ * The MIPI clock generation differs for modes that use the scaler and modes
++ * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
++ * BIT CLk, and thus:
++ *
++ * - mipi_sclk = bpl / MIPI_DIV / 2;
++ *   MIPI_DIV = 1;
++ *
++ * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
++ * from the pixel clock, and thus:
++ *
++ * - sample_rate = bpl / (bpp / num_lanes);
++ *             = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
++ *             = bpl / (4 * MIPI_DIV / num_lanes);
++ * - MIPI_DIV  = bpp / (4 * num_lanes);
++ *
++ * FIXME: this have been tested with 16bpp and 2 lanes setup only.
++ * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
++ * above formula for setups with 1 lane or image formats with different bpp.
++ *
++ * FIXME: this deviates from the sensor manual documentation which is quite
++ * thin on the MIPI clock tree generation part.
++ */
++static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
++                              unsigned long rate)
++{
++      const struct ov5640_mode_info *mode = sensor->current_mode;
++      u8 prediv, mult, sysdiv;
++      u8 mipi_div;
++      int ret;
++
++      /*
++       * 1280x720 is reported to use 'SUBSAMPLING' only,
++       * but according to the sensor manual it goes through the
++       * scaler before subsampling.
++       */
++      if (mode->dn_mode == SCALING ||
++         (mode->id == OV5640_MODE_720P_1280_720))
++              mipi_div = OV5640_MIPI_DIV_SCLK;
++      else
++              mipi_div = OV5640_MIPI_DIV_PCLK;
++
++      ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
++                           0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
++                           0xff, sysdiv << 4 | mipi_div);
++      if (ret)
++              return ret;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
++      if (ret)
++              return ret;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
++                           0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
++      if (ret)
++              return ret;
++
++      return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
++                            0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
++}
++
++static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
++                                    unsigned long rate,
++                                    u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
++                                    u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
++{
++      unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
++                              OV5640_PCLK_ROOT_DIV;
++
++      _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
++                                  sysdiv);
++      *pll_rdiv = OV5640_PLL_ROOT_DIV;
++      *bit_div = OV5640_BIT_DIV;
++      *pclk_div = OV5640_PCLK_ROOT_DIV;
++
++      return _rate / *pll_rdiv / *bit_div / *pclk_div;
++}
++
++static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
++{
++      u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
++      int ret;
++
++      ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
++                       &bit_div, &pclk_div);
++
++#ifndef CONFIG_VIN_SENSOR_OV5640
++      if (bit_div == 2)
++              bit_div = 8;
++#else
++      bit_div = 0xa;
++#endif
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
++                           0x0f, bit_div);
++      if (ret)
++              return ret;
++
++      /*
++       * We need to set sysdiv according to the clock, and to clear
++       * the MIPI divider.
++       */
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
++                           0xff, sysdiv << 4);
++      if (ret)
++              return ret;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
++                           0xff, mult);
++      if (ret)
++              return ret;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
++                           0x1f, prediv | ((pll_rdiv - 1) << 4));
++      if (ret)
++              return ret;
++
++      return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
++                            (ilog2(pclk_div) << 4));
++}
++
++/* set JPEG framing sizes */
++static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
++                                 const struct ov5640_mode_info *mode)
++{
++      int ret;
++
++      /*
++       * compression mode 3 timing
++       *
++       * Data is transmitted with programmable width (VFIFO_HSIZE).
++       * No padding done. Last line may have less data. Varying
++       * number of lines per frame, depending on amount of data.
++       */
++      ret = ov5640_mod_reg(sensor, OV5640_REG_JPG_MODE_SELECT, 0x7, 0x3);
++      if (ret < 0)
++              return ret;
++
++      ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact);
++      if (ret < 0)
++              return ret;
++
++      return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
++}
++
++/* download ov5640 settings to sensor through i2c */
++static int ov5640_set_timings(struct ov5640_dev *sensor,
++                            const struct ov5640_mode_info *mode)
++{
++      int ret;
++
++      if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
++              ret = ov5640_set_jpeg_timings(sensor, mode);
++              if (ret < 0)
++                      return ret;
++      }
++
++      ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
++      if (ret < 0)
++              return ret;
++
++      ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
++      if (ret < 0)
++              return ret;
++
++      ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
++      if (ret < 0)
++              return ret;
++
++      return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
++}
++
++static int ov5640_load_regs(struct ov5640_dev *sensor,
++                          const struct ov5640_mode_info *mode)
++{
++      const struct reg_value *regs = mode->reg_data;
++      unsigned int i;
++      u32 delay_ms;
++      u16 reg_addr;
++      u8 mask, val;
++      int ret = 0;
++
++      st_info(ST_SENSOR, "%s, mode = 0x%x\n", __func__, mode->id);
++      for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++              delay_ms = regs->delay_ms;
++              reg_addr = regs->reg_addr;
++              val = regs->val;
++              mask = regs->mask;
++
++              /* remain in power down mode for DVP */
++              if (regs->reg_addr == OV5640_REG_SYS_CTRL0 &&
++                  val == OV5640_REG_SYS_CTRL0_SW_PWUP &&
++                  sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY)
++                      continue;
++
++              if (mask)
++                      ret = ov5640_mod_reg(sensor, reg_addr, mask, val);
++              else
++                      ret = ov5640_write_reg(sensor, reg_addr, val);
++              if (ret)
++                      break;
++
++              if (delay_ms)
++                      usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++      }
++
++      return ov5640_set_timings(sensor, mode);
++}
++
++static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
++{
++      return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
++                            BIT(0), on ? 0 : BIT(0));
++}
++
++/* read exposure, in number of line periods */
++static int ov5640_get_exposure(struct ov5640_dev *sensor)
++{
++      int exp, ret;
++      u8 temp;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_HI, &temp);
++      if (ret)
++              return ret;
++      exp = ((int)temp & 0x0f) << 16;
++      ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_MED, &temp);
++      if (ret)
++              return ret;
++      exp |= ((int)temp << 8);
++      ret = ov5640_read_reg(sensor, OV5640_REG_AEC_PK_EXPOSURE_LO, &temp);
++      if (ret)
++              return ret;
++      exp |= (int)temp;
++
++      return exp >> 4;
++}
++
++/* write exposure, given number of line periods */
++static int ov5640_set_exposure(struct ov5640_dev *sensor, u32 exposure)
++{
++      int ret;
++
++      exposure <<= 4;
++
++      ret = ov5640_write_reg(sensor,
++                             OV5640_REG_AEC_PK_EXPOSURE_LO,
++                             exposure & 0xff);
++      if (ret)
++              return ret;
++      ret = ov5640_write_reg(sensor,
++                             OV5640_REG_AEC_PK_EXPOSURE_MED,
++                             (exposure >> 8) & 0xff);
++      if (ret)
++              return ret;
++      return ov5640_write_reg(sensor,
++                              OV5640_REG_AEC_PK_EXPOSURE_HI,
++                              (exposure >> 16) & 0x0f);
++}
++
++static int ov5640_get_gain(struct ov5640_dev *sensor)
++{
++      u16 gain;
++      int ret;
++
++      ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, &gain);
++      if (ret)
++              return ret;
++
++      return gain & 0x3ff;
++}
++
++static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
++{
++      return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
++                                (u16)gain & 0x3ff);
++}
++
++static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
++{
++      return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
++                            BIT(1), on ? 0 : BIT(1));
++}
++
++static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
++{
++      return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ?
++                              OV5640_REG_SYS_CTRL0_SW_PWUP :
++                              OV5640_REG_SYS_CTRL0_SW_PWDN);
++}
++
++static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on)
++{
++      int ret;
++
++      /*
++       * Enable/disable the MIPI interface
++       *
++       * 0x300e = on ? 0x45 : 0x40
++       *
++       * FIXME: the sensor manual (version 2.03) reports
++       * [7:5] = 000  : 1 data lane mode
++       * [7:5] = 001  : 2 data lanes mode
++       * But this settings do not work, while the following ones
++       * have been validated for 2 data lanes mode.
++       *
++       * [7:5] = 010  : 2 data lanes mode
++       * [4] = 0      : Power up MIPI HS Tx
++       * [3] = 0      : Power up MIPI LS Rx
++       * [2] = 1/0    : MIPI interface enable/disable
++       * [1:0] = 01/00: FIXME: 'debug'
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00,
++                             on ? 0x45 : 0x40);
++      if (ret)
++              return ret;
++
++      return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
++                              on ? 0x00 : 0x0f);
++}
++
++static int ov5640_get_sysclk(struct ov5640_dev *sensor)
++{
++       /* calculate sysclk */
++      u32 xvclk = sensor->xclk_freq / 10000;
++      u32 multiplier, prediv, VCO, sysdiv, pll_rdiv;
++      u32 sclk_rdiv_map[] = {1, 2, 4, 8};
++      u32 bit_div2x = 1, sclk_rdiv, sysclk;
++      u8 temp1, temp2;
++      int ret;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL0, &temp1);
++      if (ret)
++              return ret;
++      temp2 = temp1 & 0x0f;
++      if (temp2 == 8 || temp2 == 10)
++              bit_div2x = temp2 / 2;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL1, &temp1);
++      if (ret)
++              return ret;
++      sysdiv = temp1 >> 4;
++      if (sysdiv == 0)
++              sysdiv = 16;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL2, &temp1);
++      if (ret)
++              return ret;
++      multiplier = temp1;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_SC_PLL_CTRL3, &temp1);
++      if (ret)
++              return ret;
++      prediv = temp1 & 0x0f;
++      pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, &temp1);
++      if (ret)
++              return ret;
++      temp2 = temp1 & 0x03;
++      sclk_rdiv = sclk_rdiv_map[temp2];
++
++      if (!prediv || !sysdiv || !pll_rdiv || !bit_div2x)
++              return -EINVAL;
++
++      VCO = xvclk * multiplier / prediv;
++
++      sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
++
++      return sysclk;
++}
++
++static int ov5640_set_night_mode(struct ov5640_dev *sensor)
++{
++       /* read HTS from register settings */
++      u8 mode;
++      int ret;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_AEC_CTRL00, &mode);
++      if (ret)
++              return ret;
++      mode &= 0xfb;
++      return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL00, mode);
++}
++
++static int ov5640_get_hts(struct ov5640_dev *sensor)
++{
++      /* read HTS from register settings */
++      u16 hts;
++      int ret;
++
++      ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_HTS, &hts);
++      if (ret)
++              return ret;
++      return hts;
++}
++
++static int ov5640_get_vts(struct ov5640_dev *sensor)
++{
++      u16 vts;
++      int ret;
++
++      ret = ov5640_read_reg16(sensor, OV5640_REG_TIMING_VTS, &vts);
++      if (ret)
++              return ret;
++      return vts;
++}
++
++static int ov5640_set_vts(struct ov5640_dev *sensor, int vts)
++{
++      return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, vts);
++}
++
++static int ov5640_get_light_freq(struct ov5640_dev *sensor)
++{
++      /* get banding filter value */
++      int ret, light_freq = 0;
++      u8 temp, temp1;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL01, &temp);
++      if (ret)
++              return ret;
++
++      if (temp & 0x80) {
++              /* manual */
++              ret = ov5640_read_reg(sensor, OV5640_REG_HZ5060_CTRL00,
++                                    &temp1);
++              if (ret)
++                      return ret;
++              if (temp1 & 0x04) {
++                      /* 50Hz */
++                      light_freq = 50;
++              } else {
++                      /* 60Hz */
++                      light_freq = 60;
++              }
++      } else {
++              /* auto */
++              ret = ov5640_read_reg(sensor, OV5640_REG_SIGMADELTA_CTRL0C,
++                                    &temp1);
++              if (ret)
++                      return ret;
++
++              if (temp1 & 0x01) {
++                      /* 50Hz */
++                      light_freq = 50;
++              } else {
++                      /* 60Hz */
++              }
++      }
++
++      return light_freq;
++}
++
++static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
++{
++      u32 band_step60, max_band60, band_step50, max_band50, prev_vts;
++      int ret;
++
++      /* read preview PCLK */
++      ret = ov5640_get_sysclk(sensor);
++      if (ret < 0)
++              return ret;
++      if (ret == 0)
++              return -EINVAL;
++      sensor->prev_sysclk = ret;
++      /* read preview HTS */
++      ret = ov5640_get_hts(sensor);
++      if (ret < 0)
++              return ret;
++      if (ret == 0)
++              return -EINVAL;
++      sensor->prev_hts = ret;
++
++      /* read preview VTS */
++      ret = ov5640_get_vts(sensor);
++      if (ret < 0)
++              return ret;
++      prev_vts = ret;
++
++      /* calculate banding filter */
++      /* 60Hz */
++      band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
++      ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B60_STEP, band_step60);
++      if (ret)
++              return ret;
++      if (!band_step60)
++              return -EINVAL;
++      max_band60 = (int)((prev_vts - 4) / band_step60);
++      ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0D, max_band60);
++      if (ret)
++              return ret;
++
++      /* 50Hz */
++      band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
++      ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_B50_STEP, band_step50);
++      if (ret)
++              return ret;
++      if (!band_step50)
++              return -EINVAL;
++      max_band50 = (int)((prev_vts - 4) / band_step50);
++      return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0E, max_band50);
++}
++
++static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target)
++{
++      /* stable in high */
++      u32 fast_high, fast_low;
++      int ret;
++
++      sensor->ae_low = target * 23 / 25;      /* 0.92 */
++      sensor->ae_high = target * 27 / 25;     /* 1.08 */
++
++      fast_high = sensor->ae_high << 1;
++      if (fast_high > 255)
++              fast_high = 255;
++
++      fast_low = sensor->ae_low >> 1;
++
++      ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL0F, sensor->ae_high);
++      if (ret)
++              return ret;
++      ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL10, sensor->ae_low);
++      if (ret)
++              return ret;
++      ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1B, sensor->ae_high);
++      if (ret)
++              return ret;
++      ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1E, sensor->ae_low);
++      if (ret)
++              return ret;
++      ret = ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL11, fast_high);
++      if (ret)
++              return ret;
++      return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
++}
++
++static int ov5640_get_binning(struct ov5640_dev *sensor)
++{
++      u8 temp;
++      int ret;
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp);
++      if (ret)
++              return ret;
++
++      return temp & BIT(0);
++}
++
++static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
++{
++      int ret;
++
++      /*
++       * TIMING TC REG21:
++       * - [0]:       Horizontal binning enable
++       */
++      ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
++                           BIT(0), enable ? BIT(0) : 0);
++      if (ret)
++              return ret;
++      /*
++       * TIMING TC REG20:
++       * - [0]:       Undocumented, but hardcoded init sequences
++       *              are always setting REG21/REG20 bit 0 to same value...
++       */
++      return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
++                            BIT(0), enable ? BIT(0) : 0);
++}
++
++static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      u8 temp, channel = virtual_channel;
++      int ret;
++
++      if (channel > 3) {
++              dev_err(&client->dev,
++                      "%s: wrong virtual_channel parameter, expected (0..3), got %d\n",
++                      __func__, channel);
++              return -EINVAL;
++      }
++
++      ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
++      if (ret)
++              return ret;
++      temp &= ~(3 << 6);
++      temp |= (channel << 6);
++      return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
++}
++
++static const struct ov5640_mode_info *
++ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
++               int width, int height, bool nearest)
++{
++      const struct ov5640_mode_info *mode;
++
++      mode = v4l2_find_nearest_size(ov5640_mode_data,
++                                    ARRAY_SIZE(ov5640_mode_data),
++                                    hact, vact,
++                                    width, height);
++
++      if (!mode ||
++          (!nearest && (mode->hact != width || mode->vact != height)))
++              return NULL;
++
++      /* Check to see if the current mode exceeds the max frame rate */
++      if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps])
++              return NULL;
++
++      return mode;
++}
++
++static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
++{
++      u64 rate;
++
++      rate = sensor->current_mode->vtot * sensor->current_mode->htot;
++      rate *= ov5640_framerates[sensor->current_fr];
++
++      return rate;
++}
++
++/*
++ * sensor changes between scaling and subsampling, go through
++ * exposure calculation
++ */
++static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
++                                       const struct ov5640_mode_info *mode)
++{
++      u32 prev_shutter, prev_gain16;
++      u32 cap_shutter, cap_gain16;
++      u32 cap_sysclk, cap_hts, cap_vts;
++      u32 light_freq, cap_bandfilt, cap_maxband;
++      u32 cap_gain16_shutter;
++      u8 average;
++      int ret;
++
++      if (!mode->reg_data)
++              return -EINVAL;
++
++      /* read preview shutter */
++      ret = ov5640_get_exposure(sensor);
++      if (ret < 0)
++              return ret;
++      prev_shutter = ret;
++      ret = ov5640_get_binning(sensor);
++      if (ret < 0)
++              return ret;
++      if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
++          mode->id != OV5640_MODE_1080P_1920_1080)
++              prev_shutter *= 2;
++
++      /* read preview gain */
++      ret = ov5640_get_gain(sensor);
++      if (ret < 0)
++              return ret;
++      prev_gain16 = ret;
++
++      /* get average */
++      ret = ov5640_read_reg(sensor, OV5640_REG_AVG_READOUT, &average);
++      if (ret)
++              return ret;
++
++      /* turn off night mode for capture */
++      ret = ov5640_set_night_mode(sensor);
++      if (ret < 0)
++              return ret;
++
++      /* Write capture setting */
++      ret = ov5640_load_regs(sensor, mode);
++      if (ret < 0)
++              return ret;
++
++      /* read capture VTS */
++      ret = ov5640_get_vts(sensor);
++      if (ret < 0)
++              return ret;
++      cap_vts = ret;
++      ret = ov5640_get_hts(sensor);
++      if (ret < 0)
++              return ret;
++      if (ret == 0)
++              return -EINVAL;
++      cap_hts = ret;
++
++      ret = ov5640_get_sysclk(sensor);
++      if (ret < 0)
++              return ret;
++      if (ret == 0)
++              return -EINVAL;
++      cap_sysclk = ret;
++
++      /* calculate capture banding filter */
++      ret = ov5640_get_light_freq(sensor);
++      if (ret < 0)
++              return ret;
++      light_freq = ret;
++
++      if (light_freq == 60) {
++              /* 60Hz */
++              cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
++      } else {
++              /* 50Hz */
++              cap_bandfilt = cap_sysclk * 100 / cap_hts;
++      }
++
++      if (!sensor->prev_sysclk) {
++              ret = ov5640_get_sysclk(sensor);
++              if (ret < 0)
++                      return ret;
++              if (ret == 0)
++                      return -EINVAL;
++              sensor->prev_sysclk = ret;
++      }
++
++      if (!cap_bandfilt)
++              return -EINVAL;
++
++      cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
++
++      /* calculate capture shutter/gain16 */
++      if (average > sensor->ae_low && average < sensor->ae_high) {
++              /* in stable range */
++              cap_gain16_shutter =
++                      prev_gain16 * prev_shutter *
++                      cap_sysclk / sensor->prev_sysclk *
++                      sensor->prev_hts / cap_hts *
++                      sensor->ae_target / average;
++      } else {
++              cap_gain16_shutter =
++                      prev_gain16 * prev_shutter *
++                      cap_sysclk / sensor->prev_sysclk *
++                      sensor->prev_hts / cap_hts;
++      }
++
++      /* gain to shutter */
++      if (cap_gain16_shutter < (cap_bandfilt * 16)) {
++              /* shutter < 1/100 */
++              cap_shutter = cap_gain16_shutter / 16;
++              if (cap_shutter < 1)
++                      cap_shutter = 1;
++
++              cap_gain16 = cap_gain16_shutter / cap_shutter;
++              if (cap_gain16 < 16)
++                      cap_gain16 = 16;
++      } else {
++              if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
++                      /* exposure reach max */
++                      cap_shutter = cap_bandfilt * cap_maxband;
++                      if (!cap_shutter)
++                              return -EINVAL;
++
++                      cap_gain16 = cap_gain16_shutter / cap_shutter;
++              } else {
++                      /* 1/100 < (cap_shutter = n/100) =< max */
++                      cap_shutter =
++                              ((int)(cap_gain16_shutter / 16 / cap_bandfilt))
++                              * cap_bandfilt;
++                      if (!cap_shutter)
++                              return -EINVAL;
++
++                      cap_gain16 = cap_gain16_shutter / cap_shutter;
++              }
++      }
++
++      /* set capture gain */
++      ret = ov5640_set_gain(sensor, cap_gain16);
++      if (ret)
++              return ret;
++
++      /* write capture shutter */
++      if (cap_shutter > (cap_vts - 4)) {
++              cap_vts = cap_shutter + 4;
++              ret = ov5640_set_vts(sensor, cap_vts);
++              if (ret < 0)
++                      return ret;
++      }
++
++      /* set exposure */
++      return ov5640_set_exposure(sensor, cap_shutter);
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
++                                const struct ov5640_mode_info *mode)
++{
++      if (!mode->reg_data)
++              return -EINVAL;
++
++      /* Write capture setting */
++      return ov5640_load_regs(sensor, mode);
++}
++
++static int ov5640_set_mode(struct ov5640_dev *sensor)
++{
++      const struct ov5640_mode_info *mode = sensor->current_mode;
++      const struct ov5640_mode_info *orig_mode = sensor->last_mode;
++      enum ov5640_downsize_mode dn_mode, orig_dn_mode;
++      bool auto_gain = sensor->ctrls.auto_gain->val == 1;
++      bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
++      unsigned long rate;
++      int ret;
++
++      dn_mode = mode->dn_mode;
++      orig_dn_mode = orig_mode->dn_mode;
++
++      /* auto gain and exposure must be turned off when changing modes */
++      if (auto_gain) {
++              ret = ov5640_set_autogain(sensor, false);
++              if (ret)
++                      return ret;
++      }
++
++      if (auto_exp) {
++              ret = ov5640_set_autoexposure(sensor, false);
++              if (ret)
++                      goto restore_auto_gain;
++      }
++
++      /*
++       * All the formats we support have 16 bits per pixel, seems to require
++       * the same rate than YUV, so we can just use 16 bpp all the time.
++       */
++      rate = ov5640_calc_pixel_rate(sensor) * 16;
++      if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
++              rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
++              ret = ov5640_set_mipi_pclk(sensor, rate);
++      } else {
++              rate = rate / sensor->ep.bus.parallel.bus_width;
++              ret = ov5640_set_dvp_pclk(sensor, rate);
++      }
++
++      if (ret < 0)
++              return 0;
++
++      if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
++          (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
++              /*
++               * change between subsampling and scaling
++               * go through exposure calculation
++               */
++              ret = ov5640_set_mode_exposure_calc(sensor, mode);
++      } else {
++              /*
++               * change inside subsampling or scaling
++               * download firmware directly
++               */
++              ret = ov5640_set_mode_direct(sensor, mode);
++      }
++      if (ret < 0)
++              goto restore_auto_exp_gain;
++
++      /* restore auto gain and exposure */
++      if (auto_gain)
++              ov5640_set_autogain(sensor, true);
++      if (auto_exp)
++              ov5640_set_autoexposure(sensor, true);
++
++      ret = ov5640_set_binning(sensor, dn_mode != SCALING);
++      if (ret < 0)
++              return ret;
++      ret = ov5640_set_ae_target(sensor, sensor->ae_target);
++      if (ret < 0)
++              return ret;
++      ret = ov5640_get_light_freq(sensor);
++      if (ret < 0)
++              return ret;
++      ret = ov5640_set_bandingfilter(sensor);
++      if (ret < 0)
++              return ret;
++      ret = ov5640_set_virtual_channel(sensor);
++      if (ret < 0)
++              return ret;
++
++      sensor->pending_mode_change = false;
++      sensor->last_mode = mode;
++
++      return 0;
++
++restore_auto_exp_gain:
++      if (auto_exp)
++              ov5640_set_autoexposure(sensor, true);
++restore_auto_gain:
++      if (auto_gain)
++              ov5640_set_autogain(sensor, true);
++
++      return ret;
++}
++
++static int ov5640_set_framefmt(struct ov5640_dev *sensor,
++                             struct v4l2_mbus_framefmt *format);
++
++/* restore the last set video mode after chip power-on */
++static int ov5640_restore_mode(struct ov5640_dev *sensor)
++{
++      int ret;
++
++      /* first load the initial register values */
++      ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
++      if (ret < 0)
++              return ret;
++      sensor->last_mode = &ov5640_mode_init_data;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
++                           (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
++                           ilog2(OV5640_SCLK_ROOT_DIV));
++      if (ret)
++              return ret;
++
++      /* now restore the last capture mode */
++      ret = ov5640_set_mode(sensor);
++      if (ret < 0)
++              return ret;
++
++      return ov5640_set_framefmt(sensor, &sensor->fmt);
++}
++
++static void ov5640_power(struct ov5640_dev *sensor, bool enable)
++{
++      if (!sensor->pwdn_gpio)
++              return;
++      gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
++}
++
++static void ov5640_reset(struct ov5640_dev *sensor)
++{
++      if (!sensor->reset_gpio)
++              return;
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++
++      /* camera power cycle */
++      ov5640_power(sensor, false);
++      usleep_range(5000, 10000);
++      ov5640_power(sensor, true);
++      usleep_range(5000, 10000);
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++      usleep_range(1000, 2000);
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++      usleep_range(20000, 25000);
++}
++
++static int ov5640_set_power_on(struct ov5640_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      int ret;
++
++      ret = clk_prepare_enable(sensor->xclk);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable clock\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = regulator_bulk_enable(OV5640_NUM_SUPPLIES,
++                                  sensor->supplies);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable regulators\n",
++                      __func__);
++              goto xclk_off;
++      }
++
++      ov5640_reset(sensor);
++      ov5640_power(sensor, true);
++
++      ret = ov5640_init_slave_id(sensor);
++      if (ret)
++              goto power_off;
++
++      return 0;
++
++power_off:
++      ov5640_power(sensor, false);
++      regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
++xclk_off:
++      clk_disable_unprepare(sensor->xclk);
++      return ret;
++}
++
++static void ov5640_set_power_off(struct ov5640_dev *sensor)
++{
++      ov5640_power(sensor, false);
++      regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
++      clk_disable_unprepare(sensor->xclk);
++}
++
++static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on)
++{
++      int ret;
++
++      if (!on) {
++              /* Reset MIPI bus settings to their default values. */
++              ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
++              ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x04);
++              ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x00);
++              return 0;
++      }
++
++      /*
++       * Power up MIPI HS Tx and LS Rx; 2 data lanes mode
++       *
++       * 0x300e = 0x40
++       * [7:5] = 010  : 2 data lanes mode (see FIXME note in
++       *                "ov5640_set_stream_mipi()")
++       * [4] = 0      : Power up MIPI HS Tx
++       * [3] = 0      : Power up MIPI LS Rx
++       * [2] = 0      : MIPI interface disabled
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x40);
++      if (ret)
++              return ret;
++
++      /*
++       * Gate clock and set LP11 in 'no packets mode' (idle)
++       *
++       * 0x4800 = 0x24
++       * [5] = 1      : Gate clock when 'no packets'
++       * [2] = 1      : MIPI bus in LP11 when 'no packets'
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x24);
++      if (ret)
++              return ret;
++
++      /*
++       * Set data lanes and clock in LP11 when 'sleeping'
++       *
++       * 0x3019 = 0x70
++       * [6] = 1      : MIPI data lane 2 in LP11 when 'sleeping'
++       * [5] = 1      : MIPI data lane 1 in LP11 when 'sleeping'
++       * [4] = 1      : MIPI clock lane in LP11 when 'sleeping'
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x70);
++      if (ret)
++              return ret;
++
++      /* Give lanes some time to coax into LP11 state. */
++      usleep_range(500, 1000);
++
++      return 0;
++}
++
++static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on)
++{
++      unsigned int flags = sensor->ep.bus.parallel.flags;
++      bool bt656 = sensor->ep.bus_type == V4L2_MBUS_BT656;
++      u8 polarities = 0;
++      int ret;
++
++      if (!on) {
++              /* Reset settings to their default values. */
++              ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, 0x00);
++              ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
++              ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, 0x20);
++              ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, 0x00);
++              ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0x00);
++              return 0;
++      }
++
++      /*
++       * Note about parallel port configuration.
++       *
++       * When configured in parallel mode, the OV5640 will
++       * output 10 bits data on DVP data lines [9:0].
++       * If only 8 bits data are wanted, the 8 bits data lines
++       * of the camera interface must be physically connected
++       * on the DVP data lines [9:2].
++       *
++       * Control lines polarity can be configured through
++       * devicetree endpoint control lines properties.
++       * If no endpoint control lines properties are set,
++       * polarity will be as below:
++       * - VSYNC:     active high
++       * - HREF:      active low
++       * - PCLK:      active low
++       *
++       * VSYNC & HREF are not configured if BT656 bus mode is selected
++       */
++
++      /*
++       * BT656 embedded synchronization configuration
++       *
++       * CCIR656 CTRL00
++       * - [7]:       SYNC code selection (0: auto generate sync code,
++       *              1: sync code from regs 0x4732-0x4735)
++       * - [6]:       f value in CCIR656 SYNC code when fixed f value
++       * - [5]:       Fixed f value
++       * - [4:3]:     Blank toggle data options (00: data=1'h040/1'h200,
++       *              01: data from regs 0x4736-0x4738, 10: always keep 0)
++       * - [1]:       Clip data disable
++       * - [0]:       CCIR656 mode enable
++       *
++       * Default CCIR656 SAV/EAV mode with default codes
++       * SAV=0xff000080 & EAV=0xff00009d is enabled here with settings:
++       * - CCIR656 mode enable
++       * - auto generation of sync codes
++       * - blank toggle data 1'h040/1'h200
++       * - clip reserved data (0x00 & 0xff changed to 0x01 & 0xfe)
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00,
++                             bt656 ? 0x01 : 0x00);
++      if (ret)
++              return ret;
++
++      /*
++       * configure parallel port control lines polarity
++       *
++       * POLARITY CTRL0
++       * - [5]:       PCLK polarity (0: active low, 1: active high)
++       * - [1]:       HREF polarity (0: active low, 1: active high)
++       * - [0]:       VSYNC polarity (mismatch here between
++       *              datasheet and hardware, 0 is active high
++       *              and 1 is active low...)
++       */
++      if (!bt656) {
++              if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
++                      polarities |= BIT(1);
++              if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
++                      polarities |= BIT(0);
++      }
++      if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
++              polarities |= BIT(5);
++
++      ret = ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, polarities);
++      if (ret)
++              return ret;
++
++      /*
++       * powerdown MIPI TX/RX PHY & enable DVP
++       *
++       * MIPI CONTROL 00
++       * [4] = 1      : Power down MIPI HS Tx
++       * [3] = 1      : Power down MIPI LS Rx
++       * [2] = 0      : DVP enable (MIPI disable)
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58);
++      if (ret)
++              return ret;
++
++      /*
++       * enable VSYNC/HREF/PCLK DVP control lines
++       * & D[9:6] DVP data lines
++       *
++       * PAD OUTPUT ENABLE 01
++       * - 6:         VSYNC output enable
++       * - 5:         HREF output enable
++       * - 4:         PCLK output enable
++       * - [3:0]:     D[9:6] output enable
++       */
++      ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01,
++                             bt656 ? 0x1f : 0x7f);
++      if (ret)
++              return ret;
++
++      /*
++       * enable D[5:0] DVP data lines
++       *
++       * PAD OUTPUT ENABLE 02
++       * - [7:2]:     D[5:0] output enable
++       */
++      return ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0xfc);
++}
++
++static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
++{
++      int ret = 0;
++
++      if (on) {
++              ret = ov5640_set_power_on(sensor);
++              if (ret)
++                      return ret;
++
++              ret = ov5640_restore_mode(sensor);
++              if (ret)
++                      goto power_off;
++      }
++
++      if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++              ret = ov5640_set_power_mipi(sensor, on);
++      else
++              ret = ov5640_set_power_dvp(sensor, on);
++      if (ret)
++              goto power_off;
++
++      if (!on)
++              ov5640_set_power_off(sensor);
++
++      return 0;
++
++power_off:
++      ov5640_set_power_off(sensor);
++      return ret;
++}
++
++/* --------------- Subdev Operations --------------- */
++
++static int ov5640_s_power(struct v4l2_subdev *sd, int on)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      int ret = 0;
++
++      mutex_lock(&sensor->lock);
++
++      /*
++       * If the power count is modified from 0 to != 0 or from != 0 to 0,
++       * update the power state.
++       */
++      if (sensor->power_count == !on) {
++              ret = ov5640_set_power(sensor, !!on);
++              if (ret)
++                      goto out;
++      }
++
++      /* Update the power count. */
++      sensor->power_count += on ? 1 : -1;
++      WARN_ON(sensor->power_count < 0);
++out:
++      mutex_unlock(&sensor->lock);
++
++      if (on && !ret && sensor->power_count == 1) {
++              /* restore controls */
++              ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++      }
++
++      return ret;
++}
++
++static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
++                                   struct v4l2_fract *fi,
++                                   u32 width, u32 height)
++{
++      const struct ov5640_mode_info *mode;
++      enum ov5640_frame_rate rate = OV5640_15_FPS;
++      int minfps, maxfps, best_fps, fps;
++      int i;
++
++      minfps = ov5640_framerates[OV5640_15_FPS];
++      maxfps = ov5640_framerates[OV5640_60_FPS];
++
++      if (fi->numerator == 0) {
++              fi->denominator = maxfps;
++              fi->numerator = 1;
++              rate = OV5640_60_FPS;
++              goto find_mode;
++      }
++
++      fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++                      minfps, maxfps);
++
++      best_fps = minfps;
++      for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) {
++              int curr_fps = ov5640_framerates[i];
++
++              if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++                      best_fps = curr_fps;
++                      rate = i;
++              }
++      }
++
++      fi->numerator = 1;
++      fi->denominator = best_fps;
++
++find_mode:
++      mode = ov5640_find_mode(sensor, rate, width, height, false);
++      return mode ? rate : -EINVAL;
++}
++
++static int ov5640_get_fmt(struct v4l2_subdev *sd,
++                        struct v4l2_subdev_state *state,
++                        struct v4l2_subdev_format *format)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      struct v4l2_mbus_framefmt *fmt;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++                                               format->pad);
++      else
++              fmt = &sensor->fmt;
++
++      format->format = *fmt;
++
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
++                                 struct v4l2_mbus_framefmt *fmt,
++                                 enum ov5640_frame_rate fr,
++                                 const struct ov5640_mode_info **new_mode)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      const struct ov5640_mode_info *mode;
++      int i;
++
++      mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
++      if (!mode)
++              return -EINVAL;
++      fmt->width = mode->hact;
++      fmt->height = mode->vact;
++
++      if (new_mode)
++              *new_mode = mode;
++
++      for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++)
++              if (ov5640_formats[i].code == fmt->code)
++                      break;
++      if (i >= ARRAY_SIZE(ov5640_formats))
++              i = 0;
++
++      fmt->code = ov5640_formats[i].code;
++      fmt->colorspace = ov5640_formats[i].colorspace;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++
++      return 0;
++}
++
++static int ov5640_set_fmt(struct v4l2_subdev *sd,
++                        struct v4l2_subdev_state *state,
++                        struct v4l2_subdev_format *format)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      const struct ov5640_mode_info *new_mode;
++      struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++      struct v4l2_mbus_framefmt *fmt;
++      int ret;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      ret = ov5640_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++      if (ret)
++              goto out;
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(sd, state, 0);
++      else
++              fmt = &sensor->fmt;
++
++      if (mbus_fmt->code != sensor->fmt.code)
++              sensor->pending_fmt_change = true;
++
++      *fmt = *mbus_fmt;
++
++      if (new_mode != sensor->current_mode) {
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++      if (new_mode->max_fps < sensor->current_fr) {
++              sensor->current_fr = new_mode->max_fps;
++              sensor->frame_interval.numerator = 1;
++              sensor->frame_interval.denominator =
++                      ov5640_framerates[sensor->current_fr];
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++
++      __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                               ov5640_calc_pixel_rate(sensor));
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int ov5640_set_framefmt(struct ov5640_dev *sensor,
++                             struct v4l2_mbus_framefmt *format)
++{
++      int ret = 0;
++      bool is_jpeg = false;
++      u8 fmt, mux;
++
++      switch (format->code) {
++      case MEDIA_BUS_FMT_UYVY8_2X8:
++              /* YUV422, UYVY */
++              fmt = 0x3f;
++              mux = OV5640_FMT_MUX_YUV422;
++              break;
++      case MEDIA_BUS_FMT_YUYV8_2X8:
++              /* YUV422, YUYV */
++              fmt = 0x30;
++              mux = OV5640_FMT_MUX_YUV422;
++              break;
++      case MEDIA_BUS_FMT_RGB565_2X8_LE:
++              /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
++              fmt = 0x6F;
++              mux = OV5640_FMT_MUX_RGB;
++              break;
++      case MEDIA_BUS_FMT_RGB565_2X8_BE:
++              /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
++              fmt = 0x61;
++              mux = OV5640_FMT_MUX_RGB;
++              break;
++      case MEDIA_BUS_FMT_JPEG_1X8:
++              /* YUV422, YUYV */
++              fmt = 0x30;
++              mux = OV5640_FMT_MUX_YUV422;
++              is_jpeg = true;
++              break;
++      case MEDIA_BUS_FMT_SBGGR8_1X8:
++              /* Raw, BGBG... / GRGR... */
++              fmt = 0x00;
++              mux = OV5640_FMT_MUX_RAW_DPC;
++              break;
++      case MEDIA_BUS_FMT_SGBRG8_1X8:
++              /* Raw bayer, GBGB... / RGRG... */
++              fmt = 0x01;
++              mux = OV5640_FMT_MUX_RAW_DPC;
++              break;
++      case MEDIA_BUS_FMT_SGRBG8_1X8:
++              /* Raw bayer, GRGR... / BGBG... */
++              fmt = 0x02;
++              mux = OV5640_FMT_MUX_RAW_DPC;
++              break;
++      case MEDIA_BUS_FMT_SRGGB8_1X8:
++              /* Raw bayer, RGRG... / GBGB... */
++              fmt = 0x03;
++              mux = OV5640_FMT_MUX_RAW_DPC;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      /* FORMAT CONTROL00: YUV and RGB formatting */
++      ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt);
++      if (ret)
++              return ret;
++
++      /* FORMAT MUX CONTROL: ISP YUV or RGB */
++      ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux);
++      if (ret)
++              return ret;
++
++      /*
++       * TIMING TC REG21:
++       * - [5]:       JPEG enable
++       */
++      ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
++                           BIT(5), is_jpeg ? BIT(5) : 0);
++      if (ret)
++              return ret;
++
++      /*
++       * SYSTEM RESET02:
++       * - [4]:       Reset JFIFO
++       * - [3]:       Reset SFIFO
++       * - [2]:       Reset JPEG
++       */
++      ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02,
++                           BIT(4) | BIT(3) | BIT(2),
++                           is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2)));
++      if (ret)
++              return ret;
++
++      /*
++       * CLOCK ENABLE02:
++       * - [5]:       Enable JPEG 2x clock
++       * - [3]:       Enable JPEG clock
++       */
++      return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02,
++                            BIT(5) | BIT(3),
++                            is_jpeg ? (BIT(5) | BIT(3)) : 0);
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int ov5640_set_ctrl_hue(struct ov5640_dev *sensor, int value)
++{
++      int ret;
++
++      if (value) {
++              ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
++                                   BIT(0), BIT(0));
++              if (ret)
++                      return ret;
++              ret = ov5640_write_reg16(sensor, OV5640_REG_SDE_CTRL1, value);
++      } else {
++              ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(0), 0);
++      }
++
++      return ret;
++}
++
++static int ov5640_set_ctrl_contrast(struct ov5640_dev *sensor, int value)
++{
++      int ret;
++
++      if (value) {
++              ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
++                                   BIT(2), BIT(2));
++              if (ret)
++                      return ret;
++              ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL5,
++                                     value & 0xff);
++      } else {
++              ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(2), 0);
++      }
++
++      return ret;
++}
++
++static int ov5640_set_ctrl_saturation(struct ov5640_dev *sensor, int value)
++{
++      int ret;
++
++      if (value) {
++              ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0,
++                                   BIT(1), BIT(1));
++              if (ret)
++                      return ret;
++              ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL3,
++                                     value & 0xff);
++              if (ret)
++                      return ret;
++              ret = ov5640_write_reg(sensor, OV5640_REG_SDE_CTRL4,
++                                     value & 0xff);
++      } else {
++              ret = ov5640_mod_reg(sensor, OV5640_REG_SDE_CTRL0, BIT(1), 0);
++      }
++
++      return ret;
++}
++
++static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb)
++{
++      int ret;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_AWB_MANUAL_CTRL,
++                           BIT(0), awb ? 0 : 1);
++      if (ret)
++              return ret;
++
++      if (!awb) {
++              u16 red = (u16)sensor->ctrls.red_balance->val;
++              u16 blue = (u16)sensor->ctrls.blue_balance->val;
++
++              ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_R_GAIN, red);
++              if (ret)
++                      return ret;
++              ret = ov5640_write_reg16(sensor, OV5640_REG_AWB_B_GAIN, blue);
++      }
++
++      return ret;
++}
++
++static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor,
++                                  enum v4l2_exposure_auto_type auto_exposure)
++{
++      struct ov5640_ctrls *ctrls = &sensor->ctrls;
++      bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
++      int ret = 0;
++
++      if (ctrls->auto_exp->is_new) {
++              ret = ov5640_set_autoexposure(sensor, auto_exp);
++              if (ret)
++                      return ret;
++      }
++
++      if (!auto_exp && ctrls->exposure->is_new) {
++              u16 max_exp;
++
++              ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS,
++                                      &max_exp);
++              if (ret)
++                      return ret;
++              ret = ov5640_get_vts(sensor);
++              if (ret < 0)
++                      return ret;
++              max_exp += ret;
++              ret = 0;
++
++              if (ctrls->exposure->val < max_exp)
++                      ret = ov5640_set_exposure(sensor, ctrls->exposure->val);
++      }
++
++      return ret;
++}
++
++static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain)
++{
++      struct ov5640_ctrls *ctrls = &sensor->ctrls;
++      int ret = 0;
++
++      if (ctrls->auto_gain->is_new) {
++              ret = ov5640_set_autogain(sensor, auto_gain);
++              if (ret)
++                      return ret;
++      }
++
++      if (!auto_gain && ctrls->gain->is_new)
++              ret = ov5640_set_gain(sensor, ctrls->gain->val);
++
++      return ret;
++}
++
++static const char * const test_pattern_menu[] = {
++      "Disabled",
++      "Color bars",
++      "Color bars w/ rolling bar",
++      "Color squares",
++      "Color squares w/ rolling bar",
++};
++
++#define OV5640_TEST_ENABLE            BIT(7)
++#define OV5640_TEST_ROLLING           BIT(6)  /* rolling horizontal bar */
++#define OV5640_TEST_TRANSPARENT               BIT(5)
++#define OV5640_TEST_SQUARE_BW         BIT(4)  /* black & white squares */
++#define OV5640_TEST_BAR_STANDARD      (0 << 2)
++#define OV5640_TEST_BAR_VERT_CHANGE_1 (1 << 2)
++#define OV5640_TEST_BAR_HOR_CHANGE    (2 << 2)
++#define OV5640_TEST_BAR_VERT_CHANGE_2 (3 << 2)
++#define OV5640_TEST_BAR                       (0 << 0)
++#define OV5640_TEST_RANDOM            (1 << 0)
++#define OV5640_TEST_SQUARE            (2 << 0)
++#define OV5640_TEST_BLACK             (3 << 0)
++
++static const u8 test_pattern_val[] = {
++      0,
++      OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 |
++              OV5640_TEST_BAR,
++      OV5640_TEST_ENABLE | OV5640_TEST_ROLLING |
++              OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR,
++      OV5640_TEST_ENABLE | OV5640_TEST_SQUARE,
++      OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | OV5640_TEST_SQUARE,
++};
++
++static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
++{
++      return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1,
++                              test_pattern_val[value]);
++}
++
++static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
++{
++      int ret;
++
++      ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7),
++                           (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ?
++                           0 : BIT(7));
++      if (ret)
++              return ret;
++
++      return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2),
++                            (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ?
++                            BIT(2) : 0);
++}
++
++static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
++{
++      /*
++       * If sensor is mounted upside down, mirror logic is inversed.
++       *
++       * Sensor is a BSI (Back Side Illuminated) one,
++       * so image captured is physically mirrored.
++       * This is why mirror logic is inversed in
++       * order to cancel this mirror effect.
++       */
++
++      /*
++       * TIMING TC REG21:
++       * - [2]:       ISP mirror
++       * - [1]:       Sensor mirror
++       */
++      return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
++                            BIT(2) | BIT(1),
++                            (!(value ^ sensor->upside_down)) ?
++                            (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
++{
++      /* If sensor is mounted upside down, flip logic is inversed */
++
++      /*
++       * TIMING TC REG20:
++       * - [2]:       ISP vflip
++       * - [1]:       Sensor vflip
++       */
++      return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
++                            BIT(2) | BIT(1),
++                            (value ^ sensor->upside_down) ?
++                            (BIT(2) | BIT(1)) : 0);
++}
++
++static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      int val;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      switch (ctrl->id) {
++      case V4L2_CID_AUTOGAIN:
++              val = ov5640_get_gain(sensor);
++              if (val < 0)
++                      return val;
++              sensor->ctrls.gain->val = val;
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              val = ov5640_get_exposure(sensor);
++              if (val < 0)
++                      return val;
++              sensor->ctrls.exposure->val = val;
++              break;
++      }
++
++      return 0;
++}
++
++static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      int ret;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      /*
++       * If the device is not powered up by the host driver do
++       * not apply any controls to H/W at this time. Instead
++       * the controls will be restored right after power-up.
++       */
++      if (sensor->power_count == 0)
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_AUTOGAIN:
++              ret = ov5640_set_ctrl_gain(sensor, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              ret = ov5640_set_ctrl_exposure(sensor, ctrl->val);
++              break;
++      case V4L2_CID_AUTO_WHITE_BALANCE:
++              ret = ov5640_set_ctrl_white_balance(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HUE:
++              ret = ov5640_set_ctrl_hue(sensor, ctrl->val);
++              break;
++      case V4L2_CID_CONTRAST:
++              ret = ov5640_set_ctrl_contrast(sensor, ctrl->val);
++              break;
++      case V4L2_CID_SATURATION:
++              ret = ov5640_set_ctrl_saturation(sensor, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
++              break;
++      case V4L2_CID_POWER_LINE_FREQUENCY:
++              ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HFLIP:
++              ret = ov5640_set_ctrl_hflip(sensor, ctrl->val);
++              break;
++      case V4L2_CID_VFLIP:
++              ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
++      .g_volatile_ctrl = ov5640_g_volatile_ctrl,
++      .s_ctrl = ov5640_s_ctrl,
++};
++
++static int ov5640_init_controls(struct ov5640_dev *sensor)
++{
++      const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
++      struct ov5640_ctrls *ctrls = &sensor->ctrls;
++      struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++      int ret;
++
++      v4l2_ctrl_handler_init(hdl, 32);
++
++      /* we can use our own mutex for the ctrl lock */
++      hdl->lock = &sensor->lock;
++
++      /* Clock related controls */
++      ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++                                            0, INT_MAX, 1,
++                                            ov5640_calc_pixel_rate(sensor));
++
++      /* Auto/manual white balance */
++      ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++                                         V4L2_CID_AUTO_WHITE_BALANCE,
++                                         0, 1, 1, 1);
++      ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++                                              0, 4095, 1, 0);
++      ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++                                             0, 4095, 1, 0);
++      /* Auto/manual exposure */
++      ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++                                               V4L2_CID_EXPOSURE_AUTO,
++                                               V4L2_EXPOSURE_MANUAL, 0,
++                                               V4L2_EXPOSURE_AUTO);
++      ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++                                          0, 65535, 1, 0);
++      /* Auto/manual gain */
++      ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++                                           0, 1, 1, 1);
++      ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++                                      0, 1023, 1, 0);
++
++      ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
++                                            0, 255, 1, 64);
++      ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
++                                     0, 359, 1, 0);
++      ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
++                                          0, 255, 1, 0);
++      ctrls->test_pattern =
++              v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++                                           ARRAY_SIZE(test_pattern_menu) - 1,
++                                           0, 0, test_pattern_menu);
++      ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++                                       0, 1, 1, 0);
++      ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++                                       0, 1, 1, 0);
++
++      ctrls->light_freq =
++              v4l2_ctrl_new_std_menu(hdl, ops,
++                                     V4L2_CID_POWER_LINE_FREQUENCY,
++                                     V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++                                     V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++
++      if (hdl->error) {
++              ret = hdl->error;
++              goto free_ctrls;
++      }
++
++      ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++      ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++      v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++      v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
++      v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
++
++      sensor->sd.ctrl_handler = hdl;
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(hdl);
++      return ret;
++}
++
++static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
++                                struct v4l2_subdev_state *state,
++                                struct v4l2_subdev_frame_size_enum *fse)
++{
++      if (fse->pad != 0)
++              return -EINVAL;
++      if (fse->index >= OV5640_NUM_MODES)
++              return -EINVAL;
++
++      fse->min_width =
++              ov5640_mode_data[fse->index].hact;
++      fse->max_width = fse->min_width;
++      fse->min_height =
++              ov5640_mode_data[fse->index].vact;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static int ov5640_enum_frame_interval(
++      struct v4l2_subdev *sd,
++      struct v4l2_subdev_state *state,
++      struct v4l2_subdev_frame_interval_enum *fie)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      struct v4l2_fract tpf;
++      int ret;
++
++      if (fie->pad != 0)
++              return -EINVAL;
++      if (fie->index >= OV5640_NUM_FRAMERATES)
++              return -EINVAL;
++
++      tpf.numerator = 1;
++      tpf.denominator = ov5640_framerates[fie->index];
++
++      ret = ov5640_try_frame_interval(sensor, &tpf,
++                                      fie->width, fie->height);
++      if (ret < 0)
++              return -EINVAL;
++
++      fie->interval = tpf;
++      return 0;
++}
++
++static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
++                                 struct v4l2_subdev_frame_interval *fi)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++
++      mutex_lock(&sensor->lock);
++      fi->interval = sensor->frame_interval;
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
++                                 struct v4l2_subdev_frame_interval *fi)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      const struct ov5640_mode_info *mode;
++      int frame_rate, ret = 0;
++
++      if (fi->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      mode = sensor->current_mode;
++
++      frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
++                                             mode->hact, mode->vact);
++      if (frame_rate < 0) {
++              /* Always return a valid frame interval value */
++              fi->interval = sensor->frame_interval;
++              goto out;
++      }
++
++      mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
++                              mode->vact, true);
++      if (!mode) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      if (mode != sensor->current_mode ||
++          frame_rate != sensor->current_fr) {
++              sensor->current_fr = frame_rate;
++              sensor->frame_interval = fi->interval;
++              sensor->current_mode = mode;
++              sensor->pending_mode_change = true;
++
++              __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                                       ov5640_calc_pixel_rate(sensor));
++      }
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *state,
++                               struct v4l2_subdev_mbus_code_enum *code)
++{
++      if (code->pad != 0)
++              return -EINVAL;
++      if (code->index >= ARRAY_SIZE(ov5640_formats))
++              return -EINVAL;
++
++      code->code = ov5640_formats[code->index].code;
++      return 0;
++}
++
++static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++      int ret = 0;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming == !enable) {
++              if (enable && sensor->pending_mode_change) {
++                      ret = ov5640_set_mode(sensor);
++                      if (ret)
++                              goto out;
++              }
++
++              if (enable && sensor->pending_fmt_change) {
++                      ret = ov5640_set_framefmt(sensor, &sensor->fmt);
++                      if (ret)
++                              goto out;
++                      sensor->pending_fmt_change = false;
++              }
++
++              if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
++                      ret = ov5640_set_stream_mipi(sensor, enable);
++              else
++                      ret = ov5640_set_stream_dvp(sensor, enable);
++
++              if (ret)
++                      goto out;
++      }
++      sensor->streaming += enable ? 1 : -1;
++      WARN_ON(sensor->streaming < 0);
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++int ov5640_skip_frames(struct v4l2_subdev *sd, u32 *frames)
++{
++      *frames = OV5640_SKIP_FRAMES;
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops ov5640_core_ops = {
++      .s_power = ov5640_s_power,
++      .log_status = v4l2_ctrl_subdev_log_status,
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops ov5640_video_ops = {
++      .g_frame_interval = ov5640_g_frame_interval,
++      .s_frame_interval = ov5640_s_frame_interval,
++      .s_stream = ov5640_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
++      .enum_mbus_code = ov5640_enum_mbus_code,
++      .get_fmt = ov5640_get_fmt,
++      .set_fmt = ov5640_set_fmt,
++      .enum_frame_size = ov5640_enum_frame_size,
++      .enum_frame_interval = ov5640_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_sensor_ops ov5640_sensor_ops = {
++      .g_skip_frames = ov5640_skip_frames,
++};
++
++static const struct v4l2_subdev_ops ov5640_subdev_ops = {
++      .core = &ov5640_core_ops,
++      .video = &ov5640_video_ops,
++      .pad = &ov5640_pad_ops,
++      .sensor = &ov5640_sensor_ops,
++};
++
++static int ov5640_get_regulators(struct ov5640_dev *sensor)
++{
++      int i;
++
++      for (i = 0; i < OV5640_NUM_SUPPLIES; i++)
++              sensor->supplies[i].supply = ov5640_supply_name[i];
++
++      return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++                                     OV5640_NUM_SUPPLIES,
++                                     sensor->supplies);
++}
++
++static int ov5640_check_chip_id(struct ov5640_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      int ret = 0;
++      u16 chip_id;
++
++      ret = ov5640_set_power_on(sensor);
++      if (ret)
++              return ret;
++
++      ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to read chip identifier\n",
++                      __func__);
++              goto power_off;
++      }
++
++      if (chip_id != OV5640_CHIP_ID) {
++              dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++                      __func__, OV5640_CHIP_ID, chip_id);
++              ret = -ENXIO;
++      }
++      dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++              __func__, chip_id);
++
++power_off:
++      ov5640_set_power_off(sensor);
++      return ret;
++}
++
++static int ov5640_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct fwnode_handle *endpoint;
++      struct ov5640_dev *sensor;
++      struct v4l2_mbus_framefmt *fmt;
++      u32 rotation;
++      int ret;
++
++      sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++      if (!sensor)
++              return -ENOMEM;
++
++      sensor->i2c_client = client;
++
++      /*
++       * default init sequence initialize sensor to
++       * YUV422 UYVY VGA@30fps
++       */
++      fmt = &sensor->fmt;
++      fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++      fmt->width = 640;
++      fmt->height = 480;
++      fmt->field = V4L2_FIELD_NONE;
++      sensor->frame_interval.numerator = 1;
++      sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
++      sensor->current_fr = OV5640_30_FPS;
++      sensor->current_mode =
++              &ov5640_mode_data[OV5640_MODE_VGA_640_480];
++      sensor->last_mode = sensor->current_mode;
++
++      sensor->ae_target = 52;
++
++      /* optional indication of physical rotation of sensor */
++      ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++                                     &rotation);
++      if (!ret) {
++              switch (rotation) {
++              case 180:
++                      sensor->upside_down = true;
++                      fallthrough;
++              case 0:
++                      break;
++              default:
++                      dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++                               rotation);
++              }
++      }
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++                                                NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++      fwnode_handle_put(endpoint);
++      if (ret) {
++              dev_err(dev, "Could not parse endpoint\n");
++              return ret;
++      }
++
++      if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL &&
++          sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY &&
++          sensor->ep.bus_type != V4L2_MBUS_BT656) {
++              dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++              return -EINVAL;
++      }
++
++      /* get system clock (xclk) */
++      sensor->xclk = devm_clk_get(dev, "xclk");
++      if (IS_ERR(sensor->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(sensor->xclk);
++      }
++
++      sensor->xclk_freq = clk_get_rate(sensor->xclk);
++      if (sensor->xclk_freq < OV5640_XCLK_MIN ||
++          sensor->xclk_freq > OV5640_XCLK_MAX) {
++              dev_err(dev, "xclk frequency out of range: %d Hz\n",
++                      sensor->xclk_freq);
++              return -EINVAL;
++      }
++
++      /* request optional power down pin */
++      sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++                                                  GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->pwdn_gpio))
++              return PTR_ERR(sensor->pwdn_gpio);
++
++      /* request optional reset pin */
++      sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++                                                   GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->reset_gpio))
++              return PTR_ERR(sensor->reset_gpio);
++
++      v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
++
++      sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++                          V4L2_SUBDEV_FL_HAS_EVENTS;
++      sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++      sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++      ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++      if (ret)
++              return ret;
++
++      ret = ov5640_get_regulators(sensor);
++      if (ret)
++              return ret;
++
++      mutex_init(&sensor->lock);
++
++      ret = ov5640_check_chip_id(sensor);
++      if (ret)
++              goto entity_cleanup;
++
++      ret = ov5640_init_controls(sensor);
++      if (ret)
++              goto entity_cleanup;
++
++      ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++      if (ret)
++              goto free_ctrls;
++
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++entity_cleanup:
++      media_entity_cleanup(&sensor->sd.entity);
++      mutex_destroy(&sensor->lock);
++      return ret;
++}
++
++static int ov5640_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct ov5640_dev *sensor = to_ov5640_dev(sd);
++
++      v4l2_async_unregister_subdev(&sensor->sd);
++      media_entity_cleanup(&sensor->sd.entity);
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++      mutex_destroy(&sensor->lock);
++
++      return 0;
++}
++
++static const struct i2c_device_id ov5640_id[] = {
++      {"ov5640", 0},
++      {},
++};
++MODULE_DEVICE_TABLE(i2c, ov5640_id);
++
++static const struct of_device_id ov5640_dt_ids[] = {
++      { .compatible = "ovti,ov5640" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
++
++static struct i2c_driver ov5640_i2c_driver = {
++      .driver = {
++              .name  = "ov5640",
++              .of_match_table = ov5640_dt_ids,
++      },
++      .id_table = ov5640_id,
++      .probe_new = ov5640_probe,
++      .remove   = ov5640_remove,
++};
++
++module_i2c_driver(ov5640_i2c_driver);
++
++MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/sc2235.c
+@@ -0,0 +1,1914 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/of_device.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include "stfcamss.h"
++
++/* min/typical/max system clock (xclk) frequencies */
++#define SC2235_XCLK_MIN                       6000000
++#define SC2235_XCLK_MAX                       27000000
++
++#define SC2235_CHIP_ID                (0x2235)
++
++#define SC2235_REG_CHIP_ID                            0x3107
++#define SC2235_REG_AEC_PK_MANUAL              0x3e03
++#define SC2235_REG_AEC_PK_EXPOSURE_HI 0x3e01
++#define SC2235_REG_AEC_PK_EXPOSURE_LO 0x3e02
++#define SC2235_REG_AEC_PK_REAL_GAIN           0x3e08
++#define SC2235_REG_TIMING_HTS                 0x320c
++#define SC2235_REG_TIMING_VTS                 0x320e
++#define SC2235_REG_TEST_SET0                  0x4501
++#define SC2235_REG_TEST_SET1                  0x3902
++#define SC2235_REG_TIMING_TC_REG21            0x3221
++#define SC2235_REG_SC_PLL_CTRL0                       0x3039
++#define SC2235_REG_SC_PLL_CTRL1                       0x303a
++#define SC2235_REG_STREAM_ON            0x0100
++
++enum sc2235_mode_id {
++      SC2235_MODE_1080P_1920_1080 = 0,
++      SC2235_NUM_MODES,
++};
++
++enum sc2235_frame_rate {
++      SC2235_15_FPS = 0,
++      SC2235_30_FPS,
++      SC2235_NUM_FRAMERATES,
++};
++
++struct sc2235_pixfmt {
++      u32 code;
++      u32 colorspace;
++};
++
++static const struct sc2235_pixfmt sc2235_formats[] = {
++      { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB, },
++};
++
++static const int sc2235_framerates[] = {
++      [SC2235_15_FPS] = 15,
++      [SC2235_30_FPS] = 30,
++};
++
++/* regulator supplies */
++static const char * const sc2235_supply_name[] = {
++      "DOVDD", /* Digital I/O (1.8V) supply */
++      "AVDD",  /* Analog (2.8V) supply */
++      "DVDD",  /* Digital Core (1.5V) supply */
++};
++
++#define SC2235_NUM_SUPPLIES ARRAY_SIZE(sc2235_supply_name)
++
++struct reg_value {
++      u16 reg_addr;
++      u8 val;
++      u8 mask;
++      u32 delay_ms;
++};
++
++struct sc2235_mode_info {
++      enum sc2235_mode_id id;
++      u32 hact;
++      u32 htot;
++      u32 vact;
++      u32 vtot;
++      const struct reg_value *reg_data;
++      u32 reg_data_size;
++      u32 max_fps;
++};
++
++struct sc2235_ctrls {
++      struct v4l2_ctrl_handler handler;
++      struct v4l2_ctrl *pixel_rate;
++      struct {
++              struct v4l2_ctrl *auto_exp;
++              struct v4l2_ctrl *exposure;
++      };
++      struct {
++              struct v4l2_ctrl *auto_wb;
++              struct v4l2_ctrl *blue_balance;
++              struct v4l2_ctrl *red_balance;
++      };
++      struct {
++              struct v4l2_ctrl *auto_gain;
++              struct v4l2_ctrl *gain;
++      };
++      struct v4l2_ctrl *brightness;
++      struct v4l2_ctrl *light_freq;
++      struct v4l2_ctrl *saturation;
++      struct v4l2_ctrl *contrast;
++      struct v4l2_ctrl *hue;
++      struct v4l2_ctrl *test_pattern;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vflip;
++};
++
++struct sc2235_dev {
++      struct i2c_client *i2c_client;
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++      struct v4l2_fwnode_endpoint ep; /* the parsed DT endpoint info */
++      struct clk *xclk; /* system clock to SC2235 */
++      u32 xclk_freq;
++
++      struct regulator_bulk_data supplies[SC2235_NUM_SUPPLIES];
++      struct gpio_desc *reset_gpio;
++      struct gpio_desc *pwdn_gpio;
++      bool   upside_down;
++
++      /* lock to protect all members below */
++      struct mutex lock;
++
++      struct v4l2_mbus_framefmt fmt;
++      bool pending_fmt_change;
++
++      const struct sc2235_mode_info *current_mode;
++      const struct sc2235_mode_info *last_mode;
++      enum sc2235_frame_rate current_fr;
++      struct v4l2_fract frame_interval;
++
++      struct sc2235_ctrls ctrls;
++
++      bool pending_mode_change;
++      int streaming;
++};
++
++static inline struct sc2235_dev *to_sc2235_dev(struct v4l2_subdev *sd)
++{
++      return container_of(sd, struct sc2235_dev, sd);
++}
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++      return &container_of(ctrl->handler, struct sc2235_dev,
++                              ctrls.handler)->sd;
++}
++
++/* sc2235 initial register 30fps*/
++static struct reg_value sc2235_init_regs_tbl_1080[] = {
++      {0x0103, 0x01, 0, 50},
++      {0x0100, 0x00, 0, 0},
++      {0x3039, 0x80, 0, 0},
++      {0x3621, 0x28, 0, 0},
++
++      {0x3309, 0x60, 0, 0},
++      {0x331f, 0x4d, 0, 0},
++      {0x3321, 0x4f, 0, 0},
++      {0x33b5, 0x10, 0, 0},
++
++      {0x3303, 0x20, 0, 0},
++      {0x331e, 0x0d, 0, 0},
++      {0x3320, 0x0f, 0, 0},
++
++      {0x3622, 0x02, 0, 0},
++      {0x3633, 0x42, 0, 0},
++      {0x3634, 0x42, 0, 0},
++
++      {0x3306, 0x66, 0, 0},
++      {0x330b, 0xd1, 0, 0},
++
++      {0x3301, 0x0e, 0, 0},
++
++      {0x320c, 0x08, 0, 0},
++      {0x320d, 0x98, 0, 0},
++
++      {0x3364, 0x05, 0, 0},           // [2] 1: write at sampling ending
++
++      {0x363c, 0x28, 0, 0},           //bypass nvdd
++      {0x363b, 0x0a, 0, 0},           //HVDD
++      {0x3635, 0xa0, 0, 0},           //TXVDD
++
++      {0x4500, 0x59, 0, 0},
++      {0x3d08, 0x00, 0, 0},
++      {0x3908, 0x11, 0, 0},
++
++      {0x363c, 0x08, 0, 0},
++
++      {0x3e03, 0x03, 0, 0},
++      {0x3e01, 0x46, 0, 0},
++
++      //0703
++      {0x3381, 0x0a, 0, 0},
++      {0x3348, 0x09, 0, 0},
++      {0x3349, 0x50, 0, 0},
++      {0x334a, 0x02, 0, 0},
++      {0x334b, 0x60, 0, 0},
++
++      {0x3380, 0x04, 0, 0},
++      {0x3340, 0x06, 0, 0},
++      {0x3341, 0x50, 0, 0},
++      {0x3342, 0x02, 0, 0},
++      {0x3343, 0x60, 0, 0},
++
++      //0707
++
++      {0x3632, 0x88, 0, 0},           //anti sm
++      {0x3309, 0xa0, 0, 0},
++      {0x331f, 0x8d, 0, 0},
++      {0x3321, 0x8f, 0, 0},
++
++      {0x335e, 0x01, 0, 0},           //ana dithering
++      {0x335f, 0x03, 0, 0},
++      {0x337c, 0x04, 0, 0},
++      {0x337d, 0x06, 0, 0},
++      {0x33a0, 0x05, 0, 0},
++      {0x3301, 0x05, 0, 0},
++
++      {0x337f, 0x03, 0, 0},
++      {0x3368, 0x02, 0, 0},
++      {0x3369, 0x00, 0, 0},
++      {0x336a, 0x00, 0, 0},
++      {0x336b, 0x00, 0, 0},
++      {0x3367, 0x08, 0, 0},
++      {0x330e, 0x30, 0, 0},
++
++      {0x3366, 0x7c, 0, 0},           // div_rst gap
++
++      {0x3635, 0xc1, 0, 0},
++      {0x363b, 0x09, 0, 0},
++      {0x363c, 0x07, 0, 0},
++
++      {0x391e, 0x00, 0, 0},
++
++      {0x3637, 0x14, 0, 0},           //fullwell 7K
++
++      {0x3306, 0x54, 0, 0},
++      {0x330b, 0xd8, 0, 0},
++      {0x366e, 0x08, 0, 0},           // ofs auto en [3]
++      {0x366f, 0x2f, 0, 0},
++
++      {0x3631, 0x84, 0, 0},
++      {0x3630, 0x48, 0, 0},
++      {0x3622, 0x06, 0, 0},
++
++      //ramp by sc
++      {0x3638, 0x1f, 0, 0},
++      {0x3625, 0x02, 0, 0},
++      {0x3636, 0x24, 0, 0},
++
++      //0714
++      {0x3348, 0x08, 0, 0},
++      {0x3e03, 0x0b, 0, 0},
++
++      //7.17 fpn
++      {0x3342, 0x03, 0, 0},
++      {0x3343, 0xa0, 0, 0},
++      {0x334a, 0x03, 0, 0},
++      {0x334b, 0xa0, 0, 0},
++
++      //0718
++      {0x3343, 0xb0, 0, 0},
++      {0x334b, 0xb0, 0, 0},
++
++      //0720
++      //digital ctrl
++      {0x3802, 0x01, 0, 0},
++      {0x3235, 0x04, 0, 0},
++      {0x3236, 0x63, 0, 0},           // vts-2
++
++      //fpn
++      {0x3343, 0xd0, 0, 0},
++      {0x334b, 0xd0, 0, 0},
++      {0x3348, 0x07, 0, 0},
++      {0x3349, 0x80, 0, 0},
++
++      //0724
++      {0x391b, 0x4d, 0, 0},
++
++      {0x3342, 0x04, 0, 0},
++      {0x3343, 0x20, 0, 0},
++      {0x334a, 0x04, 0, 0},
++      {0x334b, 0x20, 0, 0},
++
++      //0804
++      {0x3222, 0x29, 0, 0},
++      {0x3901, 0x02, 0, 0},
++
++      //0808
++
++      // auto blc
++      {0x3900, 0xD5, 0, 0},           // Bit[0]: blc_enable
++      {0x3902, 0x45, 0, 0},           // Bit[6]: blc_auto_en
++
++      // blc target
++      {0x3907, 0x00, 0, 0},
++      {0x3908, 0x00, 0, 0},
++
++      // auto dpc
++      {0x5000, 0x00, 0, 0},           // Bit[2]: white dead pixel cancel enable, Bit[1]: black dead pixel cancel enable
++
++      //digital ctrl
++      {0x3f00, 0x07, 0, 0},           // bit[2] = 1
++      {0x3f04, 0x08, 0, 0},
++      {0x3f05, 0x74, 0, 0},           // hts - { 0x24
++
++      //0809
++      {0x330b, 0xc8, 0, 0},
++
++      //0817
++      {0x3306, 0x4a, 0, 0},
++      {0x330b, 0xca, 0, 0},
++      {0x3639, 0x09, 0, 0},
++
++      //manual DPC
++      {0x5780, 0xff, 0, 0},
++      {0x5781, 0x04, 0, 0},
++      {0x5785, 0x18, 0, 0},
++
++      //0822
++      {0x3039, 0x35, 0, 0},           //fps
++      {0x303a, 0x2e, 0, 0},
++      {0x3034, 0x05, 0, 0},
++      {0x3035, 0x2a, 0, 0},
++
++      {0x320c, 0x08, 0, 0},
++      {0x320d, 0xca, 0, 0},
++      {0x320e, 0x04, 0, 0},
++      {0x320f, 0xb0, 0, 0},
++
++      {0x3f04, 0x08, 0, 0},
++      {0x3f05, 0xa6, 0, 0},           // hts - { 0x24
++
++      {0x3235, 0x04, 0, 0},
++      {0x3236, 0xae, 0, 0},           // vts-2
++
++      //0825
++      {0x3313, 0x05, 0, 0},
++      {0x3678, 0x42, 0, 0},
++
++      //for AE control per frame
++      {0x3670, 0x00, 0, 0},
++      {0x3633, 0x42, 0, 0},
++
++      {0x3802, 0x00, 0, 0},
++
++      //20180126
++      {0x3677, 0x3f, 0, 0},
++      {0x3306, 0x44, 0, 0},           //20180126[3c },4a]
++      {0x330b, 0xca, 0, 0},           //20180126[c2 },d3]
++
++      //20180202
++      {0x3237, 0x08, 0, 0},
++      {0x3238, 0x9a, 0, 0},           //hts-0x30
++
++      //20180417
++      {0x3640, 0x01, 0, 0},
++      {0x3641, 0x02, 0, 0},
++
++      {0x3301, 0x12, 0, 0},           //[8 },15]20180126
++      {0x3631, 0x84, 0, 0},
++      {0x366f, 0x2f, 0, 0},
++      {0x3622, 0xc6, 0, 0},           //20180117
++
++      {0x3e03, 0x03, 0, 0},           // Bit[3]: AGC table mapping method, Bit[1]: AGC manual, BIt[0]: AEC manual
++
++      // {0x0100, 0x00, 0, 0},
++      // {0x4501, 0xc8, 0, 0},        //bar testing
++      // {0x3902, 0x45, 0, 0},
++};
++
++static struct reg_value sc2235_setting_1080P_1920_1080[] = {
++
++};
++
++/* power-on sensor init reg table */
++static const struct sc2235_mode_info sc2235_mode_init_data = {
++      SC2235_MODE_1080P_1920_1080,
++      1920, 0x8ca, 1080, 0x4b0,
++      sc2235_init_regs_tbl_1080,
++      ARRAY_SIZE(sc2235_init_regs_tbl_1080),
++      SC2235_30_FPS,
++};
++
++static const struct sc2235_mode_info
++sc2235_mode_data[SC2235_NUM_MODES] = {
++      {SC2235_MODE_1080P_1920_1080,
++       1920, 0x8ca, 1080, 0x4b0,
++       sc2235_setting_1080P_1920_1080,
++       ARRAY_SIZE(sc2235_setting_1080P_1920_1080),
++       SC2235_30_FPS},
++};
++
++static int sc2235_write_reg(struct sc2235_dev *sensor, u16 reg, u8 val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg;
++      u8 buf[3];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++      buf[2] = val;
++
++      msg.addr = client->addr;
++      msg.flags = client->flags;
++      msg.buf = buf;
++      msg.len = sizeof(buf);
++
++      ret = i2c_transfer(client->adapter, &msg, 1);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
++                      __func__, reg, val);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int sc2235_read_reg(struct sc2235_dev *sensor, u16 reg, u8 *val)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      struct i2c_msg msg[2];
++      u8 buf[2];
++      int ret;
++
++      buf[0] = reg >> 8;
++      buf[1] = reg & 0xff;
++
++      msg[0].addr = client->addr;
++      msg[0].flags = client->flags;
++      msg[0].buf = buf;
++      msg[0].len = sizeof(buf);
++
++      msg[1].addr = client->addr;
++      msg[1].flags = client->flags | I2C_M_RD;
++      msg[1].buf = buf;
++      msg[1].len = 1;
++
++      ret = i2c_transfer(client->adapter, msg, 2);
++      if (ret < 0) {
++              dev_err(&client->dev, "%s: error: reg=%x\n",
++                      __func__, reg);
++              return ret;
++      }
++
++      *val = buf[0];
++      return 0;
++}
++
++static int sc2235_read_reg16(struct sc2235_dev *sensor, u16 reg, u16 *val)
++{
++      u8 hi, lo;
++      int ret;
++
++      ret = sc2235_read_reg(sensor, reg, &hi);
++      if (ret)
++              return ret;
++      ret = sc2235_read_reg(sensor, reg + 1, &lo);
++      if (ret)
++              return ret;
++
++      *val = ((u16)hi << 8) | (u16)lo;
++      return 0;
++}
++
++static int sc2235_write_reg16(struct sc2235_dev *sensor, u16 reg, u16 val)
++{
++      int ret;
++
++      ret = sc2235_write_reg(sensor, reg, val >> 8);
++      if (ret)
++              return ret;
++
++      return sc2235_write_reg(sensor, reg + 1, val & 0xff);
++}
++
++static int sc2235_mod_reg(struct sc2235_dev *sensor, u16 reg,
++                      u8 mask, u8 val)
++{
++      u8 readval;
++      int ret;
++
++      ret = sc2235_read_reg(sensor, reg, &readval);
++      if (ret)
++              return ret;
++
++      readval &= ~mask;
++      val &= mask;
++      val |= readval;
++
++      return sc2235_write_reg(sensor, reg, val);
++}
++
++#define SC2235_PLL_PREDIV     3
++
++#define SC2235_SYSDIV_MIN     0
++#define SC2235_SYSDIV_MAX     7
++
++#define SC2235_PLL_MULT_MIN   0
++#define SC2235_PLL_MULT_MAX   63
++
++#ifdef UNUSED_CODE
++static unsigned long sc2235_compute_sys_clk(struct sc2235_dev *sensor,
++                                      u8 pll_pre, u8 pll_mult,
++                                      u8 sysdiv)
++{
++      unsigned long sysclk =
++              sensor->xclk_freq * (64 - pll_mult) / (pll_pre * (sysdiv + 1));
++
++      /* PLL1 output cannot exceed 1GHz. */
++      if (sysclk / 1000000 > 1000)
++              return 0;
++
++      return sysclk;
++}
++
++static unsigned long sc2235_calc_sys_clk(struct sc2235_dev *sensor,
++                                      unsigned long rate,
++                                      u8 *pll_prediv, u8 *pll_mult,
++                                      u8 *sysdiv)
++{
++      unsigned long best = ~0;
++      u8 best_sysdiv = 1, best_mult = 1;
++      u8 _sysdiv, _pll_mult;
++
++      for (_sysdiv = SC2235_SYSDIV_MIN;
++              _sysdiv <= SC2235_SYSDIV_MAX;
++              _sysdiv++) {
++              for (_pll_mult = SC2235_PLL_MULT_MIN;
++                      _pll_mult <= SC2235_PLL_MULT_MAX;
++                      _pll_mult++) {
++                      unsigned long _rate;
++
++                      _rate = sc2235_compute_sys_clk(sensor,
++                                                      SC2235_PLL_PREDIV,
++                                                      _pll_mult, _sysdiv);
++
++                      /*
++                       * We have reached the maximum allowed PLL1 output,
++                       * increase sysdiv.
++                       */
++                      if (!_rate)
++                              break;
++
++                      /*
++                       * Prefer rates above the expected clock rate than
++                       * below, even if that means being less precise.
++                       */
++                      if (_rate < rate)
++                              continue;
++
++                      if (abs(rate - _rate) < abs(rate - best)) {
++                              best = _rate;
++                              best_sysdiv = _sysdiv;
++                              best_mult = _pll_mult;
++                      }
++
++                      if (_rate == rate)
++                              goto out;
++              }
++      }
++
++out:
++      *sysdiv = best_sysdiv;
++      *pll_prediv = SC2235_PLL_PREDIV;
++      *pll_mult = best_mult;
++
++      return best;
++}
++#endif
++
++static int sc2235_set_timings(struct sc2235_dev *sensor,
++                              const struct sc2235_mode_info *mode)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int sc2235_load_regs(struct sc2235_dev *sensor,
++                              const struct sc2235_mode_info *mode)
++{
++      const struct reg_value *regs = mode->reg_data;
++      unsigned int i;
++      u32 delay_ms;
++      u16 reg_addr;
++      u8 mask, val;
++      int ret = 0;
++
++      for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
++              delay_ms = regs->delay_ms;
++              reg_addr = regs->reg_addr;
++              val = regs->val;
++              mask = regs->mask;
++
++              if (mask)
++                      ret = sc2235_mod_reg(sensor, reg_addr, mask, val);
++              else
++                      ret = sc2235_write_reg(sensor, reg_addr, val);
++              if (ret)
++                      break;
++
++              if (delay_ms)
++                      usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++      }
++
++      return sc2235_set_timings(sensor, mode);
++}
++
++static int sc2235_set_autoexposure(struct sc2235_dev *sensor, bool on)
++{
++      return sc2235_mod_reg(sensor, SC2235_REG_AEC_PK_MANUAL,
++                              BIT(0), on ? 0 : BIT(0));
++}
++
++static int sc2235_get_exposure(struct sc2235_dev *sensor)
++{
++      int exp = 0, ret = 0;
++      u8 temp;
++
++      ret = sc2235_read_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_HI, &temp);
++      if (ret)
++              return ret;
++      exp |= (int)temp << 8;
++      ret = sc2235_read_reg(sensor, SC2235_REG_AEC_PK_EXPOSURE_LO, &temp);
++      if (ret)
++              return ret;
++      exp |= (int)temp;
++
++      return exp >> 4;
++}
++
++static int sc2235_set_exposure(struct sc2235_dev *sensor, u32 exposure)
++{
++      int ret;
++
++      exposure <<= 4;
++
++      ret = sc2235_write_reg(sensor,
++                              SC2235_REG_AEC_PK_EXPOSURE_LO,
++                              exposure & 0xff);
++      if (ret)
++              return ret;
++      return sc2235_write_reg(sensor,
++                              SC2235_REG_AEC_PK_EXPOSURE_HI,
++                              (exposure >> 8) & 0xff);
++}
++
++static int sc2235_get_gain(struct sc2235_dev *sensor)
++{
++      u16 gain;
++      int ret;
++
++      ret = sc2235_read_reg16(sensor, SC2235_REG_AEC_PK_REAL_GAIN, &gain);
++      if (ret)
++              return ret;
++
++      return gain & 0x1fff;
++}
++
++static int sc2235_set_gain(struct sc2235_dev *sensor, int gain)
++{
++      return sc2235_write_reg16(sensor, SC2235_REG_AEC_PK_REAL_GAIN,
++                                      (u16)gain & 0x1fff);
++}
++
++static int sc2235_set_autogain(struct sc2235_dev *sensor, bool on)
++{
++      return sc2235_mod_reg(sensor, SC2235_REG_AEC_PK_MANUAL,
++                              BIT(1), on ? 0 : BIT(1));
++}
++
++#ifdef UNUSED_CODE
++static int sc2235_get_sysclk(struct sc2235_dev *sensor)
++{
++      return 0;
++}
++
++static int sc2235_set_night_mode(struct sc2235_dev *sensor)
++{
++      return 0;
++}
++
++static int sc2235_get_hts(struct sc2235_dev *sensor)
++{
++      u16 hts;
++      int ret;
++
++      ret = sc2235_read_reg16(sensor, SC2235_REG_TIMING_HTS, &hts);
++      if (ret)
++              return ret;
++      return hts;
++}
++#endif
++
++static int sc2235_get_vts(struct sc2235_dev *sensor)
++{
++      u16 vts;
++      int ret;
++
++      ret = sc2235_read_reg16(sensor, SC2235_REG_TIMING_VTS, &vts);
++      if (ret)
++              return ret;
++      return vts;
++}
++
++#ifdef UNUSED_CODE
++static int sc2235_set_vts(struct sc2235_dev *sensor, int vts)
++{
++      return sc2235_write_reg16(sensor, SC2235_REG_TIMING_VTS, vts);
++}
++
++static int sc2235_get_light_freq(struct sc2235_dev *sensor)
++{
++      return 0;
++}
++
++static int sc2235_set_bandingfilter(struct sc2235_dev *sensor)
++{
++      return 0;
++}
++
++static int sc2235_set_ae_target(struct sc2235_dev *sensor, int target)
++{
++      return 0;
++}
++
++static int sc2235_get_binning(struct sc2235_dev *sensor)
++{
++      return 0;
++}
++
++static int sc2235_set_binning(struct sc2235_dev *sensor, bool enable)
++{
++      return 0;
++}
++
++#endif
++
++static const struct sc2235_mode_info *
++sc2235_find_mode(struct sc2235_dev *sensor, enum sc2235_frame_rate fr,
++               int width, int height, bool nearest)
++{
++      const struct sc2235_mode_info *mode;
++
++      mode = v4l2_find_nearest_size(sc2235_mode_data,
++                                      ARRAY_SIZE(sc2235_mode_data),
++                                      hact, vact,
++                                      width, height);
++
++      if (!mode ||
++              (!nearest && (mode->hact != width || mode->vact != height)))
++              return NULL;
++
++      /* Check to see if the current mode exceeds the max frame rate */
++      if (sc2235_framerates[fr] > sc2235_framerates[mode->max_fps])
++              return NULL;
++
++      return mode;
++}
++
++static u64 sc2235_calc_pixel_rate(struct sc2235_dev *sensor)
++{
++      u64 rate;
++
++      rate = sensor->current_mode->vtot * sensor->current_mode->htot;
++      rate *= sc2235_framerates[sensor->current_fr];
++
++      return rate;
++}
++
++#ifdef UNUSED_CODE
++/*
++ * sc2235_set_dvp_pclk() - Calculate the clock tree configuration values
++ *                            for the dvp output.
++ *
++ * @rate: The requested bandwidth per lane in bytes per second.
++ *    'Bandwidth Per Lane' is calculated as:
++ *    rate = HTOT * VTOT * FPS;
++ *
++ * This function use the requested bandwidth to calculate:
++ * - rate = xclk * (64 - M) / (N * (S + 1));
++ *
++ */
++
++#define PLL_PREDIV  1
++#define PLL_SYSEL   0
++
++static int sc2235_set_dvp_pclk(struct sc2235_dev *sensor,
++                              unsigned long rate)
++{
++      u8 prediv, mult, sysdiv;
++      int ret = 0;
++
++      sc2235_calc_sys_clk(sensor, rate, &prediv, &mult,
++                              &sysdiv);
++
++
++      return ret;
++}
++
++/*
++ * if sensor changes inside scaling or subsampling
++ * change mode directly
++ */
++static int sc2235_set_mode_direct(struct sc2235_dev *sensor,
++                              const struct sc2235_mode_info *mode)
++{
++      if (!mode->reg_data)
++              return -EINVAL;
++
++      /* Write capture setting */
++      return sc2235_load_regs(sensor, mode);
++}
++#endif
++
++static int sc2235_set_mode(struct sc2235_dev *sensor)
++{
++#ifdef UNUSED_CODE
++      bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
++      const struct sc2235_mode_info *mode = sensor->current_mode;
++#endif
++      bool auto_gain = sensor->ctrls.auto_gain->val == 1;
++      int ret = 0;
++
++      /* auto gain and exposure must be turned off when changing modes */
++      if (auto_gain) {
++              ret = sc2235_set_autogain(sensor, false);
++              if (ret)
++                      return ret;
++      }
++#ifdef UNUSED_CODE
++      /* This issue will be addressed in the EVB board*/
++      /* This action will result in poor image display 2021 1111*/
++      if (auto_exp) {
++              ret = sc2235_set_autoexposure(sensor, false);
++              if (ret)
++                      goto restore_auto_gain;
++      }
++
++      rate = sc2235_calc_pixel_rate(sensor);
++
++      ret = sc2235_set_dvp_pclk(sensor, rate);
++      if (ret < 0)
++              return 0;
++
++      ret = sc2235_set_mode_direct(sensor, mode);
++      if (ret < 0)
++              goto restore_auto_exp_gain;
++
++      /* restore auto gain and exposure */
++      if (auto_gain)
++              sc2235_set_autogain(sensor, true);
++      if (auto_exp)
++              sc2235_set_autoexposure(sensor, true);
++
++
++      sensor->pending_mode_change = false;
++      sensor->last_mode = mode;
++      return 0;
++
++restore_auto_exp_gain:
++      if (auto_exp)
++              sc2235_set_autoexposure(sensor, true);
++restore_auto_gain:
++      if (auto_gain)
++              sc2235_set_autogain(sensor, true);
++#endif
++      return ret;
++}
++
++static int sc2235_set_framefmt(struct sc2235_dev *sensor,
++                              struct v4l2_mbus_framefmt *format);
++
++/* restore the last set video mode after chip power-on */
++static int sc2235_restore_mode(struct sc2235_dev *sensor)
++{
++      int ret;
++
++      /* first load the initial register values */
++      ret = sc2235_load_regs(sensor, &sc2235_mode_init_data);
++      if (ret < 0)
++              return ret;
++      sensor->last_mode = &sc2235_mode_init_data;
++      /* now restore the last capture mode */
++      ret = sc2235_set_mode(sensor);
++      if (ret < 0)
++              return ret;
++
++      return sc2235_set_framefmt(sensor, &sensor->fmt);
++}
++
++static void sc2235_power(struct sc2235_dev *sensor, bool enable)
++{
++      if (!sensor->pwdn_gpio)
++              return;
++      gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
++}
++
++static void sc2235_reset(struct sc2235_dev *sensor)
++{
++      if (!sensor->reset_gpio)
++              return;
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++
++      /* camera power cycle */
++      sc2235_power(sensor, false);
++      usleep_range(5000, 10000);
++      sc2235_power(sensor, true);
++      usleep_range(5000, 10000);
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 1);
++      usleep_range(1000, 2000);
++
++      gpiod_set_value_cansleep(sensor->reset_gpio, 0);
++      usleep_range(20000, 25000);
++}
++
++static int sc2235_set_power_on(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      int ret;
++
++      ret = clk_prepare_enable(sensor->xclk);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable clock\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = regulator_bulk_enable(SC2235_NUM_SUPPLIES,
++                                      sensor->supplies);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable regulators\n",
++                      __func__);
++              goto xclk_off;
++      }
++
++      sc2235_reset(sensor);
++      sc2235_power(sensor, true);
++
++      return 0;
++
++xclk_off:
++      clk_disable_unprepare(sensor->xclk);
++      return ret;
++}
++
++static int sc2235_set_power_off(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++
++      sc2235_power(sensor, false);
++      regulator_bulk_disable(SC2235_NUM_SUPPLIES, sensor->supplies);
++      clk_disable_unprepare(sensor->xclk);
++
++      return 0;
++}
++
++static int sc2235_set_power(struct sc2235_dev *sensor, bool on)
++{
++      int ret = 0;
++
++      if (on) {
++              pm_runtime_get_sync(&sensor->i2c_client->dev);
++
++              ret = sc2235_restore_mode(sensor);
++              if (ret)
++                      goto power_off;
++      }
++
++      if (!on)
++              pm_runtime_put_sync(&sensor->i2c_client->dev);
++
++      return 0;
++
++power_off:
++      pm_runtime_put_sync(&sensor->i2c_client->dev);
++
++      return ret;
++}
++
++static int sc2235_s_power(struct v4l2_subdev *sd, int on)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      int ret = 0;
++
++      mutex_lock(&sensor->lock);
++
++      ret = sc2235_set_power(sensor, !!on);
++      if (ret)
++              goto out;
++
++      mutex_unlock(&sensor->lock);
++      return 0;
++
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int sc2235_try_frame_interval(struct sc2235_dev *sensor,
++                              struct v4l2_fract *fi,
++                              u32 width, u32 height)
++{
++      const struct sc2235_mode_info *mode;
++      enum sc2235_frame_rate rate = SC2235_15_FPS;
++      int minfps, maxfps, best_fps, fps;
++      int i;
++
++      minfps = sc2235_framerates[SC2235_15_FPS];
++      maxfps = sc2235_framerates[SC2235_30_FPS];
++
++      if (fi->numerator == 0) {
++              fi->denominator = maxfps;
++              fi->numerator = 1;
++              rate = SC2235_30_FPS;
++              goto find_mode;
++      }
++
++      fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
++                      minfps, maxfps);
++
++      best_fps = minfps;
++      for (i = 0; i < ARRAY_SIZE(sc2235_framerates); i++) {
++              int curr_fps = sc2235_framerates[i];
++
++              if (abs(curr_fps - fps) < abs(best_fps - fps)) {
++                      best_fps = curr_fps;
++                      rate = i;
++              }
++      }
++
++      fi->numerator = 1;
++      fi->denominator = best_fps;
++
++find_mode:
++      mode = sc2235_find_mode(sensor, rate, width, height, false);
++      return mode ? rate : -EINVAL;
++}
++
++static int sc2235_get_fmt(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *format)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      struct v4l2_mbus_framefmt *fmt;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(&sensor->sd, state,
++                                              format->pad);
++      else
++              fmt = &sensor->fmt;
++
++      format->format = *fmt;
++
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int sc2235_try_fmt_internal(struct v4l2_subdev *sd,
++                              struct v4l2_mbus_framefmt *fmt,
++                              enum sc2235_frame_rate fr,
++                              const struct sc2235_mode_info **new_mode)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      const struct sc2235_mode_info *mode;
++      int i;
++
++      mode = sc2235_find_mode(sensor, fr, fmt->width, fmt->height, true);
++      if (!mode)
++              return -EINVAL;
++      fmt->width = mode->hact;
++      fmt->height = mode->vact;
++
++      if (new_mode)
++              *new_mode = mode;
++
++      for (i = 0; i < ARRAY_SIZE(sc2235_formats); i++)
++              if (sc2235_formats[i].code == fmt->code)
++                      break;
++      if (i >= ARRAY_SIZE(sc2235_formats))
++              i = 0;
++
++      fmt->code = sc2235_formats[i].code;
++      fmt->colorspace = sc2235_formats[i].colorspace;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++
++      return 0;
++}
++
++static int sc2235_set_fmt(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *format)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      const struct sc2235_mode_info *new_mode;
++      struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
++      struct v4l2_mbus_framefmt *fmt;
++      int ret;
++
++      if (format->pad != 0)
++              return -EINVAL;
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      ret = sc2235_try_fmt_internal(sd, mbus_fmt, 0, &new_mode);
++      if (ret)
++              goto out;
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              fmt = v4l2_subdev_get_try_format(sd, state, 0);
++      else
++              fmt = &sensor->fmt;
++
++      if (mbus_fmt->code != sensor->fmt.code)
++              sensor->pending_fmt_change = true;
++
++      *fmt = *mbus_fmt;
++
++      if (new_mode != sensor->current_mode) {
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++      if (new_mode->max_fps < sensor->current_fr) {
++              sensor->current_fr = new_mode->max_fps;
++              sensor->frame_interval.numerator = 1;
++              sensor->frame_interval.denominator =
++                      sc2235_framerates[sensor->current_fr];
++              sensor->current_mode = new_mode;
++              sensor->pending_mode_change = true;
++      }
++
++      __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                              sc2235_calc_pixel_rate(sensor));
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int sc2235_set_framefmt(struct sc2235_dev *sensor,
++                              struct v4l2_mbus_framefmt *format)
++{
++      int ret = 0;
++
++      switch (format->code) {
++      default:
++              return ret;
++      }
++      return ret;
++}
++
++/*
++ * Sensor Controls.
++ */
++
++static int sc2235_set_ctrl_hue(struct sc2235_dev *sensor, int value)
++{
++      int ret = 0;
++      return ret;
++}
++
++static int sc2235_set_ctrl_contrast(struct sc2235_dev *sensor, int value)
++{
++      int ret = 0;
++      return ret;
++}
++
++static int sc2235_set_ctrl_saturation(struct sc2235_dev *sensor, int value)
++{
++      int ret  = 0;
++      return ret;
++}
++
++static int sc2235_set_ctrl_white_balance(struct sc2235_dev *sensor, int awb)
++{
++      int ret = 0;
++      return ret;
++}
++
++static int sc2235_set_ctrl_exposure(struct sc2235_dev *sensor,
++                              enum v4l2_exposure_auto_type auto_exposure)
++{
++      struct sc2235_ctrls *ctrls = &sensor->ctrls;
++      bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO);
++      int ret = 0;
++
++      if (ctrls->auto_exp->is_new) {
++              ret = sc2235_set_autoexposure(sensor, auto_exp);
++              if (ret)
++                      return ret;
++      }
++
++      if (!auto_exp && ctrls->exposure->is_new) {
++              u16 max_exp = 0;
++
++              ret = sc2235_get_vts(sensor);
++              if (ret < 0)
++                      return ret;
++              max_exp += ret - 4;
++              ret = 0;
++
++              if (ctrls->exposure->val < max_exp)
++                      ret = sc2235_set_exposure(sensor, ctrls->exposure->val);
++      }
++
++      return ret;
++}
++
++static int sc2235_set_ctrl_gain(struct sc2235_dev *sensor, bool auto_gain)
++{
++      struct sc2235_ctrls *ctrls = &sensor->ctrls;
++      int ret = 0;
++
++      if (ctrls->auto_gain->is_new) {
++              ret = sc2235_set_autogain(sensor, auto_gain);
++              if (ret)
++                      return ret;
++      }
++
++      if (!auto_gain && ctrls->gain->is_new)
++              ret = sc2235_set_gain(sensor, ctrls->gain->val);
++
++      return ret;
++}
++
++static const char * const test_pattern_menu[] = {
++      "Disabled",
++      "Black bars",
++      "Auto Black bars",
++};
++
++#define SC2235_TEST_ENABLE            BIT(3)
++#define SC2235_TEST_BLACK             (3 << 0)
++
++static int sc2235_set_ctrl_test_pattern(struct sc2235_dev *sensor, int value)
++{
++      int ret = 0;
++      /*
++       *For 7110 platform, refer to 1125 FW code configuration. This operation will cause the image to be white.
++       */
++#ifdef UNUSED_CODE
++      ret = sc2235_mod_reg(sensor, SC2235_REG_TEST_SET0, BIT(3),
++                              !!value << 3);
++
++      ret |= sc2235_mod_reg(sensor, SC2235_REG_TEST_SET1, BIT(6),
++                              (value >> 1) << 6);
++#endif
++      return ret;
++}
++
++static int sc2235_set_ctrl_light_freq(struct sc2235_dev *sensor, int value)
++{
++      return 0;
++}
++
++static int sc2235_set_ctrl_hflip(struct sc2235_dev *sensor, int value)
++{
++      return sc2235_mod_reg(sensor, SC2235_REG_TIMING_TC_REG21,
++                              BIT(2) | BIT(1),
++                              (!(value ^ sensor->upside_down)) ?
++                              (BIT(2) | BIT(1)) : 0);
++}
++
++static int sc2235_set_ctrl_vflip(struct sc2235_dev *sensor, int value)
++{
++      return sc2235_mod_reg(sensor, SC2235_REG_TIMING_TC_REG21,
++                              BIT(6) | BIT(5),
++                              (value ^ sensor->upside_down) ?
++                              (BIT(6) | BIT(5)) : 0);
++}
++
++static int sc2235_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      int val;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_AUTOGAIN:
++              val = sc2235_get_gain(sensor);
++              if (val < 0)
++                      return val;
++              sensor->ctrls.gain->val = val;
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              val = sc2235_get_exposure(sensor);
++              if (val < 0)
++                      return val;
++              sensor->ctrls.exposure->val = val;
++              break;
++      }
++
++      pm_runtime_put(&sensor->i2c_client->dev);
++
++      return 0;
++}
++
++static int sc2235_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      int ret;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      /*
++       * If the device is not powered up by the host driver do
++       * not apply any controls to H/W at this time. Instead
++       * the controls will be restored at start streaming time.
++       */
++      if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_AUTOGAIN:
++              ret = sc2235_set_ctrl_gain(sensor, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              ret = sc2235_set_ctrl_exposure(sensor, ctrl->val);
++              break;
++      case V4L2_CID_AUTO_WHITE_BALANCE:
++              ret = sc2235_set_ctrl_white_balance(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HUE:
++              ret = sc2235_set_ctrl_hue(sensor, ctrl->val);
++              break;
++      case V4L2_CID_CONTRAST:
++              ret = sc2235_set_ctrl_contrast(sensor, ctrl->val);
++              break;
++      case V4L2_CID_SATURATION:
++              ret = sc2235_set_ctrl_saturation(sensor, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = sc2235_set_ctrl_test_pattern(sensor, ctrl->val);
++              break;
++      case V4L2_CID_POWER_LINE_FREQUENCY:
++              ret = sc2235_set_ctrl_light_freq(sensor, ctrl->val);
++              break;
++      case V4L2_CID_HFLIP:
++              ret = sc2235_set_ctrl_hflip(sensor, ctrl->val);
++              break;
++      case V4L2_CID_VFLIP:
++              ret = sc2235_set_ctrl_vflip(sensor, ctrl->val);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      pm_runtime_put(&sensor->i2c_client->dev);
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops sc2235_ctrl_ops = {
++      .g_volatile_ctrl = sc2235_g_volatile_ctrl,
++      .s_ctrl = sc2235_s_ctrl,
++};
++
++static int sc2235_init_controls(struct sc2235_dev *sensor)
++{
++      const struct v4l2_ctrl_ops *ops = &sc2235_ctrl_ops;
++      struct sc2235_ctrls *ctrls = &sensor->ctrls;
++      struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++      int ret;
++
++      v4l2_ctrl_handler_init(hdl, 32);
++
++      /* we can use our own mutex for the ctrl lock */
++      hdl->lock = &sensor->lock;
++
++      /* Clock related controls */
++      ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++                                              0, INT_MAX, 1,
++                                              sc2235_calc_pixel_rate(sensor));
++
++      /* Auto/manual white balance */
++      ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++                                      V4L2_CID_AUTO_WHITE_BALANCE,
++                                      0, 1, 1, 1);
++      ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++                                              0, 4095, 1, 0);
++      ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++                                              0, 4095, 1, 0);
++      /* Auto/manual exposure */
++#ifdef UNUSED_CODE
++      /*
++       *For 7110 platform, This operation will cause the image to be white.
++       */
++      ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++                                              V4L2_CID_EXPOSURE_AUTO,
++                                              V4L2_EXPOSURE_MANUAL, 0,
++                                              V4L2_EXPOSURE_AUTO);
++      ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++                                      0, 65535, 1, 0);
++      /* Auto/manual gain */
++      ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++                                              0, 1, 1, 1);
++      ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++                                      0, 1023, 1, 0);
++#else
++      ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++                                              V4L2_CID_EXPOSURE_AUTO,
++                                              V4L2_EXPOSURE_MANUAL, 0,
++                                              1);
++      ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++                                      0, 65535, 1, 720);
++      /* Auto/manual gain */
++      ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++                                              0, 1, 1, 0);
++      ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++                                      0, 1023, 1, 0x10);
++#endif
++      ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
++                                              0, 255, 1, 64);
++      ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
++                                      0, 359, 1, 0);
++      ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
++                                              0, 255, 1, 0);
++      ctrls->test_pattern =
++              v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++                                      ARRAY_SIZE(test_pattern_menu) - 1,
++                                      0, 0, test_pattern_menu);   //0x02
++      ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++                                      0, 1, 1, 1);
++      ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++                                      0, 1, 1, 0);
++
++      ctrls->light_freq =
++              v4l2_ctrl_new_std_menu(hdl, ops,
++                                      V4L2_CID_POWER_LINE_FREQUENCY,
++                                      V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++                                      V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++
++      if (hdl->error) {
++              ret = hdl->error;
++              goto free_ctrls;
++      }
++
++      ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++      ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++      v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++      v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
++      v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
++
++      sensor->sd.ctrl_handler = hdl;
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(hdl);
++      return ret;
++}
++
++static int sc2235_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      if (fse->pad != 0)
++              return -EINVAL;
++      if (fse->index >= SC2235_NUM_MODES)
++              return -EINVAL;
++
++      fse->min_width =
++              sc2235_mode_data[fse->index].hact;
++      fse->max_width = fse->min_width;
++      fse->min_height =
++              sc2235_mode_data[fse->index].vact;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static int sc2235_enum_frame_interval(
++      struct v4l2_subdev *sd,
++      struct v4l2_subdev_state *state,
++      struct v4l2_subdev_frame_interval_enum *fie)
++{
++      struct v4l2_fract tpf;
++      int i;
++
++      if (fie->pad != 0)
++              return -EINVAL;
++
++      if (fie->index >= SC2235_NUM_FRAMERATES)
++              return -EINVAL;
++
++      tpf.numerator = 1;
++      tpf.denominator = sc2235_framerates[fie->index];
++
++      for (i = 0; i < SC2235_NUM_MODES; i++) {
++              if (fie->width == sc2235_mode_data[i].hact &&
++                      fie->height == sc2235_mode_data[i].vact)
++                      break;
++      }
++      if (i == SC2235_NUM_MODES)
++              return -ENOTTY;
++
++      fie->interval = tpf;
++      return 0;
++}
++
++static int sc2235_g_frame_interval(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_frame_interval *fi)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++
++      mutex_lock(&sensor->lock);
++      fi->interval = sensor->frame_interval;
++      mutex_unlock(&sensor->lock);
++
++      return 0;
++}
++
++static int sc2235_s_frame_interval(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_frame_interval *fi)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      const struct sc2235_mode_info *mode;
++      int frame_rate, ret = 0;
++
++      if (fi->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming) {
++              ret = -EBUSY;
++              goto out;
++      }
++
++      mode = sensor->current_mode;
++
++      frame_rate = sc2235_try_frame_interval(sensor, &fi->interval,
++                                              mode->hact, mode->vact);
++      if (frame_rate < 0) {
++              /* Always return a valid frame interval value */
++              fi->interval = sensor->frame_interval;
++              goto out;
++      }
++
++      mode = sc2235_find_mode(sensor, frame_rate, mode->hact,
++                              mode->vact, true);
++      if (!mode) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      if (mode != sensor->current_mode ||
++          frame_rate != sensor->current_fr) {
++              sensor->current_fr = frame_rate;
++              sensor->frame_interval = fi->interval;
++              sensor->current_mode = mode;
++              sensor->pending_mode_change = true;
++
++              __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
++                                      sc2235_calc_pixel_rate(sensor));
++      }
++out:
++      mutex_unlock(&sensor->lock);
++      return ret;
++}
++
++static int sc2235_enum_mbus_code(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_mbus_code_enum *code)
++{
++      if (code->pad != 0)
++              return -EINVAL;
++      if (code->index >= ARRAY_SIZE(sc2235_formats))
++              return -EINVAL;
++
++      code->code = sc2235_formats[code->index].code;
++      return 0;
++}
++
++static int sc2235_stream_start(struct sc2235_dev *sensor, int enable)
++{
++      return sc2235_mod_reg(sensor, SC2235_REG_STREAM_ON, BIT(0), !!enable);
++}
++
++static int sc2235_s_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++      int ret = 0;
++
++      if (enable) {
++              ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
++              if (ret)
++                      return ret;
++      }
++
++      mutex_lock(&sensor->lock);
++
++      if (sensor->streaming == !enable) {
++              if (enable && sensor->pending_mode_change) {
++                      ret = sc2235_set_mode(sensor);
++                      if (ret)
++                              goto out;
++              }
++
++              if (enable && sensor->pending_fmt_change) {
++                      ret = sc2235_set_framefmt(sensor, &sensor->fmt);
++                      if (ret)
++                              goto out;
++                      sensor->pending_fmt_change = false;
++              }
++
++              ret = sc2235_stream_start(sensor, enable);
++              if (ret)
++                      goto out;
++      }
++      sensor->streaming += enable ? 1 : -1;
++      WARN_ON(sensor->streaming < 0);
++out:
++      mutex_unlock(&sensor->lock);
++
++      return ret;
++}
++
++static const struct v4l2_subdev_core_ops sc2235_core_ops = {
++      .s_power = sc2235_s_power,
++      .log_status = v4l2_ctrl_subdev_log_status,
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops sc2235_video_ops = {
++      .g_frame_interval = sc2235_g_frame_interval,
++      .s_frame_interval = sc2235_s_frame_interval,
++      .s_stream = sc2235_s_stream,
++};
++
++static const struct v4l2_subdev_pad_ops sc2235_pad_ops = {
++      .enum_mbus_code = sc2235_enum_mbus_code,
++      .get_fmt = sc2235_get_fmt,
++      .set_fmt = sc2235_set_fmt,
++      .enum_frame_size = sc2235_enum_frame_size,
++      .enum_frame_interval = sc2235_enum_frame_interval,
++};
++
++static const struct v4l2_subdev_ops sc2235_subdev_ops = {
++      .core = &sc2235_core_ops,
++      .video = &sc2235_video_ops,
++      .pad = &sc2235_pad_ops,
++};
++
++static int sc2235_get_regulators(struct sc2235_dev *sensor)
++{
++      int i;
++
++      for (i = 0; i < SC2235_NUM_SUPPLIES; i++)
++              sensor->supplies[i].supply = sc2235_supply_name[i];
++
++      return devm_regulator_bulk_get(&sensor->i2c_client->dev,
++                                      SC2235_NUM_SUPPLIES,
++                                      sensor->supplies);
++}
++
++static int sc2235_check_chip_id(struct sc2235_dev *sensor)
++{
++      struct i2c_client *client = sensor->i2c_client;
++      int ret = 0;
++      u16 chip_id;
++
++      ret = sc2235_read_reg16(sensor, SC2235_REG_CHIP_ID, &chip_id);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to read chip identifier\n",
++                      __func__);
++              return ret;
++      }
++
++      if (chip_id != SC2235_CHIP_ID) {
++              dev_err(&client->dev, "%s: wrong chip identifier, expected 0x%x, got 0x%x\n",
++                      __func__, SC2235_CHIP_ID, chip_id);
++              return -ENXIO;
++      }
++      dev_err(&client->dev, "%s: chip identifier, got 0x%x\n",
++              __func__, chip_id);
++
++      return 0;
++}
++
++static int sc2235_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct fwnode_handle *endpoint;
++      struct sc2235_dev *sensor;
++      struct v4l2_mbus_framefmt *fmt;
++      u32 rotation;
++      int ret;
++
++      sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
++      if (!sensor)
++              return -ENOMEM;
++
++      sensor->i2c_client = client;
++
++      fmt = &sensor->fmt;
++      fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++      fmt->width = 1920;
++      fmt->height = 1080;
++      fmt->field = V4L2_FIELD_NONE;
++      sensor->frame_interval.numerator = 1;
++      sensor->frame_interval.denominator = sc2235_framerates[SC2235_30_FPS];
++      sensor->current_fr = SC2235_30_FPS;
++      sensor->current_mode =
++              &sc2235_mode_data[SC2235_MODE_1080P_1920_1080];
++      sensor->last_mode = sensor->current_mode;
++
++      /* optional indication of physical rotation of sensor */
++      ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
++                                      &rotation);
++      if (!ret) {
++              switch (rotation) {
++              case 180:
++                      sensor->upside_down = true;
++                      fallthrough;
++              case 0:
++                      break;
++              default:
++                      dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
++                              rotation);
++              }
++      }
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
++                                              NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
++      fwnode_handle_put(endpoint);
++      if (ret) {
++              dev_err(dev, "Could not parse endpoint\n");
++              return ret;
++      }
++
++      if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL) {
++              dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type);
++              return -EINVAL;
++      }
++
++      /* get system clock (xclk) */
++      sensor->xclk = devm_clk_get(dev, "xclk");
++      if (IS_ERR(sensor->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(sensor->xclk);
++      }
++
++      sensor->xclk_freq = clk_get_rate(sensor->xclk);
++      if (sensor->xclk_freq < SC2235_XCLK_MIN ||
++          sensor->xclk_freq > SC2235_XCLK_MAX) {
++              dev_err(dev, "xclk frequency out of range: %d Hz\n",
++                      sensor->xclk_freq);
++              return -EINVAL;
++      }
++
++      sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
++                                              GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->pwdn_gpio))
++              return PTR_ERR(sensor->pwdn_gpio);
++
++      sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++                                              GPIOD_OUT_HIGH);
++      if (IS_ERR(sensor->reset_gpio))
++              return PTR_ERR(sensor->reset_gpio);
++
++      v4l2_i2c_subdev_init(&sensor->sd, client, &sc2235_subdev_ops);
++
++      sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++                      V4L2_SUBDEV_FL_HAS_EVENTS;
++      sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
++      sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++      ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
++      if (ret)
++              return ret;
++
++      ret = sc2235_get_regulators(sensor);
++      if (ret)
++              return ret;
++      mutex_init(&sensor->lock);
++
++      ret = sc2235_set_power_on(dev);
++      if (ret) {
++              dev_err(dev, "failed to power on\n");
++              goto entity_cleanup;
++      }
++
++      ret = sc2235_check_chip_id(sensor);
++      if (ret)
++              goto entity_power_off;
++
++      ret = sc2235_init_controls(sensor);
++      if (ret)
++              goto entity_power_off;
++
++      ret = v4l2_async_register_subdev_sensor(&sensor->sd);
++      if (ret)
++              goto free_ctrls;
++
++      pm_runtime_set_active(dev);
++      pm_runtime_enable(dev);
++
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++entity_power_off:
++      sc2235_set_power_off(dev);
++entity_cleanup:
++      media_entity_cleanup(&sensor->sd.entity);
++      mutex_destroy(&sensor->lock);
++      return ret;
++}
++
++static void sc2235_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct sc2235_dev *sensor = to_sc2235_dev(sd);
++
++      v4l2_async_unregister_subdev(&sensor->sd);
++      media_entity_cleanup(&sensor->sd.entity);
++      v4l2_ctrl_handler_free(&sensor->ctrls.handler);
++      mutex_destroy(&sensor->lock);
++
++      pm_runtime_disable(&client->dev);
++      if (!pm_runtime_status_suspended(&client->dev))
++              sc2235_set_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct i2c_device_id sc2235_id[] = {
++      { "sc2235", 0 },
++      {},
++};
++MODULE_DEVICE_TABLE(i2c, sc2235_id);
++
++static const struct of_device_id sc2235_dt_ids[] = {
++      { .compatible = "smartsens,sc2235" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, sc2235_dt_ids);
++
++static const struct dev_pm_ops sc2235_pm_ops = {
++      SET_RUNTIME_PM_OPS(sc2235_set_power_off, sc2235_set_power_on, NULL)
++};
++
++static struct i2c_driver sc2235_i2c_driver = {
++      .driver = {
++              .name  = "sc2235",
++              .of_match_table = sc2235_dt_ids,
++              .pm = &sc2235_pm_ops,
++      },
++      .id_table = sc2235_id,
++      .probe = sc2235_probe,
++      .remove   = sc2235_remove,
++};
++
++module_i2c_driver(sc2235_i2c_driver);
++
++MODULE_DESCRIPTION("SC2235 Camera Subdev Driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_common.h
+@@ -0,0 +1,185 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_COMMON_H
++#define STF_COMMON_H
++
++#include <linux/kern_levels.h>
++
++// #define STF_DEBUG
++
++// #define USE_CSIDPHY_ONE_CLK_MODE 1
++
++enum {
++      ST_DVP = 0x0001,
++      ST_CSIPHY = 0x0002,
++      ST_CSI = 0x0004,
++      ST_ISP = 0x0008,
++      ST_VIN = 0x0010,
++      ST_VIDEO = 0x0020,
++      ST_CAMSS = 0x0040,
++      ST_SENSOR = 0x0080,
++};
++
++enum {
++      ST_NONE = 0x00,
++      ST_ERR = 0x01,
++      ST_WARN = 0x02,
++      ST_INFO = 0x03,
++      ST_DEBUG = 0x04,
++};
++
++extern unsigned int stdbg_level;
++extern unsigned int stdbg_mask;
++
++#define ST_MODULE2STRING(__module) ({ \
++      char *__str; \
++      \
++      switch (__module) { \
++      case ST_DVP: \
++              __str = "st_dvp"; \
++              break; \
++      case ST_CSIPHY: \
++              __str = "st_csiphy"; \
++              break; \
++      case ST_CSI: \
++              __str = "st_csi"; \
++              break; \
++      case ST_ISP: \
++              __str = "st_isp"; \
++              break; \
++      case ST_VIN: \
++              __str = "st_vin"; \
++              break; \
++      case ST_VIDEO: \
++              __str = "st_video"; \
++              break; \
++      case ST_CAMSS: \
++              __str = "st_camss"; \
++              break; \
++      case ST_SENSOR: \
++              __str = "st_sensor"; \
++              break; \
++      default: \
++              __str = "unknow"; \
++              break; \
++      } \
++      \
++      __str; \
++      })
++
++#define st_debug(module, __fmt, arg...)       \
++      do { \
++              if (stdbg_level > ST_INFO) { \
++                      if (stdbg_mask & module)  \
++                              pr_err("[%s] debug: " __fmt, \
++                                              ST_MODULE2STRING(module), \
++                                              ## arg); \
++              } \
++      } while (0)
++
++#define st_info(module, __fmt, arg...)        \
++      do { \
++              if (stdbg_level > ST_WARN) { \
++                      if (stdbg_mask & module)  \
++                              pr_err("[%s] info: " __fmt, \
++                                              ST_MODULE2STRING(module), \
++                                              ## arg); \
++              } \
++      } while (0)
++
++#define st_warn(module, __fmt, arg...)        \
++      do { \
++              if (stdbg_level > ST_ERR) { \
++                      if (stdbg_mask & module)  \
++                              pr_err("[%s] warn: " __fmt, \
++                                              ST_MODULE2STRING(module), \
++                                              ## arg); \
++              } \
++      } while (0)
++
++#define st_err(module, __fmt, arg...) \
++      do { \
++              if (stdbg_level > ST_NONE) { \
++                      if (stdbg_mask & module) \
++                              pr_err("[%s] error: " __fmt, \
++                                              ST_MODULE2STRING(module), \
++                                              ## arg); \
++              } \
++      } while (0)
++
++#define st_err_ratelimited(module, fmt, ...)                 \
++      do {                                                                    \
++              static DEFINE_RATELIMIT_STATE(_rs,                              \
++                                              DEFAULT_RATELIMIT_INTERVAL,     \
++                                              DEFAULT_RATELIMIT_BURST);       \
++              if (__ratelimit(&_rs) && (stdbg_level > ST_NONE)) {             \
++                      if (stdbg_mask & module)                                \
++                              pr_err("[%s] error: " fmt,                      \
++                                              ST_MODULE2STRING(module),       \
++                                              ##__VA_ARGS__);                 \
++              } \
++      } while (0)
++
++#define set_bits(p, v, b, m)  (((p) & ~(m)) | ((v) << (b)))
++
++static inline u32 reg_read(void __iomem *base, u32 reg)
++{
++      return ioread32(base + reg);
++}
++
++static inline void reg_write(void __iomem *base, u32 reg, u32 val)
++{
++      iowrite32(val, base + reg);
++}
++
++static inline void reg_set_bit(void __iomem *base, u32 reg, u32 mask, u32 val)
++{
++      u32 value;
++
++      value = ioread32(base + reg) & ~mask;
++      val &= mask;
++      val |= value;
++      iowrite32(val, base + reg);
++}
++
++static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
++{
++      iowrite32(ioread32(base + reg) | mask, base + reg);
++}
++
++static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
++{
++      iowrite32(ioread32(base + reg) & ~mask, base + reg);
++}
++
++static inline void reg_set_highest_bit(void __iomem *base, u32 reg)
++{
++      u32 val;
++
++      val = ioread32(base + reg);
++      val &= ~(0x1 << 31);
++      val |= (0x1 & 0x1) << 31;
++      iowrite32(val, base + reg);
++}
++
++static inline void reg_clr_highest_bit(void __iomem *base, u32 reg)
++{
++      u32 val;
++
++      val = ioread32(base + reg);
++      val &= ~(0x1 << 31);
++      val |= (0x0 & 0x1) << 31;
++      iowrite32(val, base + reg);
++}
++
++static inline void print_reg(unsigned int module, void __iomem *base, u32 reg)
++{
++      //st_debug(module, "REG 0x%x = 0x%x\n",
++      //              base + reg, ioread32(base + reg));
++}
++
++#endif /* STF_COMMON_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi.c
+@@ -0,0 +1,465 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static const struct csi_format csi_formats_sink[] = {
++      { MEDIA_BUS_FMT_UYVY8_2X8, 16},
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++/* this bpp need see csi controllor */
++static const struct csi_format csi_formats_src[] = {
++      { MEDIA_BUS_FMT_AYUV8_1X32, 32},
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 16},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 16},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 16},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 16},
++};
++
++static int csi_find_format(u32 code,
++              const struct csi_format *formats,
++              unsigned int nformats)
++{
++      int i;
++
++      for (i = 0; i < nformats; i++)
++              if (formats[i].code == code)
++                      return i;
++      return -EINVAL;
++}
++
++int stf_csi_subdev_init(struct stfcamss *stfcamss)
++{
++      struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
++
++      csi_dev->s_type = SENSOR_VIN;
++      csi_dev->hw_ops = &csi_ops;
++      csi_dev->stfcamss = stfcamss;
++      csi_dev->formats_sink = csi_formats_sink;
++      csi_dev->nformats_sink = ARRAY_SIZE(csi_formats_sink);
++      csi_dev->formats_src = csi_formats_src;
++      csi_dev->nformats_src = ARRAY_SIZE(csi_formats_src);
++      mutex_init(&csi_dev->stream_lock);
++      return 0;
++}
++
++static int csi_set_power(struct v4l2_subdev *sd, int on)
++{
++      struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++
++      csi_dev->hw_ops->csi_power_on(csi_dev, (u8)on);
++      return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__csi_get_format(struct stf_csi_dev *csi_dev,
++              struct v4l2_subdev_state *state,
++              unsigned int pad,
++              enum v4l2_subdev_format_whence which)
++{
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_format(&csi_dev->subdev, state, pad);
++
++      return &csi_dev->fmt[pad];
++}
++
++static u32 code_to_data_type(int code)
++{
++      switch (code) {
++      case MEDIA_BUS_FMT_SRGGB10_1X10:
++      case MEDIA_BUS_FMT_SGRBG10_1X10:
++      case MEDIA_BUS_FMT_SGBRG10_1X10:
++      case MEDIA_BUS_FMT_SBGGR10_1X10:
++              return 0x2b;
++      case MEDIA_BUS_FMT_UYVY8_2X8:
++              return 0x1E;
++      default:
++              return 0x2b;
++      }
++}
++
++static int csi_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++      int ret = 0;
++      u32 code, width, dt;
++      u8 bpp;
++
++      format = __csi_get_format(csi_dev, NULL, STF_CSI_PAD_SINK,
++                              V4L2_SUBDEV_FORMAT_ACTIVE);
++      if (format == NULL)
++              return -EINVAL;
++
++      width = format->width;
++
++      ret = csi_find_format(format->code,
++                              csi_dev->formats_sink,
++                              csi_dev->nformats_sink);
++      if (ret < 0)
++              return ret;
++
++      code = csi_dev->formats_sink[ret].code;
++      bpp = csi_dev->formats_src[ret].bpp;
++      dt = code_to_data_type(code);
++
++      mutex_lock(&csi_dev->stream_lock);
++      if (enable) {
++              if (csi_dev->stream_count == 0) {
++                      csi_dev->hw_ops->csi_clk_enable(csi_dev);
++                      csi_dev->hw_ops->csi_stream_set(csi_dev, enable, dt, width, bpp);
++              }
++              csi_dev->stream_count++;
++      } else {
++              if (csi_dev->stream_count == 0)
++                      goto exit;
++              if (csi_dev->stream_count == 1) {
++                      csi_dev->hw_ops->csi_stream_set(csi_dev, enable, dt, width, bpp);
++                      csi_dev->hw_ops->csi_clk_disable(csi_dev);
++              }
++              csi_dev->stream_count--;
++      }
++exit:
++      mutex_unlock(&csi_dev->stream_lock);
++      return 0;
++}
++
++static void csi_try_format(struct stf_csi_dev *csi_dev,
++                      struct v4l2_subdev_state *state,
++                      unsigned int pad,
++                      struct v4l2_mbus_framefmt *fmt,
++                      enum v4l2_subdev_format_whence which)
++{
++      unsigned int i;
++
++      switch (pad) {
++      case STF_CSI_PAD_SINK:
++              /* Set format on sink pad */
++
++              for (i = 0; i < csi_dev->nformats_sink; i++)
++                      if (fmt->code == csi_dev->formats_sink[i].code)
++                              break;
++
++              if (i >= csi_dev->nformats_sink)
++                      fmt->code = csi_dev->formats_sink[0].code;
++
++              fmt->width = clamp_t(u32,
++                              fmt->width,
++                              STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              fmt->height = clamp_t(u32,
++                              fmt->height,
++                              STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++
++
++              fmt->field = V4L2_FIELD_NONE;
++              fmt->colorspace = V4L2_COLORSPACE_SRGB;
++              fmt->flags = 0;
++
++              break;
++
++      case STF_CSI_PAD_SRC:
++              /* Set format on src pad */
++
++              for (i = 0; i < csi_dev->nformats_src; i++)
++                      if (fmt->code == csi_dev->formats_src[i].code)
++                              break;
++
++              if (i >= csi_dev->nformats_src)
++                      fmt->code = csi_dev->formats_src[0].code;
++
++              fmt->width = clamp_t(u32,
++                              fmt->width,
++                              STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              fmt->height = clamp_t(u32,
++                              fmt->height,
++                              STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++
++              fmt->field = V4L2_FIELD_NONE;
++              fmt->colorspace = V4L2_COLORSPACE_SRGB;
++              fmt->flags = 0;
++
++              break;
++      }
++}
++
++static int csi_enum_mbus_code(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++
++      if (code->index >= csi_dev->nformats_sink)
++              return -EINVAL;
++      if (code->pad == STF_CSI_PAD_SINK) {
++              code->code = csi_dev->formats_sink[code->index].code;
++      } else {
++              struct v4l2_mbus_framefmt *sink_fmt;
++
++              sink_fmt = __csi_get_format(csi_dev, state, STF_CSI_PAD_SINK,
++                                              code->which);
++
++              code->code = sink_fmt->code;
++              if (!code->code)
++                      return -EINVAL;
++      }
++      code->flags = 0;
++
++      return 0;
++}
++
++static int csi_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt format;
++
++      if (fse->index != 0)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = 1;
++      format.height = 1;
++      csi_try_format(csi_dev, state, fse->pad, &format, fse->which);
++      fse->min_width = format.width;
++      fse->min_height = format.height;
++
++      if (format.code != fse->code)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = -1;
++      format.height = -1;
++      csi_try_format(csi_dev, state, fse->pad, &format, fse->which);
++      fse->max_width = format.width;
++      fse->max_height = format.height;
++
++      return 0;
++}
++
++static int csi_get_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __csi_get_format(csi_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      fmt->format = *format;
++
++      return 0;
++}
++
++static int csi_set_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_csi_dev *csi_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++      struct v4l2_mbus_framefmt *format_src;
++      int ret;
++
++      format = __csi_get_format(csi_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      mutex_lock(&csi_dev->stream_lock);
++      if (csi_dev->stream_count) {
++              fmt->format = *format;
++              mutex_unlock(&csi_dev->stream_lock);
++              goto out;
++      } else {
++              csi_try_format(csi_dev, state, fmt->pad, &fmt->format, fmt->which);
++              *format = fmt->format;
++      }
++      mutex_unlock(&csi_dev->stream_lock);
++
++      if (fmt->pad == STF_CSI_PAD_SINK) {
++              format_src = __csi_get_format(csi_dev, state, STF_DVP_PAD_SRC,
++                                      fmt->which);
++
++              ret = csi_find_format(format->code, csi_dev->formats_sink,
++                              csi_dev->nformats_sink);
++              if (ret < 0)
++                      return ret;
++
++              format_src->code = csi_dev->formats_src[ret].code;
++              csi_try_format(csi_dev, state, STF_DVP_PAD_SRC, format_src,
++                                      fmt->which);
++      }
++out:
++      return 0;
++}
++
++static int csi_init_formats(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_fh *fh)
++{
++      struct v4l2_subdev_format format = {
++              .pad = STF_CSI_PAD_SINK,
++              .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++                              V4L2_SUBDEV_FORMAT_ACTIVE,
++              .format = {
++                      .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++                      .width = 1920,
++                      .height = 1080
++              }
++      };
++
++      return csi_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int csi_link_setup(struct media_entity *entity,
++                      const struct media_pad *local,
++                      const struct media_pad *remote, u32 flags)
++{
++      if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
++              (flags & MEDIA_LNK_FL_ENABLED)) {
++              struct v4l2_subdev *sd;
++              struct stf_csi_dev *csi_dev;
++              struct vin_line *line;
++
++              if (media_pad_remote_pad_first(local))
++                      return -EBUSY;
++
++              sd = media_entity_to_v4l2_subdev(entity);
++              csi_dev = v4l2_get_subdevdata(sd);
++
++              sd = media_entity_to_v4l2_subdev(remote->entity);
++              line = v4l2_get_subdevdata(sd);
++              if (line->sdev_type == VIN_DEV_TYPE)
++                      csi_dev->s_type = SENSOR_VIN;
++              if (line->sdev_type == ISP_DEV_TYPE)
++                      csi_dev->s_type = SENSOR_ISP;
++              st_info(ST_CSI, "CSI device sensor type: %d\n", csi_dev->s_type);
++      }
++
++      if ((local->flags & MEDIA_PAD_FL_SINK) &&
++              (flags & MEDIA_LNK_FL_ENABLED)) {
++              struct v4l2_subdev *sd;
++              struct stf_csi_dev *csi_dev;
++              struct stf_csiphy_dev *csiphy_dev;
++
++              if (media_pad_remote_pad_first(local))
++                      return -EBUSY;
++
++              sd = media_entity_to_v4l2_subdev(entity);
++              csi_dev = v4l2_get_subdevdata(sd);
++
++              sd = media_entity_to_v4l2_subdev(remote->entity);
++              csiphy_dev = v4l2_get_subdevdata(sd);
++
++              st_info(ST_CSI, "CSI0 link to csiphy0\n");
++      }
++
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops csi_core_ops = {
++      .s_power = csi_set_power,
++};
++
++static const struct v4l2_subdev_video_ops csi_video_ops = {
++      .s_stream = csi_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops csi_pad_ops = {
++      .enum_mbus_code = csi_enum_mbus_code,
++      .enum_frame_size = csi_enum_frame_size,
++      .get_fmt = csi_get_format,
++      .set_fmt = csi_set_format,
++};
++
++static const struct v4l2_subdev_ops csi_v4l2_ops = {
++      .core = &csi_core_ops,
++      .video = &csi_video_ops,
++      .pad = &csi_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops csi_v4l2_internal_ops = {
++      .open = csi_init_formats,
++};
++
++static const struct media_entity_operations csi_media_ops = {
++      .link_setup = csi_link_setup,
++      .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_csi_register(struct stf_csi_dev *csi_dev, struct v4l2_device *v4l2_dev)
++{
++      struct v4l2_subdev *sd = &csi_dev->subdev;
++      struct device *dev = csi_dev->stfcamss->dev;
++      struct media_pad *pads = csi_dev->pads;
++      int ret;
++
++      csi_dev->mipirx_1p8 = devm_regulator_get(dev, "mipirx_1p8");
++      if (IS_ERR(csi_dev->mipirx_1p8))
++              return PTR_ERR(csi_dev->mipirx_1p8);
++
++      csi_dev->mipirx_0p9 = devm_regulator_get(dev, "mipirx_0p9");
++      if (IS_ERR(csi_dev->mipirx_0p9))
++              return PTR_ERR(csi_dev->mipirx_0p9);
++
++      v4l2_subdev_init(sd, &csi_v4l2_ops);
++      sd->internal_ops = &csi_v4l2_internal_ops;
++      sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++      snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++              STF_CSI_NAME, 0);
++      v4l2_set_subdevdata(sd, csi_dev);
++
++      ret = csi_init_formats(sd, NULL);
++      if (ret < 0) {
++              dev_err(dev, "Failed to init format: %d\n", ret);
++              return ret;
++      }
++
++      pads[STF_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++      pads[STF_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++      sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++      sd->entity.ops = &csi_media_ops;
++      ret = media_entity_pads_init(&sd->entity, STF_CSI_PADS_NUM, pads);
++      if (ret < 0) {
++              dev_err(dev, "Failed to init media entity: %d\n", ret);
++              return ret;
++      }
++
++      ret = v4l2_device_register_subdev(v4l2_dev, sd);
++      if (ret < 0) {
++              dev_err(dev, "Failed to register subdev: %d\n", ret);
++              goto err_sreg;
++      }
++
++      return 0;
++
++err_sreg:
++      media_entity_cleanup(&sd->entity);
++      return ret;
++}
++
++int stf_csi_unregister(struct stf_csi_dev *csi_dev)
++{
++      v4l2_device_unregister_subdev(&csi_dev->subdev);
++      media_entity_cleanup(&csi_dev->subdev.entity);
++      mutex_destroy(&csi_dev->stream_lock);
++      return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi.h
+@@ -0,0 +1,61 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_CSI_H
++#define STF_CSI_H
++
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_CSI_NAME "stf_csi"
++
++#define STF_CSI_PAD_SINK     0
++#define STF_CSI_PAD_SRC      1
++#define STF_CSI_PADS_NUM     2
++
++struct csi_format {
++      u32 code;
++      u8 bpp;
++};
++
++struct stf_csi_dev;
++
++struct csi_hw_ops {
++      int (*csi_power_on)(struct stf_csi_dev *csi_dev, u8 on);
++      int (*csi_clk_enable)(struct stf_csi_dev *csi_dev);
++      int (*csi_clk_disable)(struct stf_csi_dev *csi_dev);
++      int (*csi_stream_set)(struct stf_csi_dev *csi_dev, int on,
++                            u32 dt, u32 width, u8 bpp);
++};
++
++struct stf_csi_dev {
++      struct stfcamss *stfcamss;
++      enum sensor_type s_type;
++      struct v4l2_subdev subdev;
++      struct media_pad pads[STF_CSI_PADS_NUM];
++      struct v4l2_mbus_framefmt fmt[STF_CSI_PADS_NUM];
++      const struct csi_format *formats_sink;
++      unsigned int nformats_sink;
++      const struct csi_format *formats_src;
++      unsigned int nformats_src;
++      struct csi_hw_ops *hw_ops;
++      struct mutex stream_lock;
++      int stream_count;
++      struct regulator *mipirx_1p8;
++      struct regulator *mipirx_0p9;
++};
++
++extern int stf_csi_subdev_init(struct stfcamss *stfcamss);
++extern int stf_csi_register(struct stf_csi_dev *csi_dev,
++                      struct v4l2_device *v4l2_dev);
++extern int stf_csi_unregister(struct stf_csi_dev *csi_dev);
++extern struct csi_hw_ops csi_ops;
++extern void dump_csi_reg(void *__iomem csibase);
++
++#endif /* STF_CSI_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
+@@ -0,0 +1,310 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/regmap.h>
++
++#define CSI2RX_DEVICE_CFG_REG                 0x000
++
++#define CSI2RX_SOFT_RESET_REG                 0x004
++#define CSI2RX_SOFT_RESET_PROTOCOL            BIT(1)
++#define CSI2RX_SOFT_RESET_FRONT                       BIT(0)
++
++#define CSI2RX_DPHY_LANE_CONTROL                0x040
++
++#define CSI2RX_STATIC_CFG_REG                 0x008
++#define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane)     \
++              ((plane) << (16 + (llane) * 4))
++#define CSI2RX_STATIC_CFG_LANES_MASK          GENMASK(11, 8)
++
++#define CSI2RX_STREAM_BASE(n)         (((n) + 1) * 0x100)
++
++#define CSI2RX_STREAM_CTRL_REG(n)             (CSI2RX_STREAM_BASE(n) + 0x000)
++#define CSI2RX_STREAM_CTRL_START              BIT(0)
++
++#define CSI2RX_STREAM_DATA_CFG_REG(n)         (CSI2RX_STREAM_BASE(n) + 0x008)
++#define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT   BIT(31)
++#define CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 BIT(7)
++#define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n)   BIT((n) + 16)
++
++#define CSI2RX_STREAM_CFG_REG(n)              (CSI2RX_STREAM_BASE(n) + 0x00c)
++#define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8)
++
++#define CSI2RX_LANES_MAX      4
++#define CSI2RX_STREAMS_MAX    4
++
++static int stf_csi_power_on(struct stf_csi_dev *csi_dev, u8 on)
++{
++      struct stfcamss *stfcamss = csi_dev->stfcamss;
++      int ret;
++
++      if (on) {
++              ret = regulator_enable(csi_dev->mipirx_1p8);
++              if (ret) {
++                      st_err(ST_CSI, "Cannot enable mipirx_1p8 regulator\n");
++                      goto err_1p8;
++              }
++
++              ret = regulator_enable(csi_dev->mipirx_0p9);
++              if (ret) {
++                      st_err(ST_CSI, "Cannot enable mipirx_0p9 regulator\n");
++                      goto err_0p9;
++              }
++      } else {
++              regulator_disable(csi_dev->mipirx_1p8);
++              regulator_disable(csi_dev->mipirx_0p9);
++      }
++
++      regmap_update_bits(stfcamss->stf_aon_syscon, stfcamss->aon_gp_reg,
++                              BIT(31), BIT(31));
++
++      return 0;
++
++err_0p9:
++      regulator_disable(csi_dev->mipirx_1p8);
++err_1p8:
++      return ret;
++
++}
++
++static int stf_csi_clk_enable(struct stf_csi_dev *csi_dev)
++{
++      struct stfcamss *stfcamss = csi_dev->stfcamss;
++
++      clk_set_rate(stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk, 198000000);
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF0].clk);
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF1].clk);
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF2].clk);
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF3].clk);
++
++      reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF0].rstc);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF1].rstc);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF2].rstc);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF3].rstc);
++
++      switch (csi_dev->s_type) {
++      case SENSOR_VIN:
++              reset_control_deassert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++              clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
++                      stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++              break;
++      case SENSOR_ISP:
++              clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
++                      stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++              break;
++      }
++
++      return 0;
++}
++
++static int stf_csi_clk_disable(struct stf_csi_dev *csi_dev)
++{
++      struct stfcamss *stfcamss = csi_dev->stfcamss;
++
++      switch (csi_dev->s_type) {
++      case SENSOR_VIN:
++              reset_control_assert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++              break;
++      case SENSOR_ISP:
++              break;
++      }
++
++      reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF3].rstc);
++      reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF2].rstc);
++      reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF1].rstc);
++      reset_control_assert(stfcamss->sys_rst[STFRST_PIXEL_CLK_IF0].rstc);
++
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF3].clk);
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF2].clk);
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF1].clk);
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PIXEL_CLK_IF0].clk);
++
++      return 0;
++}
++
++static void csi2rx_reset(void *reg_base)
++{
++      writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT,
++             reg_base + CSI2RX_SOFT_RESET_REG);
++
++      udelay(10);
++
++      writel(0, reg_base + CSI2RX_SOFT_RESET_REG);
++}
++
++static int csi2rx_start(struct stf_csi_dev *csi_dev, void *reg_base, u32 dt)
++{
++      struct stfcamss *stfcamss = csi_dev->stfcamss;
++      struct csi2phy_cfg *csiphy =
++              stfcamss->csiphy_dev->csiphy;
++      unsigned int i;
++      unsigned long lanes_used = 0;
++      u32 reg;
++
++      if (!csiphy) {
++              st_err(ST_CSI, "csiphy0 config not exist\n");
++              return -EINVAL;
++      }
++
++      csi2rx_reset(reg_base);
++
++      reg = csiphy->num_data_lanes << 8;
++      for (i = 0; i < csiphy->num_data_lanes; i++) {
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++              reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csiphy->data_lanes[i]);
++              set_bit(csiphy->data_lanes[i] - 1, &lanes_used);
++#else
++              reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1);
++              set_bit(i, &lanes_used);
++#endif
++      }
++
++      /*
++       * Even the unused lanes need to be mapped. In order to avoid
++       * to map twice to the same physical lane, keep the lanes used
++       * in the previous loop, and only map unused physical lanes to
++       * the rest of our logical lanes.
++       */
++      for (i = csiphy->num_data_lanes; i < CSI2RX_LANES_MAX; i++) {
++              unsigned int idx = find_first_zero_bit(&lanes_used,
++                                                     CSI2RX_LANES_MAX);
++
++              set_bit(idx, &lanes_used);
++              reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, idx + 1);
++      }
++
++      writel(reg, reg_base + CSI2RX_STATIC_CFG_REG);
++
++      // 0x40 DPHY_LANE_CONTROL
++      reg = 0;
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++      for (i = 0; i < csiphy->num_data_lanes; i++)
++              reg |= 1 << (csiphy->data_lanes[i] - 1)
++                      | 1 << (csiphy->data_lanes[i] + 11);
++#else
++      for (i = 0; i < csiphy->num_data_lanes; i++)
++              reg |= 1 << i | 1 << (i + 12);          //data_clane
++#endif
++
++      reg |= 1 << 4 | 1 << 16;                //clk_lane
++      writel(reg, reg_base + CSI2RX_DPHY_LANE_CONTROL);
++
++      /*
++       * Create a static mapping between the CSI virtual channels
++       * and the output stream.
++       *
++       * This should be enhanced, but v4l2 lacks the support for
++       * changing that mapping dynamically.
++       *
++       * We also cannot enable and disable independent streams here,
++       * hence the reference counting.
++       */
++      for (i = 0; i < CSI2RX_STREAMS_MAX; i++) {
++              writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
++                     reg_base + CSI2RX_STREAM_CFG_REG(i));
++
++              writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT |
++                     CSI2RX_STREAM_DATA_CFG_VC_SELECT(i) |
++                     CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 | dt,
++                     reg_base + CSI2RX_STREAM_DATA_CFG_REG(i));
++
++              writel(CSI2RX_STREAM_CTRL_START,
++                     reg_base + CSI2RX_STREAM_CTRL_REG(i));
++      }
++
++      return 0;
++}
++
++static void csi2rx_stop(struct stf_csi_dev *csi_dev, void *reg_base)
++{
++      unsigned int i;
++
++      for (i = 0; i < CSI2RX_STREAMS_MAX; i++)
++              writel(0, reg_base + CSI2RX_STREAM_CTRL_REG(i));
++}
++
++static void csi_set_vin_axiwr_pix(struct stf_csi_dev *csi_dev, u32 width, u8 bpp)
++{
++      struct stf_vin_dev *vin = csi_dev->stfcamss->vin;
++      u32 value = 0;
++      int cnfg_axiwr_pix_ct = 64 / bpp;
++
++      if (cnfg_axiwr_pix_ct == 2)
++              value = 0;
++      else if (cnfg_axiwr_pix_ct == 4)
++              value = 1;
++      else if (cnfg_axiwr_pix_ct == 8)
++              value = 2;
++
++      reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++              BIT(14)|BIT(13), value << 13);  //u0_vin_cnfg_axiwr0_pix_ct
++      reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++              BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2),
++              (width / cnfg_axiwr_pix_ct - 1)<<2);    //u0_vin_cnfg_axiwr0_pix_cnt_end
++}
++
++static int stf_csi_stream_set(struct stf_csi_dev *csi_dev,
++                            int on, u32 dt, u32 width, u8 bpp)
++{
++      struct stf_vin_dev *vin = csi_dev->stfcamss->vin;
++      void __iomem *reg_base = vin->csi2rx_base;
++
++      switch (csi_dev->s_type) {
++      case SENSOR_VIN:
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_20,
++                      BIT(3)|BIT(2)|BIT(1)|BIT(0),
++                      0<<0);          //u0_vin_cnfg_axiwr0_channel_sel
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++                      BIT(16)|BIT(15),
++                      0<<15);         //u0_vin_cnfg_axiwr0_pixel_high_bit_sel
++              csi_set_vin_axiwr_pix(csi_dev, width, bpp);
++              break;
++      case SENSOR_ISP:
++              reg_set_bit(vin->sysctrl_base,  SYSCONSAIF_SYSCFG_36,
++                      BIT(7)|BIT(6),
++                      0<<6);          //u0_vin_cnfg_mipi_byte_en_isp
++              reg_set_bit(vin->sysctrl_base,  SYSCONSAIF_SYSCFG_36,
++                      BIT(11)|BIT(10)|BIT(9)|BIT(8),
++                      0<<8);          //u0_vin_cnfg_mipi_channel_sel0
++              reg_set_bit(vin->sysctrl_base,  SYSCONSAIF_SYSCFG_36,
++                      BIT(16)|BIT(15)|BIT(14)|BIT(13),
++                      0<<13);         //u0_vin_cnfg_pix_num
++
++              if (dt == 0x2b)
++                      reg_set_bit(vin->sysctrl_base,  SYSCONSAIF_SYSCFG_36,
++                              BIT(12),
++                              1<<12);         //u0_vin_cnfg_p_i_mipi_header_en0
++              break;
++      }
++
++      if (on)
++              csi2rx_start(csi_dev, reg_base, dt);
++      else
++              csi2rx_stop(csi_dev, reg_base);
++
++      return 0;
++}
++
++void dump_csi_reg(void *__iomem csibase)
++{
++      st_info(ST_CSI, "DUMP CSI register:\n");
++      print_reg(ST_CSI, csibase, 0x00);
++      print_reg(ST_CSI, csibase, 0x04);
++      print_reg(ST_CSI, csibase, 0x08);
++      print_reg(ST_CSI, csibase, 0x10);
++
++      print_reg(ST_CSI, csibase, 0x40);
++      print_reg(ST_CSI, csibase, 0x48);
++      print_reg(ST_CSI, csibase, 0x4c);
++      print_reg(ST_CSI, csibase, 0x50);
++}
++
++struct csi_hw_ops csi_ops = {
++      .csi_power_on          = stf_csi_power_on,
++      .csi_clk_enable        = stf_csi_clk_enable,
++      .csi_clk_disable       = stf_csi_clk_disable,
++      .csi_stream_set        = stf_csi_stream_set,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c
+@@ -0,0 +1,357 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static const struct csiphy_format csiphy_formats_st7110[] = {
++      { MEDIA_BUS_FMT_UYVY8_2X8, 16},
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++int stf_csiphy_subdev_init(struct stfcamss *stfcamss)
++{
++      struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
++
++      csiphy_dev->hw_ops = &csiphy_ops;
++      csiphy_dev->stfcamss = stfcamss;
++      csiphy_dev->formats = csiphy_formats_st7110;
++      csiphy_dev->nformats = ARRAY_SIZE(csiphy_formats_st7110);
++      mutex_init(&csiphy_dev->stream_lock);
++      return 0;
++}
++
++static int csiphy_set_power(struct v4l2_subdev *sd, int on)
++{
++      return 0;
++}
++
++static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++
++      mutex_lock(&csiphy_dev->stream_lock);
++      if (enable) {
++              if (csiphy_dev->stream_count == 0) {
++                      csiphy_dev->hw_ops->csiphy_clk_enable(csiphy_dev);
++                      csiphy_dev->hw_ops->csiphy_config_set(csiphy_dev);
++                      csiphy_dev->hw_ops->csiphy_stream_set(csiphy_dev, 1);
++              }
++              csiphy_dev->stream_count++;
++      } else {
++              if (csiphy_dev->stream_count == 0)
++                      goto exit;
++              if (csiphy_dev->stream_count == 1) {
++                      csiphy_dev->hw_ops->csiphy_clk_disable(csiphy_dev);
++                      csiphy_dev->hw_ops->csiphy_stream_set(csiphy_dev, 0);
++              }
++              csiphy_dev->stream_count--;
++      }
++exit:
++      mutex_unlock(&csiphy_dev->stream_lock);
++
++      return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__csiphy_get_format(struct stf_csiphy_dev *csiphy_dev,
++              struct v4l2_subdev_state *state,
++              unsigned int pad,
++              enum v4l2_subdev_format_whence which)
++{
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_format(
++                              &csiphy_dev->subdev,
++                              state,
++                              pad);
++
++      return &csiphy_dev->fmt[pad];
++}
++
++static void csiphy_try_format(struct stf_csiphy_dev *csiphy_dev,
++                      struct v4l2_subdev_state *state,
++                      unsigned int pad,
++                      struct v4l2_mbus_framefmt *fmt,
++                      enum v4l2_subdev_format_whence which)
++{
++      unsigned int i;
++
++      switch (pad) {
++      case STF_CSIPHY_PAD_SINK:
++              /* Set format on sink pad */
++
++              for (i = 0; i < csiphy_dev->nformats; i++)
++                      if (fmt->code == csiphy_dev->formats[i].code)
++                              break;
++
++              if (i >= csiphy_dev->nformats)
++                      fmt->code = csiphy_dev->formats[0].code;
++
++              fmt->width = clamp_t(u32,
++                              fmt->width,
++                              STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              fmt->height = clamp_t(u32,
++                              fmt->height,
++                              STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++
++              fmt->field = V4L2_FIELD_NONE;
++              fmt->colorspace = V4L2_COLORSPACE_SRGB;
++              fmt->flags = 0;
++
++              break;
++
++      case STF_CSIPHY_PAD_SRC:
++
++              *fmt = *__csiphy_get_format(csiphy_dev,
++                              state,
++                              STF_CSIPHY_PAD_SINK, which);
++
++              break;
++      }
++}
++
++static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++
++      if (code->index >= csiphy_dev->nformats)
++              return -EINVAL;
++
++      if (code->pad == STF_CSIPHY_PAD_SINK) {
++              code->code = csiphy_dev->formats[code->index].code;
++      } else {
++              struct v4l2_mbus_framefmt *sink_fmt;
++
++              sink_fmt = __csiphy_get_format(csiphy_dev, state,
++                                      STF_CSIPHY_PAD_SINK,
++                                      code->which);
++
++              code->code = sink_fmt->code;
++              if (!code->code)
++                      return -EINVAL;
++      }
++      code->flags = 0;
++      return 0;
++}
++
++static int csiphy_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt format;
++
++      if (fse->index != 0)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = 1;
++      format.height = 1;
++      csiphy_try_format(csiphy_dev, state, fse->pad, &format, fse->which);
++      fse->min_width = format.width;
++      fse->min_height = format.height;
++
++      if (format.code != fse->code)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = -1;
++      format.height = -1;
++      csiphy_try_format(csiphy_dev, state, fse->pad, &format, fse->which);
++      fse->max_width = format.width;
++      fse->max_height = format.height;
++
++      return 0;
++}
++
++static int csiphy_get_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __csiphy_get_format(csiphy_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      fmt->format = *format;
++
++      return 0;
++}
++
++static int csiphy_set_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_csiphy_dev *csiphy_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __csiphy_get_format(csiphy_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      mutex_lock(&csiphy_dev->stream_lock);
++      if (csiphy_dev->stream_count) {
++              fmt->format = *format;
++              mutex_unlock(&csiphy_dev->stream_lock);
++              goto out;
++      } else {
++              csiphy_try_format(csiphy_dev, state, fmt->pad, &fmt->format, fmt->which);
++              *format = fmt->format;
++      }
++      mutex_unlock(&csiphy_dev->stream_lock);
++
++      /* Propagate the format from sink to source */
++      if (fmt->pad == STF_CSIPHY_PAD_SINK) {
++              format = __csiphy_get_format(csiphy_dev,
++                                      state,
++                                      STF_CSIPHY_PAD_SRC,
++                                      fmt->which);
++
++              *format = fmt->format;
++              csiphy_try_format(csiphy_dev, state, STF_CSIPHY_PAD_SRC, format,
++                                      fmt->which);
++      }
++out:
++      return 0;
++}
++
++static int csiphy_init_formats(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_fh *fh)
++{
++      struct v4l2_subdev_format format = {
++              .pad = STF_CSIPHY_PAD_SINK,
++              .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++                              V4L2_SUBDEV_FORMAT_ACTIVE,
++              .format = {
++                      .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++                      .width = 1920,
++                      .height = 1080
++              }
++      };
++
++      return csiphy_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int csiphy_link_setup(struct media_entity *entity,
++                      const struct media_pad *local,
++                      const struct media_pad *remote, u32 flags)
++{
++      if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
++              (flags & MEDIA_LNK_FL_ENABLED)) {
++              struct v4l2_subdev *sd;
++              struct stf_csiphy_dev *csiphy_dev;
++              struct stf_csi_dev *csi_dev;
++
++              if (media_pad_remote_pad_first(local))
++                      return -EBUSY;
++
++              sd = media_entity_to_v4l2_subdev(entity);
++              csiphy_dev = v4l2_get_subdevdata(sd);
++
++              sd = media_entity_to_v4l2_subdev(remote->entity);
++              csi_dev = v4l2_get_subdevdata(sd);
++              st_info(ST_CSIPHY, "CSIPHY0 link to CSI0\n");
++      }
++
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops csiphy_core_ops = {
++      .s_power = csiphy_set_power,
++};
++
++static const struct v4l2_subdev_video_ops csiphy_video_ops = {
++      .s_stream = csiphy_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
++      .enum_mbus_code = csiphy_enum_mbus_code,
++      .enum_frame_size = csiphy_enum_frame_size,
++      .get_fmt = csiphy_get_format,
++      .set_fmt = csiphy_set_format,
++};
++
++static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
++      .core = &csiphy_core_ops,
++      .video = &csiphy_video_ops,
++      .pad = &csiphy_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = {
++      .open = csiphy_init_formats,
++};
++
++static const struct media_entity_operations csiphy_media_ops = {
++      .link_setup = csiphy_link_setup,
++      .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_csiphy_register(struct stf_csiphy_dev *csiphy_dev,
++                      struct v4l2_device *v4l2_dev)
++{
++      struct v4l2_subdev *sd = &csiphy_dev->subdev;
++      struct device *dev = csiphy_dev->stfcamss->dev;
++      struct media_pad *pads = csiphy_dev->pads;
++      int ret;
++
++      v4l2_subdev_init(sd, &csiphy_v4l2_ops);
++      sd->internal_ops = &csiphy_v4l2_internal_ops;
++      sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++      snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++              STF_CSIPHY_NAME, 0);
++      v4l2_set_subdevdata(sd, csiphy_dev);
++
++      ret = csiphy_init_formats(sd, NULL);
++      if (ret < 0) {
++              dev_err(dev, "Failed to init format: %d\n", ret);
++              return ret;
++      }
++
++      pads[STF_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++      pads[STF_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++      sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++      sd->entity.ops = &csiphy_media_ops;
++      ret = media_entity_pads_init(&sd->entity, STF_CSIPHY_PADS_NUM, pads);
++      if (ret < 0) {
++              dev_err(dev, "Failed to init media entity: %d\n", ret);
++              return ret;
++      }
++
++      ret = v4l2_device_register_subdev(v4l2_dev, sd);
++      if (ret < 0) {
++              dev_err(dev, "Failed to register subdev: %d\n", ret);
++              goto err_sreg;
++      }
++
++      return 0;
++
++err_sreg:
++      media_entity_cleanup(&sd->entity);
++      return ret;
++}
++
++int stf_csiphy_unregister(struct stf_csiphy_dev *csiphy_dev)
++{
++      v4l2_device_unregister_subdev(&csiphy_dev->subdev);
++      media_entity_cleanup(&csiphy_dev->subdev.entity);
++      mutex_destroy(&csiphy_dev->stream_lock);
++      return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h
+@@ -0,0 +1,188 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_CSIPHY_H
++#define STF_CSIPHY_H
++
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_CSIPHY_NAME "stf_csiphy"
++
++#define STF_CSIPHY_PAD_SINK     0
++#define STF_CSIPHY_PAD_SRC      1
++#define STF_CSIPHY_PADS_NUM     2
++
++#define STF_CSI2_MAX_DATA_LANES      4
++
++union static_config {
++      u32 raw;
++      struct {
++              u32 sel                 : 2;
++              u32 rsvd_6              : 2;
++              u32 v2p0_support_enable : 1;
++              u32 rsvd_5              : 3;
++              u32 lane_nb             : 3;
++              u32 rsvd_4              : 5;
++              u32 dl0_map             : 3;
++              u32 rsvd_3              : 1;
++              u32 dl1_map             : 3;
++              u32 rsvd_2              : 1;
++              u32 dl2_map             : 3;
++              u32 rsvd_1              : 1;
++              u32 dl3_map             : 3;
++              u32 rsvd_0              : 1;
++      } bits;
++};
++
++union error_bypass_cfg {
++      u32 value;
++      struct {
++              u32 crc             :  1;
++              u32 ecc             :  1;
++              u32 data_id         :  1;
++              u32 rsvd_0          : 29;
++      };
++};
++
++union stream_monitor_ctrl {
++      u32 value;
++      struct {
++              u32 lb_vc             : 4;
++              u32 lb_en             : 1;
++              u32 timer_vc          : 4;
++              u32 timer_en          : 1;
++              u32 timer_eof         : 1;
++              u32 frame_mon_vc      : 4;
++              u32 frame_mon_en      : 1;
++              u32 frame_length      : 16;
++      };
++};
++
++union stream_cfg {
++      u32 value;
++      struct {
++              u32 interface_mode :  1;
++              u32 ls_le_mode     :  1;
++              u32 rsvd_3         :  2;
++              u32 num_pixels     :  2;
++              u32 rsvd_2         :  2;
++              u32 fifo_mode      :  2;
++              u32 rsvd_1         :  2;
++              u32 bpp_bypass     :  3;
++              u32 rsvd_0         :  1;
++              u32 fifo_fill      : 16;
++      };
++};
++
++union dphy_lane_ctrl {
++      u32 raw;
++      struct {
++              u32 dl0_en    : 1;
++              u32 dl1_en    : 1;
++              u32 dl2_en    : 1;
++              u32 dl3_en    : 1;
++              u32 cl_en     : 1;
++              u32 rsvd_1    : 7;
++              u32 dl0_reset : 1;
++              u32 dl1_reset : 1;
++              u32 dl2_reset : 1;
++              u32 dl3_reset : 1;
++              u32 cl_reset  : 1;
++              u32 rsvd_0    : 15;
++      } bits;
++};
++
++union dphy_lane_swap {
++      u32 raw;
++      struct {
++              u32 rx_1c2c_sel        : 1;
++              u32 lane_swap_clk      : 3;
++              u32 lane_swap_clk1     : 3;
++              u32 lane_swap_lan0     : 3;
++              u32 lane_swap_lan1     : 3;
++              u32 lane_swap_lan2     : 3;
++              u32 lane_swap_lan3     : 3;
++              u32 dpdn_swap_clk      : 1;
++              u32 dpdn_swap_clk1     : 1;
++              u32 dpdn_swap_lan0     : 1;
++              u32 dpdn_swap_lan1     : 1;
++              u32 dpdn_swap_lan2     : 1;
++              u32 dpdn_swap_lan3     : 1;
++              u32 hs_freq_chang_clk0 : 1;
++              u32 hs_freq_chang_clk1 : 1;
++              u32 reserved           : 5;
++      } bits;
++};
++
++union dphy_lane_en {
++      u32 raw;
++      struct {
++              u32 gpio_en             : 6;
++              u32 mp_test_mode_sel    : 5;
++              u32 mp_test_en          : 1;
++              u32 dphy_enable_lan0    : 1;
++              u32 dphy_enable_lan1    : 1;
++              u32 dphy_enable_lan2    : 1;
++              u32 dphy_enable_lan3    : 1;
++              u32 rsvd_0              : 16;
++      } bits;
++};
++
++struct csiphy_format {
++      u32 code;
++      u8 bpp;
++};
++
++struct csi2phy_cfg {
++      unsigned int flags;
++      unsigned char data_lanes[STF_CSI2_MAX_DATA_LANES];
++      unsigned char clock_lane;
++      unsigned char num_data_lanes;
++      bool lane_polarities[1 + STF_CSI2_MAX_DATA_LANES];
++};
++
++struct csi2phy_cfg2 {
++      unsigned char data_lanes[STF_CSI2_MAX_DATA_LANES];
++      unsigned char num_data_lanes;
++      unsigned char num_clks;
++      unsigned char clock_lane;
++      unsigned char clock1_lane;
++      bool lane_polarities[2 + STF_CSI2_MAX_DATA_LANES];
++};
++
++struct stf_csiphy_dev;
++
++struct csiphy_hw_ops {
++      int (*csiphy_clk_enable)(struct stf_csiphy_dev *csiphy_dev);
++      int (*csiphy_clk_disable)(struct stf_csiphy_dev *csiphy_dev);
++      int (*csiphy_config_set)(struct stf_csiphy_dev *csiphy_dev);
++      int (*csiphy_stream_set)(struct stf_csiphy_dev *csiphy_dev, int on);
++};
++
++struct stf_csiphy_dev {
++      struct stfcamss *stfcamss;
++      struct csi2phy_cfg *csiphy;
++      struct v4l2_subdev subdev;
++      struct media_pad pads[STF_CSIPHY_PADS_NUM];
++      struct v4l2_mbus_framefmt fmt[STF_CSIPHY_PADS_NUM];
++      const struct csiphy_format *formats;
++      unsigned int nformats;
++      struct csiphy_hw_ops *hw_ops;
++      struct mutex stream_lock;
++      int stream_count;
++};
++
++extern int stf_csiphy_subdev_init(struct stfcamss *stfcamss);
++extern int stf_csiphy_register(struct stf_csiphy_dev *csiphy_dev,
++                      struct v4l2_device *v4l2_dev);
++extern int stf_csiphy_unregister(struct stf_csiphy_dev *csiphy_dev);
++
++extern struct csiphy_hw_ops csiphy_ops;
++
++#endif /* STF_CSIPHY_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
+@@ -0,0 +1,335 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/sort.h>
++
++static int stf_csiphy_clk_set(struct stf_csiphy_dev *csiphy_dev, int on)
++{
++      struct stfcamss *stfcamss = csiphy_dev->stfcamss;
++      static int init_flag;
++      static struct mutex count_lock;
++      static int count;
++
++      if (!init_flag) {
++              init_flag = 1;
++              mutex_init(&count_lock);
++      }
++      mutex_lock(&count_lock);
++      if (on) {
++              clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_CFGCLK_IN].clk,
++                      99000000);
++              clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_REFCLK_IN].clk,
++                      49500000);
++              clk_set_rate(stfcamss->sys_clk[STFCLK_M31DPHY_TXCLKESC_LAN0].clk,
++                      19800000);
++
++              reset_control_deassert(stfcamss->sys_rst[STFRST_M31DPHY_HW].rstc);
++              reset_control_deassert(stfcamss->sys_rst[STFRST_M31DPHY_B09_ALWAYS_ON].rstc);
++
++              count++;
++      } else {
++              if (count == 0)
++                      goto exit;
++              if (count == 1) {
++                      reset_control_assert(stfcamss->sys_rst[STFRST_M31DPHY_HW].rstc);
++                      reset_control_assert(stfcamss->sys_rst[STFRST_M31DPHY_B09_ALWAYS_ON].rstc);
++              }
++              count--;
++      }
++exit:
++      mutex_unlock(&count_lock);
++      return 0;
++}
++
++static int stf_csiphy_clk_enable(struct stf_csiphy_dev *csiphy_dev)
++{
++      return stf_csiphy_clk_set(csiphy_dev, 1);
++}
++
++static int stf_csiphy_clk_disable(struct stf_csiphy_dev *csiphy_dev)
++{
++      return stf_csiphy_clk_set(csiphy_dev, 0);
++}
++
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++static int cmp_func(const void *x1, const void *x2)
++{
++      return *((unsigned char *)x1) - *((unsigned char *)x2);
++}
++#endif
++
++int try_cfg(struct csi2phy_cfg2 *cfg, struct csi2phy_cfg *cfg0,
++              struct csi2phy_cfg *cfg1)
++{
++      int i = 0;
++
++      cfg->clock_lane = 0;
++      cfg->clock1_lane = 5;
++      cfg->data_lanes[0] = 1;
++      cfg->data_lanes[1] = 2;
++      cfg->data_lanes[2] = 3;
++      cfg->data_lanes[3] = 4;
++
++      if (cfg0 && cfg1) {
++              st_debug(ST_CSIPHY, "CSIPHY use 2 clk mode\n");
++              cfg->num_clks = 2;
++              cfg->num_data_lanes =
++                      cfg1->num_data_lanes + cfg0->num_data_lanes;
++              if (cfg->num_data_lanes > STF_CSI2_MAX_DATA_LANES)
++                      return -EINVAL;
++              cfg->clock_lane = cfg0->clock_lane;
++              cfg->lane_polarities[0] = cfg0->lane_polarities[0];
++              cfg->clock1_lane = cfg1->clock_lane;
++              cfg->lane_polarities[1] = cfg1->lane_polarities[0];
++              for (i = 0; i < cfg0->num_data_lanes; i++) {
++                      cfg->data_lanes[i] = cfg0->data_lanes[i];
++                      cfg->lane_polarities[i + 2] =
++                              cfg0->lane_polarities[i + 1];
++              }
++
++              for (i = cfg0->num_data_lanes; i < cfg->num_data_lanes; i++) {
++                      cfg->data_lanes[i] =
++                              cfg1->data_lanes[i - cfg0->num_data_lanes];
++                      cfg->lane_polarities[i + 2] =
++                              cfg1->lane_polarities[i - cfg0->num_data_lanes + 1];
++              }
++      } else if (cfg0 && !cfg1) {
++              st_debug(ST_CSIPHY, "CSIPHY cfg0 use 1 clk mode\n");
++              cfg->num_clks = 1;
++              cfg->num_data_lanes = cfg0->num_data_lanes;
++              cfg->clock_lane = cfg->clock1_lane  = cfg0->clock_lane;
++              cfg->lane_polarities[0] = cfg->lane_polarities[1] =
++                                              cfg0->lane_polarities[0];
++              for (i = 0; i < cfg0->num_data_lanes; i++) {
++                      cfg->data_lanes[i] = cfg0->data_lanes[i];
++                      cfg->lane_polarities[i + 2] = cfg0->lane_polarities[i + 1];
++              }
++      } else if (!cfg0 && cfg1) {
++              st_debug(ST_CSIPHY, "CSIPHY cfg1 use 1 clk mode\n");
++              cfg->num_clks = 1;
++              cfg->num_data_lanes = cfg1->num_data_lanes;
++              cfg->clock_lane = cfg->clock1_lane  = cfg1->clock_lane;
++              cfg->lane_polarities[0] = cfg->lane_polarities[1] =
++                                              cfg1->lane_polarities[0];
++              for (i = 0; i < cfg1->num_data_lanes; i++) {
++                      cfg->data_lanes[i] = cfg1->data_lanes[i];
++                      cfg->lane_polarities[i + 2] = cfg1->lane_polarities[i + 1];
++              }
++      } else {
++              return -EINVAL;
++      }
++
++#ifndef USE_CSIDPHY_ONE_CLK_MODE
++      sort(cfg->data_lanes, cfg->num_data_lanes,
++                      sizeof(cfg->data_lanes[0]), cmp_func, NULL);
++#endif
++      for (i = 0; i < cfg->num_data_lanes; i++)
++              st_debug(ST_CSIPHY, "%d: %d\n", i, cfg->data_lanes[i]);
++      return 0;
++}
++
++static int csi2rx_dphy_config(struct stf_vin_dev *vin,
++              struct stf_csiphy_dev *csiphy_dev)
++{
++      struct csi2phy_cfg2 cfg2 = {0};
++      struct csi2phy_cfg2 *cfg = &cfg2;
++      struct csi2phy_cfg *phycfg = csiphy_dev->csiphy;
++
++      if (!phycfg)
++              return -EINVAL;
++
++      if (try_cfg(cfg, phycfg, NULL))
++              return -EINVAL;
++
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_4, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_8, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_12, 0xfff0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_16, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_20, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_24, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_28, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_32, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_36, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_40, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_44, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_48, 0x24000000);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_52, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_56, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_60, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_64, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_68, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_72, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_76, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_80, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_84, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_88, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_92, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_96, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_100, 0x02000000);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_104, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_108, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_112, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_116, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_120, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_124, 0xc);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_128, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_132, 0xcc500000);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_136, 0xcc);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_140, 0x0);
++      reg_write(vin->rstgen_base, M31DPHY_APBCFGSAIF__SYSCFG_144, 0x0);
++
++      reg_set_bit(vin->rstgen_base,           //r100_ctrl0_2d1c_efuse_en
++              M31DPHY_APBCFGSAIF__SYSCFG_0,
++              BIT(6), 1<<6);
++      reg_set_bit(vin->rstgen_base,           //r100_ctrl0_2d1c_efuse_in
++              M31DPHY_APBCFGSAIF__SYSCFG_0,
++              BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7), 0x1b<<7);
++      reg_set_bit(vin->rstgen_base,           //r100_ctrl1_2d1c_efuse_en
++              M31DPHY_APBCFGSAIF__SYSCFG_0,
++              BIT(13), 1<<13);
++      reg_set_bit(vin->rstgen_base,           //r100_ctrl1_2d1c_efuse_in
++              M31DPHY_APBCFGSAIF__SYSCFG_0,
++              BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14), 0x1b<<14);
++
++      reg_set_bit(vin->rstgen_base,           //data_bus16_8
++              M31DPHY_APBCFGSAIF__SYSCFG_184,
++              BIT(8), 0<<8);
++
++      reg_set_bit(vin->rstgen_base,           //debug_mode_sel
++              M31DPHY_APBCFGSAIF__SYSCFG_184,
++              BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)|BIT(9), 0x5a<<9);
++
++      reg_set_bit(vin->rstgen_base,                   //dpdn_swap_clk0
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(0), cfg->lane_polarities[0]<<0);
++      reg_set_bit(vin->rstgen_base,                   //dpdn_swap_clk1
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(1), cfg->lane_polarities[1]<<1);
++      reg_set_bit(vin->rstgen_base,                   //dpdn_swap_lan0
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(2), cfg->lane_polarities[2]<<2);
++      reg_set_bit(vin->rstgen_base,                   //dpdn_swap_lan1
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(3), cfg->lane_polarities[3]<<3);
++      reg_set_bit(vin->rstgen_base,                   //dpdn_swap_lan2
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(4), cfg->lane_polarities[4]<<4);
++      reg_set_bit(vin->rstgen_base,                   //dpdn_swap_lan3
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(5), cfg->lane_polarities[5]<<5);
++      reg_set_bit(vin->rstgen_base,                   //enable clk0
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(6), 1<<6);
++      reg_set_bit(vin->rstgen_base,                   //enable clk1
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(7), 1<<7);
++      reg_set_bit(vin->rstgen_base,                   //enable lan0
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(8), 1<<8);
++      reg_set_bit(vin->rstgen_base,                   //enable lan1
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(9), 1<<9);
++      reg_set_bit(vin->rstgen_base,                   //enable lan2
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(10), 1<<10);
++      reg_set_bit(vin->rstgen_base,                   //enable lan3
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(11), 1<<11);
++      reg_set_bit(vin->rstgen_base,                   //gpi_en
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12),
++              0<<12);
++      reg_set_bit(vin->rstgen_base,                   //hs_freq_change_clk0
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(18), 0<<18);
++      reg_set_bit(vin->rstgen_base,                   //hs_freq_change_clk1
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(19), 0<<19);
++
++      reg_set_bit(vin->rstgen_base,
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(22)|BIT(21)|BIT(20), cfg->clock_lane<<20);          //clock lane 0
++      reg_set_bit(vin->rstgen_base,
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(25)|BIT(24)|BIT(23), cfg->clock1_lane<<23);         //clock lane 1
++
++      reg_set_bit(vin->rstgen_base,
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(28)|BIT(27)|BIT(26), cfg->data_lanes[0]<<26);       //data lane 0
++      reg_set_bit(vin->rstgen_base,
++              M31DPHY_APBCFGSAIF__SYSCFG_188,
++              BIT(31)|BIT(30)|BIT(29), cfg->data_lanes[1]<<29);       //data lane 1
++      reg_set_bit(vin->rstgen_base,
++              M31DPHY_APBCFGSAIF__SYSCFG_192,
++              BIT(2)|BIT(1)|BIT(0), cfg->data_lanes[2]<<0);           //data lane 2
++      reg_set_bit(vin->rstgen_base,
++              M31DPHY_APBCFGSAIF__SYSCFG_192,
++              BIT(5)|BIT(4)|BIT(3), cfg->data_lanes[3]<<3);           //data lane 3
++
++      reg_set_bit(vin->rstgen_base,           //mp_test_en
++              M31DPHY_APBCFGSAIF__SYSCFG_192,
++              BIT(6), 0<<6);
++      reg_set_bit(vin->rstgen_base,           //mp_test_mode_sel
++              M31DPHY_APBCFGSAIF__SYSCFG_192,
++              BIT(11)|BIT(10)|BIT(9)|BIT(8)|BIT(7), 0<<7);
++
++      reg_set_bit(vin->rstgen_base,           //pll_clk_sel
++              M31DPHY_APBCFGSAIF__SYSCFG_192,
++              BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16)|BIT(15)|BIT(14)|BIT(13)|BIT(12),
++              0x37c<<12);
++
++      reg_set_bit(vin->rstgen_base,           //rx_1c2c_sel
++              M31DPHY_APBCFGSAIF__SYSCFG_200,
++              BIT(8), 0<<8);
++
++      reg_set_bit(vin->rstgen_base,           //precounter in clk0
++              M31DPHY_APBCFGSAIF__SYSCFG_192,
++              BIT(29)|BIT(28)|BIT(27)|BIT(26)|BIT(25)|BIT(24)|BIT(23)|BIT(22),
++              8<<22);
++      reg_set_bit(vin->rstgen_base,           //precounter in clk1
++              M31DPHY_APBCFGSAIF__SYSCFG_196,
++              BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
++              8<<0);
++      reg_set_bit(vin->rstgen_base,           //precounter in lan0
++              M31DPHY_APBCFGSAIF__SYSCFG_196,
++              BIT(15)|BIT(14)|BIT(13)|BIT(12)|BIT(11)|BIT(10)|BIT(9)|BIT(8),
++              7<<8);
++      reg_set_bit(vin->rstgen_base,           //precounter in lan1
++              M31DPHY_APBCFGSAIF__SYSCFG_196,
++              BIT(23)|BIT(22)|BIT(21)|BIT(20)|BIT(19)|BIT(18)|BIT(17)|BIT(16),
++              7<<16);
++      reg_set_bit(vin->rstgen_base,           //precounter in lan2
++              M31DPHY_APBCFGSAIF__SYSCFG_196,
++              BIT(31)|BIT(30)|BIT(29)|BIT(28)|BIT(27)|BIT(26)|BIT(25)|BIT(24),
++              7<<24);
++      reg_set_bit(vin->rstgen_base,           //precounter in lan3
++              M31DPHY_APBCFGSAIF__SYSCFG_200,
++              BIT(7)|BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
++              7<<0);
++
++      return 0;
++}
++
++static int stf_csiphy_config_set(struct stf_csiphy_dev *csiphy_dev)
++{
++      struct stf_vin_dev *vin = csiphy_dev->stfcamss->vin;
++
++      csi2rx_dphy_config(vin, csiphy_dev);
++      return 0;
++}
++
++static int stf_csiphy_stream_set(struct stf_csiphy_dev *csiphy_dev, int on)
++{
++      return 0;
++}
++
++struct csiphy_hw_ops csiphy_ops = {
++      .csiphy_clk_enable        = stf_csiphy_clk_enable,
++      .csiphy_clk_disable       = stf_csiphy_clk_disable,
++      .csiphy_config_set        = stf_csiphy_config_set,
++      .csiphy_stream_set        = stf_csiphy_stream_set,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c
+@@ -0,0 +1,123 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/dma-buf.h>
++#include <media/v4l2-subdev.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "stf_isp_ioctl.h"
++#include "stf_dmabuf.h"
++
++#define TOTAL_SIZE_LIMIT      (64 * 1024 * 1024)
++
++static size_t total_size;
++static struct vb2_queue       vb2_queue = {
++      .dma_attrs = 0,
++      .gfp_flags = 0,
++      .dma_dir = DMA_TO_DEVICE,
++};
++static struct vb2_buffer vb = {
++      .vb2_queue = &vb2_queue,
++};
++
++static int dmabuf_create(struct device *dev,
++                         struct dmabuf_create *head)
++{
++      struct dma_buf *dmabuf = NULL;
++      void *mem_priv = NULL;
++      dma_addr_t *paddr = NULL;
++      int ret = 0;
++
++      mem_priv = vb2_dma_contig_memops.alloc(&vb, dev, head->size);
++      if (IS_ERR_OR_NULL(mem_priv)) {
++              if (mem_priv)
++                      ret = PTR_ERR(mem_priv);
++              goto exit;
++      }
++
++      dmabuf = vb2_dma_contig_memops.get_dmabuf(&vb, mem_priv, O_RDWR);
++      if (IS_ERR(dmabuf)) {
++              ret = PTR_ERR(dmabuf);
++              goto free;
++      }
++
++      head->fd = dma_buf_fd(dmabuf, O_CLOEXEC);
++      if (head->fd < 0) {
++              dma_buf_put(dmabuf);
++              ret = head->fd;
++              goto free;
++      }
++
++      paddr = vb2_dma_contig_memops.cookie(&vb, mem_priv);
++      head->paddr = *paddr;
++      return 0;
++free:
++      vb2_dma_contig_memops.put(mem_priv);
++exit:
++      return ret;
++}
++
++int stf_dmabuf_ioctl_alloc(struct device *dev, void *arg)
++{
++      struct dmabuf_create *head = arg;
++      int ret = -EINVAL;
++
++      if (IS_ERR_OR_NULL(head))
++              return -EFAULT;
++
++      head->size = PAGE_ALIGN(head->size);
++      if (!head->size)
++              return -EINVAL;
++      if ((head->size + total_size) > TOTAL_SIZE_LIMIT)
++              return -ENOMEM;
++
++      ret = dmabuf_create(dev, head);
++      if (ret)
++              return -EFAULT;
++
++      total_size += head->size;
++      return ret;
++}
++
++int stf_dmabuf_ioctl_free(struct device *dev, void *arg)
++{
++      struct dmabuf_create *head = arg;
++      struct dma_buf *dmabuf = NULL;
++      int ret = 0;
++
++      if (IS_ERR_OR_NULL(head))
++              return -EFAULT;
++      if (head->size != PAGE_ALIGN(head->size))
++              return -EINVAL;
++      if (head->size > total_size)
++              return -EINVAL;
++
++      dmabuf = dma_buf_get(head->fd);
++      if (IS_ERR_OR_NULL(dmabuf))
++              return -EINVAL;
++
++      dma_buf_put(dmabuf);
++      vb2_dma_contig_memops.put(dmabuf->priv);
++      total_size -= head->size;
++      return ret;
++}
++
++int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg)
++{
++      int ret = -ENOIOCTLCMD;
++
++      switch (cmd) {
++      case VIDIOC_STF_DMABUF_ALLOC:
++              ret = stf_dmabuf_ioctl_alloc(dev, arg);
++              break;
++      case VIDIOC_STF_DMABUF_FREE:
++              ret = stf_dmabuf_ioctl_free(dev, arg);
++              break;
++      default:
++              break;
++      }
++      return ret;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_DMABUF_H
++#define STF_DMABUF_H
++
++extern int stf_dmabuf_ioctl(struct device *dev, unsigned int cmd, void *arg);
++
++#endif /* STF_DMABUF_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dvp.c
+@@ -0,0 +1,385 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static const struct dvp_format dvp_formats_st7110[] = {
++      { MEDIA_BUS_FMT_YUYV8_2X8, 8},
++      { MEDIA_BUS_FMT_RGB565_2X8_LE, 8},
++      { MEDIA_BUS_FMT_SRGGB8_1X8, 8},
++      { MEDIA_BUS_FMT_SGRBG8_1X8, 8},
++      { MEDIA_BUS_FMT_SGBRG8_1X8, 8},
++      { MEDIA_BUS_FMT_SBGGR8_1X8, 8},
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 8},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 8},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 8},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 8},
++};
++
++static int dvp_find_format(u32 code,
++              const struct dvp_format *formats,
++              unsigned int nformats)
++{
++      int i;
++
++      for (i = 0; i < nformats; i++)
++              if (formats[i].code == code)
++                      return i;
++      return -EINVAL;
++}
++
++int stf_dvp_subdev_init(struct stfcamss *stfcamss)
++{
++      struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
++
++      dvp_dev->s_type = SENSOR_VIN;
++      dvp_dev->hw_ops = &dvp_ops;
++      dvp_dev->stfcamss = stfcamss;
++      dvp_dev->formats = dvp_formats_st7110;
++      dvp_dev->nformats = ARRAY_SIZE(dvp_formats_st7110);
++      mutex_init(&dvp_dev->stream_lock);
++      dvp_dev->stream_count = 0;
++      return 0;
++}
++
++static int dvp_set_power(struct v4l2_subdev *sd, int on)
++{
++      return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__dvp_get_format(struct stf_dvp_dev *dvp_dev,
++              struct v4l2_subdev_state *state,
++              unsigned int pad,
++              enum v4l2_subdev_format_whence which)
++{
++
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_format(
++                      &dvp_dev->subdev, state, pad);
++      return &dvp_dev->fmt[pad];
++}
++
++static int dvp_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++      int ret = 0;
++
++      format = __dvp_get_format(dvp_dev, NULL, STF_DVP_PAD_SRC,
++                              V4L2_SUBDEV_FORMAT_ACTIVE);
++      if (format == NULL)
++              return -EINVAL;
++      ret = dvp_find_format(format->code,
++                              dvp_dev->formats,
++                              dvp_dev->nformats);
++      if (ret < 0)
++              return ret;
++
++      mutex_lock(&dvp_dev->stream_lock);
++      if (enable) {
++              if (dvp_dev->stream_count == 0) {
++                      dvp_dev->hw_ops->dvp_clk_enable(dvp_dev);
++                      dvp_dev->hw_ops->dvp_config_set(dvp_dev);
++                      dvp_dev->hw_ops->dvp_set_format(dvp_dev,
++                              format->width, dvp_dev->formats[ret].bpp);
++                      dvp_dev->hw_ops->dvp_stream_set(dvp_dev, 1);
++              }
++              dvp_dev->stream_count++;
++      } else {
++              if (dvp_dev->stream_count == 0)
++                      goto exit;
++              if (dvp_dev->stream_count == 1) {
++                      dvp_dev->hw_ops->dvp_stream_set(dvp_dev, 0);
++                      dvp_dev->hw_ops->dvp_clk_disable(dvp_dev);
++              }
++              dvp_dev->stream_count--;
++      }
++exit:
++      mutex_unlock(&dvp_dev->stream_lock);
++      return 0;
++}
++
++static void dvp_try_format(struct stf_dvp_dev *dvp_dev,
++                      struct v4l2_subdev_state *state,
++                      unsigned int pad,
++                      struct v4l2_mbus_framefmt *fmt,
++                      enum v4l2_subdev_format_whence which)
++{
++      unsigned int i;
++
++      switch (pad) {
++      case STF_DVP_PAD_SINK:
++              /* Set format on sink pad */
++
++              for (i = 0; i < dvp_dev->nformats; i++)
++                      if (fmt->code == dvp_dev->formats[i].code)
++                              break;
++
++              if (i >= dvp_dev->nformats)
++                      fmt->code = dvp_dev->formats[0].code;
++
++              fmt->width = clamp_t(u32,
++                              fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              fmt->height = clamp_t(u32,
++                              fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++
++              fmt->field = V4L2_FIELD_NONE;
++              fmt->colorspace = V4L2_COLORSPACE_SRGB;
++              fmt->flags = 0;
++
++              break;
++
++      case STF_DVP_PAD_SRC:
++
++              *fmt = *__dvp_get_format(dvp_dev, state, STF_DVP_PAD_SINK, which);
++
++              break;
++      }
++}
++
++static int dvp_enum_mbus_code(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++
++      if (code->index >= dvp_dev->nformats)
++              return -EINVAL;
++
++      if (code->pad == STF_DVP_PAD_SINK) {
++              code->code = dvp_dev->formats[code->index].code;
++      } else {
++              struct v4l2_mbus_framefmt *sink_fmt;
++
++              sink_fmt = __dvp_get_format(dvp_dev, state, STF_DVP_PAD_SINK,
++                                      code->which);
++
++              code->code = sink_fmt->code;
++              if (!code->code)
++                      return -EINVAL;
++      }
++      code->flags = 0;
++
++      return 0;
++}
++
++static int dvp_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt format;
++
++      if (fse->index != 0)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = 1;
++      format.height = 1;
++      dvp_try_format(dvp_dev, state, fse->pad, &format, fse->which);
++      fse->min_width = format.width;
++      fse->min_height = format.height;
++
++      if (format.code != fse->code)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = -1;
++      format.height = -1;
++      dvp_try_format(dvp_dev, state, fse->pad, &format, fse->which);
++      fse->max_width = format.width;
++      fse->max_height = format.height;
++
++      return 0;
++}
++
++static int dvp_get_format(struct v4l2_subdev *sd,
++                       struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __dvp_get_format(dvp_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      fmt->format = *format;
++
++      return 0;
++}
++
++static int dvp_set_format(struct v4l2_subdev *sd,
++                       struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_dvp_dev *dvp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __dvp_get_format(dvp_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      mutex_lock(&dvp_dev->stream_lock);
++      if (dvp_dev->stream_count) {
++              fmt->format = *format;
++              mutex_unlock(&dvp_dev->stream_lock);
++              goto out;
++      } else {
++              dvp_try_format(dvp_dev, state, fmt->pad, &fmt->format, fmt->which);
++              *format = fmt->format;
++      }
++      mutex_unlock(&dvp_dev->stream_lock);
++
++      /* Propagate the format from sink to source */
++      if (fmt->pad == STF_DVP_PAD_SINK) {
++              format = __dvp_get_format(dvp_dev, state, STF_DVP_PAD_SRC,
++                                      fmt->which);
++
++              *format = fmt->format;
++              dvp_try_format(dvp_dev, state, STF_DVP_PAD_SRC, format,
++                                      fmt->which);
++      }
++
++out:
++      return 0;
++}
++
++static int dvp_init_formats(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_fh *fh)
++{
++      struct v4l2_subdev_format format = {
++              .pad = STF_DVP_PAD_SINK,
++              .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++                              V4L2_SUBDEV_FORMAT_ACTIVE,
++              .format = {
++                      .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++                      .width = 1920,
++                      .height = 1080
++              }
++      };
++
++      return dvp_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int dvp_link_setup(struct media_entity *entity,
++                      const struct media_pad *local,
++                      const struct media_pad *remote, u32 flags)
++{
++      if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
++              (flags & MEDIA_LNK_FL_ENABLED)) {
++              struct v4l2_subdev *sd;
++              struct stf_dvp_dev *dvp_dev;
++              struct vin_line *line;
++
++              if (media_pad_remote_pad_first(local))
++                      return -EBUSY;
++
++              sd = media_entity_to_v4l2_subdev(entity);
++              dvp_dev = v4l2_get_subdevdata(sd);
++
++              sd = media_entity_to_v4l2_subdev(remote->entity);
++              line = v4l2_get_subdevdata(sd);
++              if (line->sdev_type == VIN_DEV_TYPE)
++                      dvp_dev->s_type = SENSOR_VIN;
++              if (line->sdev_type == ISP_DEV_TYPE)
++                      dvp_dev->s_type = SENSOR_ISP;
++              st_info(ST_DVP, "DVP device sensor type: %d\n", dvp_dev->s_type);
++      }
++
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops dvp_core_ops = {
++      .s_power = dvp_set_power,
++};
++
++static const struct v4l2_subdev_video_ops dvp_video_ops = {
++      .s_stream = dvp_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops dvp_pad_ops = {
++      .enum_mbus_code = dvp_enum_mbus_code,
++      .enum_frame_size = dvp_enum_frame_size,
++      .get_fmt = dvp_get_format,
++      .set_fmt = dvp_set_format,
++};
++
++static const struct v4l2_subdev_ops dvp_v4l2_ops = {
++      .core = &dvp_core_ops,
++      .video = &dvp_video_ops,
++      .pad = &dvp_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops dvp_v4l2_internal_ops = {
++      .open = dvp_init_formats,
++};
++
++static const struct media_entity_operations dvp_media_ops = {
++      .link_setup = dvp_link_setup,
++      .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_dvp_register(struct stf_dvp_dev *dvp_dev,
++              struct v4l2_device *v4l2_dev)
++{
++      struct v4l2_subdev *sd = &dvp_dev->subdev;
++      struct media_pad *pads = dvp_dev->pads;
++      int ret;
++
++      v4l2_subdev_init(sd, &dvp_v4l2_ops);
++      sd->internal_ops = &dvp_v4l2_internal_ops;
++      sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++      snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++              STF_DVP_NAME, 0);
++      v4l2_set_subdevdata(sd, dvp_dev);
++
++      ret = dvp_init_formats(sd, NULL);
++      if (ret < 0) {
++              st_err(ST_DVP, "Failed to init format: %d\n", ret);
++              return ret;
++      }
++
++      pads[STF_DVP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++      pads[STF_DVP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++      sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++      sd->entity.ops = &dvp_media_ops;
++      ret = media_entity_pads_init(&sd->entity, STF_DVP_PADS_NUM, pads);
++      if (ret < 0) {
++              st_err(ST_DVP, "Failed to init media entity: %d\n", ret);
++              return ret;
++      }
++
++      ret = v4l2_device_register_subdev(v4l2_dev, sd);
++      if (ret < 0) {
++              st_err(ST_DVP, "Failed to register subdev: %d\n", ret);
++              goto err_sreg;
++      }
++
++      return 0;
++
++err_sreg:
++      media_entity_cleanup(&sd->entity);
++      return ret;
++}
++
++int stf_dvp_unregister(struct stf_dvp_dev *dvp_dev)
++{
++      v4l2_device_unregister_subdev(&dvp_dev->subdev);
++      media_entity_cleanup(&dvp_dev->subdev.entity);
++      mutex_destroy(&dvp_dev->stream_lock);
++      return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dvp.h
+@@ -0,0 +1,67 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_DVP_H
++#define STF_DVP_H
++
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_DVP_NAME "stf_dvp"
++
++#define STF_DVP_PAD_SINK     0
++#define STF_DVP_PAD_SRC      1
++#define STF_DVP_PADS_NUM     2
++
++struct dvp_format {
++      u32 code;
++      u8 bpp;
++};
++
++enum sensor_type;
++enum subdev_type;
++
++struct dvp_cfg {
++      unsigned int flags;
++      unsigned char bus_width;
++      unsigned char data_shift;
++};
++
++struct stf_dvp_dev;
++
++struct dvp_hw_ops {
++      int (*dvp_clk_enable)(struct stf_dvp_dev *dvp_dev);
++      int (*dvp_clk_disable)(struct stf_dvp_dev *dvp_dev);
++      int (*dvp_config_set)(struct stf_dvp_dev *dvp_dev);
++      int (*dvp_set_format)(struct stf_dvp_dev *dvp_dev,
++                      u32 pix_width, u8 bpp);
++      int (*dvp_stream_set)(struct stf_dvp_dev *dvp_dev, int on);
++};
++
++struct stf_dvp_dev {
++      struct stfcamss *stfcamss;
++      struct dvp_cfg *dvp;
++      enum sensor_type s_type;
++      struct v4l2_subdev subdev;
++      struct media_pad pads[STF_DVP_PADS_NUM];
++      struct v4l2_mbus_framefmt fmt[STF_DVP_PADS_NUM];
++      const struct dvp_format *formats;
++      unsigned int nformats;
++      struct dvp_hw_ops *hw_ops;
++      struct mutex stream_lock;
++      int stream_count;
++};
++
++extern int stf_dvp_subdev_init(struct stfcamss *stfcamss);
++extern int stf_dvp_register(struct stf_dvp_dev *dvp_dev,
++                      struct v4l2_device *v4l2_dev);
++extern int stf_dvp_unregister(struct stf_dvp_dev *dvp_dev);
++
++extern struct dvp_hw_ops dvp_ops;
++
++#endif /* STF_DVP_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c
+@@ -0,0 +1,187 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++
++static int stf_dvp_clk_enable(struct stf_dvp_dev *dvp_dev)
++{
++      struct stfcamss *stfcamss = dvp_dev->stfcamss;
++
++      switch (dvp_dev->s_type) {
++      case SENSOR_VIN:
++              reset_control_deassert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++              clk_set_phase(stfcamss->sys_clk[STFCLK_DVP_INV].clk, 0);
++              clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
++                      stfcamss->sys_clk[STFCLK_DVP_INV].clk);
++              break;
++      case SENSOR_ISP:
++              clk_set_phase(stfcamss->sys_clk[STFCLK_DVP_INV].clk, 0);
++              clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
++                      stfcamss->sys_clk[STFCLK_DVP_INV].clk);
++              break;
++      }
++
++      return 0;
++}
++
++static int stf_dvp_clk_disable(struct stf_dvp_dev *dvp_dev)
++{
++      struct stfcamss *stfcamss = dvp_dev->stfcamss;
++
++      switch (dvp_dev->s_type) {
++      case SENSOR_VIN:
++              clk_set_parent(stfcamss->sys_clk[STFCLK_AXIWR].clk,
++                      stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++              reset_control_assert(stfcamss->sys_rst[STFRST_AXIWR].rstc);
++              break;
++      case SENSOR_ISP:
++              clk_set_parent(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk,
++                      stfcamss->sys_clk[STFCLK_MIPI_RX0_PXL].clk);
++              break;
++      }
++
++      return 0;
++}
++
++static int stf_dvp_config_set(struct stf_dvp_dev *dvp_dev)
++{
++
++      struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
++      unsigned int flags = 0;
++      unsigned char data_shift = 0;
++      u32 polarities = 0;
++
++      if (!dvp_dev->dvp)
++              return -EINVAL;
++
++      flags = dvp_dev->dvp->flags;
++      data_shift = dvp_dev->dvp->data_shift;
++      st_info(ST_DVP, "%s, polarities = 0x%x, flags = 0x%x\n",
++                      __func__, polarities, flags);
++
++      if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
++              polarities |= BIT(1);
++      if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
++              polarities |= BIT(3);
++      print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_36);
++      reg_set_bit(vin->sysctrl_base,  SYSCONSAIF_SYSCFG_36,
++              U0_VIN_CNFG_DVP_HS_POS
++              | U0_VIN_CNFG_DVP_VS_POS,
++              polarities);
++      print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_36);
++
++      switch (data_shift) {
++      case 0:
++              data_shift = 0;
++              break;
++      case 2:
++              data_shift = 1;
++              break;
++      case 4:
++              data_shift = 2;
++              break;
++      case 6:
++              data_shift = 3;
++              break;
++      default:
++              data_shift = 0;
++              break;
++      };
++      print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++      reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++              UO_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL,
++              data_shift << 15);
++      print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++
++      return 0;
++}
++
++static int set_vin_axiwr_pix_ct(struct stf_vin_dev *vin, u8 bpp)
++{
++      u32 value = 0;
++      int cnfg_axiwr_pix_ct = 64 / bpp;
++
++      // need check
++      if (cnfg_axiwr_pix_ct == 2)
++              value = 1;
++      else if (cnfg_axiwr_pix_ct == 4)
++              value = 1;
++      else if (cnfg_axiwr_pix_ct == 8)
++              value = 0;
++      else
++              return 0;
++
++      print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++      reg_set_bit(vin->sysctrl_base,
++              SYSCONSAIF_SYSCFG_28,
++              U0_VIN_CNFG_AXIWR0_PIX_CT,
++              value<<13);
++      print_reg(ST_DVP, vin->sysctrl_base, SYSCONSAIF_SYSCFG_28);
++
++      return cnfg_axiwr_pix_ct;
++
++}
++
++static int stf_dvp_set_format(struct stf_dvp_dev *dvp_dev,
++              u32 pix_width, u8 bpp)
++{
++      struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
++      int val, pix_ct;
++
++      if (dvp_dev->s_type == SENSOR_VIN) {
++              pix_ct = set_vin_axiwr_pix_ct(vin, bpp);
++              val = (pix_width / pix_ct) - 1;
++              print_reg(ST_DVP, vin->sysctrl_base, SYSCTRL_VIN_WR_PIX_TOTAL);
++              reg_set_bit(vin->sysctrl_base,
++                      SYSCONSAIF_SYSCFG_28,
++                      U0_VIN_CNFG_AXIWR0_PIX_CNT_END,
++                      val << 2);
++              print_reg(ST_DVP, vin->sysctrl_base, SYSCTRL_VIN_WR_PIX_TOTAL);
++
++      }
++
++      return 0;
++}
++
++static int stf_dvp_stream_set(struct stf_dvp_dev *dvp_dev, int on)
++{
++      struct stf_vin_dev *vin = dvp_dev->stfcamss->vin;
++
++      switch (dvp_dev->s_type) {
++      case SENSOR_VIN:
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++                      U0_VIN_CNFG_ISP_DVP_EN0,
++                      0);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_0,
++                      U0_VIN_CNFG_AXI_DVP_EN,
++                      !!on<<2);
++              break;
++      case SENSOR_ISP:
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++                      U0_VIN_CNFG_ISP_DVP_EN0,
++                      !!on<<5);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_0,
++                      U0_VIN_CNFG_AXI_DVP_EN,
++                      0);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++                      U0_VIN_CNFG_DVP_SWAP_EN,
++                      0);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_36,
++                      U0_VIN_CNFG_GEN_EN_AXIRD,
++                      0);
++              break;
++      }
++
++      return 0;
++}
++
++struct dvp_hw_ops dvp_ops = {
++      .dvp_clk_enable        = stf_dvp_clk_enable,
++      .dvp_clk_disable       = stf_dvp_clk_disable,
++      .dvp_config_set        = stf_dvp_config_set,
++      .dvp_set_format        = stf_dvp_set_format,
++      .dvp_stream_set        = stf_dvp_stream_set,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_event.c
+@@ -0,0 +1,36 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/notifier.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++
++static ATOMIC_NOTIFIER_HEAD(vin_notifier_list);
++
++int vin_notifier_register(struct notifier_block *nb)
++{
++      return atomic_notifier_chain_register(&vin_notifier_list, nb);
++}
++EXPORT_SYMBOL_GPL(vin_notifier_register);
++
++void vin_notifier_unregister(struct notifier_block *nb)
++{
++      atomic_notifier_chain_unregister(&vin_notifier_list, nb);
++}
++EXPORT_SYMBOL_GPL(vin_notifier_unregister);
++
++int vin_notifier_call(unsigned long e, void *v)
++{
++      return atomic_notifier_call_chain(&vin_notifier_list, e, v);
++}
++EXPORT_SYMBOL_GPL(vin_notifier_call);
++
++MODULE_AUTHOR("StarFive Technology Co., Ltd.");
++MODULE_DESCRIPTION("Starfive VIC video in notifier");
++MODULE_LICENSE("GPL");
++//MODULE_SUPPORTED_DEVICE("video");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -0,0 +1,1521 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <media/v4l2-async.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++#include <linux/firmware.h>
++#include <linux/jh7110-isp.h>
++#include "stf_isp_ioctl.h"
++#include "stf_dmabuf.h"
++
++static int user_config_isp;
++static int isp_set_selection(struct v4l2_subdev *sd,
++                           struct v4l2_subdev_state *state,
++                           struct v4l2_subdev_selection *sel);
++
++static struct v4l2_rect *
++__isp_get_compose(struct stf_isp_dev *isp_dev,
++                struct v4l2_subdev_state *state,
++                enum v4l2_subdev_format_whence which);
++
++static struct v4l2_rect *
++__isp_get_crop(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              enum v4l2_subdev_format_whence which);
++
++static struct v4l2_rect *
++__isp_get_scale(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              struct v4l2_subdev_selection *sel);
++
++static struct v4l2_rect *
++__isp_get_itiws(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              enum v4l2_subdev_format_whence which);
++
++// sink format and raw format must one by one
++static const struct isp_format isp_formats_st7110_sink[] = {
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++static const struct isp_format isp_formats_st7110_raw[] = {
++      { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++      { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++      { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++      { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++};
++
++static const struct isp_format isp_formats_st7110_compat_10bit_raw[] = {
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++};
++
++static const struct isp_format isp_formats_st7110_compat_8bit_raw[] = {
++      { MEDIA_BUS_FMT_SRGGB8_1X8, 8},
++      { MEDIA_BUS_FMT_SGRBG8_1X8, 8},
++      { MEDIA_BUS_FMT_SGBRG8_1X8, 8},
++      { MEDIA_BUS_FMT_SBGGR8_1X8, 8},
++};
++
++static const struct isp_format isp_formats_st7110_uo[] = {
++      { MEDIA_BUS_FMT_Y12_1X12, 8},
++};
++
++static const struct isp_format isp_formats_st7110_iti[] = {
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++      { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++      { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++      { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++      { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++      { MEDIA_BUS_FMT_Y12_1X12, 8},
++      { MEDIA_BUS_FMT_YUV8_1X24, 8},
++};
++
++static const struct isp_format_table isp_formats_st7110[] = {
++      { isp_formats_st7110_sink, ARRAY_SIZE(isp_formats_st7110_sink) }, /* pad 0 */
++      { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     /* pad 1 */
++      { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     /* pad 2 */
++      { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     /* pad 3 */
++      { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },   /* pad 4 */
++      { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },   /* pad 5 */
++      { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },   /* pad 6 */
++      { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },   /* pad 7 */
++};
++
++int stf_isp_subdev_init(struct stfcamss *stfcamss)
++{
++      struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++
++      isp_dev->sdev_type = ISP_DEV_TYPE;
++      isp_dev->hw_ops = &isp_ops;
++      isp_dev->stfcamss = stfcamss;
++      isp_dev->formats = isp_formats_st7110;
++      isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110);
++      mutex_init(&isp_dev->stream_lock);
++      mutex_init(&isp_dev->power_lock);
++      mutex_init(&isp_dev->setfile_lock);
++      atomic_set(&isp_dev->shadow_count, 0);
++      return 0;
++}
++
++/*
++ * ISP Controls.
++ */
++
++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
++{
++      return &container_of(ctrl->handler, struct stf_isp_dev,
++                           ctrls.handler)->subdev;
++}
++
++static u64 isp_calc_pixel_rate(struct stf_isp_dev *isp_dev)
++{
++      u64 rate = 0;
++
++      return rate;
++}
++
++static int isp_set_ctrl_hue(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_set_ctrl_contrast(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_set_ctrl_saturation(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_set_ctrl_white_balance(struct stf_isp_dev *isp_dev, int awb)
++{
++      struct isp_ctrls *ctrls = &isp_dev->ctrls;
++      int ret = 0;
++
++      if (!awb && (ctrls->red_balance->is_new
++                      || ctrls->blue_balance->is_new)) {
++              u16 red = (u16)ctrls->red_balance->val;
++              u16 blue = (u16)ctrls->blue_balance->val;
++
++              st_debug(ST_ISP, "red = 0x%x, blue = 0x%x\n", red, blue);
++              //isp_dev->hw_ops->isp_set_awb_r_gain(isp_dev, red);
++              //if (ret)
++              //      return ret;
++              //isp_dev->hw_ops->isp_set_awb_b_gain(isp_dev, blue);
++      }
++
++      return ret;
++}
++
++static int isp_set_ctrl_exposure(struct stf_isp_dev *isp_dev,
++                                  enum v4l2_exposure_auto_type auto_exposure)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_set_ctrl_gain(struct stf_isp_dev *isp_dev, bool auto_gain)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static const char * const test_pattern_menu[] = {
++      "Disabled",
++      "Color bars",
++      "Color bars w/ rolling bar",
++      "Color squares",
++      "Color squares w/ rolling bar",
++};
++
++#define ISP_TEST_ENABLE                       BIT(7)
++#define ISP_TEST_ROLLING              BIT(6)  /* rolling horizontal bar */
++#define ISP_TEST_TRANSPARENT          BIT(5)
++#define ISP_TEST_SQUARE_BW            BIT(4)  /* black & white squares */
++#define ISP_TEST_BAR_STANDARD         (0 << 2)
++#define ISP_TEST_BAR_VERT_CHANGE_1    (1 << 2)
++#define ISP_TEST_BAR_HOR_CHANGE               (2 << 2)
++#define ISP_TEST_BAR_VERT_CHANGE_2    (3 << 2)
++#define ISP_TEST_BAR                  (0 << 0)
++#define ISP_TEST_RANDOM                       (1 << 0)
++#define ISP_TEST_SQUARE                       (2 << 0)
++#define ISP_TEST_BLACK                        (3 << 0)
++
++static const u8 test_pattern_val[] = {
++      0,
++      ISP_TEST_ENABLE | ISP_TEST_BAR_VERT_CHANGE_1 |
++              ISP_TEST_BAR,
++      ISP_TEST_ENABLE | ISP_TEST_ROLLING |
++              ISP_TEST_BAR_VERT_CHANGE_1 | ISP_TEST_BAR,
++      ISP_TEST_ENABLE | ISP_TEST_SQUARE,
++      ISP_TEST_ENABLE | ISP_TEST_ROLLING | ISP_TEST_SQUARE,
++};
++
++static int isp_set_ctrl_test_pattern(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      // return isp_write_reg(isp_dev, ISP_REG_PRE_ISP_TEST_SET1,
++      //                      test_pattern_val[value]);
++      return ret;
++}
++
++static int isp_set_ctrl_light_freq(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_set_ctrl_hflip(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_set_ctrl_vflip(struct stf_isp_dev *isp_dev, int value)
++{
++      int ret = 0;
++
++      return ret;
++}
++
++static int isp_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
++{
++      switch (ctrl->id) {
++      case V4L2_CID_AUTOGAIN:
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              break;
++      }
++
++      return 0;
++}
++
++static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      int ret = 0;
++
++      /*
++       * If the device is not powered up by the host driver do
++       * not apply any controls to H/W at this time. Instead
++       * the controls will be restored right after power-up.
++       */
++      mutex_lock(&isp_dev->power_lock);
++      if (isp_dev->power_count == 0) {
++              mutex_unlock(&isp_dev->power_lock);
++              return 0;
++      }
++      mutex_unlock(&isp_dev->power_lock);
++
++      switch (ctrl->id) {
++      case V4L2_CID_AUTOGAIN:
++              ret = isp_set_ctrl_gain(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              ret = isp_set_ctrl_exposure(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_AUTO_WHITE_BALANCE:
++              ret = isp_set_ctrl_white_balance(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_HUE:
++              ret = isp_set_ctrl_hue(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_CONTRAST:
++              ret = isp_set_ctrl_contrast(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_SATURATION:
++              ret = isp_set_ctrl_saturation(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = isp_set_ctrl_test_pattern(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_POWER_LINE_FREQUENCY:
++              ret = isp_set_ctrl_light_freq(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_HFLIP:
++              ret = isp_set_ctrl_hflip(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_VFLIP:
++              ret = isp_set_ctrl_vflip(isp_dev, ctrl->val);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_WB_SETTING:
++              break;
++      case V4L2_CID_USER_JH7110_ISP_CAR_SETTING:
++              break;
++      case V4L2_CID_USER_JH7110_ISP_CCM_SETTING:
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops isp_ctrl_ops = {
++      .g_volatile_ctrl = isp_g_volatile_ctrl,
++      .s_ctrl = isp_s_ctrl,
++};
++
++struct v4l2_ctrl_config isp_ctrl[] = {
++      [0] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "WB Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_WB_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_wb_setting),
++              .flags          = 0,
++      },
++      [1] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "Car Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_CAR_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_car_setting),
++              .flags          = 0,
++      },
++      [2] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "CCM Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_CCM_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_ccm_setting),
++              .flags          = 0,
++      },
++};
++
++static int isp_init_controls(struct stf_isp_dev *isp_dev)
++{
++      const struct v4l2_ctrl_ops *ops = &isp_ctrl_ops;
++      struct isp_ctrls *ctrls = &isp_dev->ctrls;
++      struct v4l2_ctrl_handler *hdl = &ctrls->handler;
++      int ret;
++      int i;
++
++      v4l2_ctrl_handler_init(hdl, 32);
++
++      /* Clock related controls */
++      ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
++                                            0, INT_MAX, 1,
++                                            isp_calc_pixel_rate(isp_dev));
++
++      /* Auto/manual white balance */
++      ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
++                                         V4L2_CID_AUTO_WHITE_BALANCE,
++                                         0, 1, 1, 1);
++      ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
++                                              0, 4095, 1, 0);
++      ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
++                                             0, 4095, 1, 0);
++      /* Auto/manual exposure */
++      ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
++                                               V4L2_CID_EXPOSURE_AUTO,
++                                               V4L2_EXPOSURE_MANUAL, 0,
++                                               V4L2_EXPOSURE_AUTO);
++      ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
++                                          0, 65535, 1, 0);
++      /* Auto/manual gain */
++      ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
++                                           0, 1, 1, 1);
++      ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
++                                      0, 1023, 1, 0);
++
++      ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
++                                            0, 255, 1, 64);
++      ctrls->hue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HUE,
++                                     0, 359, 1, 0);
++      ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
++                                          0, 255, 1, 0);
++      ctrls->test_pattern =
++              v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
++                                           ARRAY_SIZE(test_pattern_menu) - 1,
++                                           0, 0, test_pattern_menu);
++      ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
++                                       0, 1, 1, 0);
++      ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
++                                       0, 1, 1, 0);
++
++      ctrls->light_freq =
++              v4l2_ctrl_new_std_menu(hdl, ops,
++                                     V4L2_CID_POWER_LINE_FREQUENCY,
++                                     V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
++                                     V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
++
++      for (i = 0; i < ARRAY_SIZE(isp_ctrl); i++)
++              v4l2_ctrl_new_custom(hdl, &isp_ctrl[i], NULL);
++
++
++      if (hdl->error) {
++              ret = hdl->error;
++              goto free_ctrls;
++      }
++
++      ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
++      ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
++
++      v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
++      v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
++      v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
++
++      isp_dev->subdev.ctrl_handler = hdl;
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(hdl);
++      return ret;
++}
++
++static int isp_set_power(struct v4l2_subdev *sd, int on)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++
++      st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
++      mutex_lock(&isp_dev->power_lock);
++      if (on) {
++              if (isp_dev->power_count == 0)
++                      st_debug(ST_ISP, "turn on isp\n");
++              isp_dev->power_count++;
++      } else {
++              if (isp_dev->power_count == 0)
++                      goto exit;
++              isp_dev->power_count--;
++      }
++exit:
++      mutex_unlock(&isp_dev->power_lock);
++
++      return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__isp_get_format(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              unsigned int pad,
++              enum v4l2_subdev_format_whence which)
++{
++
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_format(&isp_dev->subdev, state, pad);
++
++      return &isp_dev->fmt[pad];
++}
++
++static int isp_get_interface_type(struct media_entity *entity)
++{
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad = &entity->pads[0];
++
++      if (!(pad->flags & MEDIA_PAD_FL_SINK))
++              return -EINVAL;
++
++      pad = media_pad_remote_pad_first(pad);
++      if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++              return -EINVAL;
++
++      subdev = media_entity_to_v4l2_subdev(pad->entity);
++
++      st_debug(ST_ISP, "interface subdev name %s\n", subdev->name);
++      if (!strncmp(subdev->name, STF_CSI_NAME, strlen(STF_CSI_NAME)))
++              return CSI_SENSOR;
++      if (!strncmp(subdev->name, STF_DVP_NAME, strlen(STF_DVP_NAME)))
++              return DVP_SENSOR;
++      return -EINVAL;
++}
++
++static int isp_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      int ret = 0, interface_type;
++      struct v4l2_mbus_framefmt *fmt;
++      struct v4l2_event src_ch = { 0 };
++
++      fmt = __isp_get_format(isp_dev, NULL, STF_ISP_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE);
++      mutex_lock(&isp_dev->stream_lock);
++      if (enable) {
++              if (isp_dev->stream_count == 0) {
++                      isp_dev->hw_ops->isp_clk_enable(isp_dev);
++                      if (!user_config_isp)
++                              isp_dev->hw_ops->isp_config_set(isp_dev);
++                      interface_type = isp_get_interface_type(&sd->entity);
++                      if (interface_type < 0) {
++                              st_err(ST_ISP, "%s, pipeline not config\n", __func__);
++                              goto exit;
++                      }
++                      isp_dev->hw_ops->isp_set_format(isp_dev,
++                                      isp_dev->rect, fmt->code, interface_type);
++                      isp_dev->hw_ops->isp_reset(isp_dev);
++                      isp_dev->hw_ops->isp_stream_set(isp_dev, enable);
++                      user_config_isp = 0;
++              }
++              isp_dev->stream_count++;
++      } else {
++              if (isp_dev->stream_count == 0)
++                      goto exit;
++              if (isp_dev->stream_count == 1) {
++                      isp_dev->hw_ops->isp_stream_set(isp_dev, enable);
++                      isp_dev->hw_ops->isp_clk_disable(isp_dev);
++              }
++              isp_dev->stream_count--;
++      }
++      src_ch.type = V4L2_EVENT_SOURCE_CHANGE,
++      src_ch.u.src_change.changes = isp_dev->stream_count,
++
++      v4l2_subdev_notify_event(sd, &src_ch);
++exit:
++      mutex_unlock(&isp_dev->stream_lock);
++
++      mutex_lock(&isp_dev->power_lock);
++      /* restore controls */
++      if (enable && isp_dev->power_count == 1) {
++              mutex_unlock(&isp_dev->power_lock);
++              ret = v4l2_ctrl_handler_setup(&isp_dev->ctrls.handler);
++      } else
++              mutex_unlock(&isp_dev->power_lock);
++
++      return ret;
++}
++
++/*Try to match sensor format with sink, and then get the index as default.*/
++static int isp_match_sensor_format_get_index(struct stf_isp_dev *isp_dev)
++{
++      int ret, idx;
++      struct media_entity *sensor;
++      struct v4l2_subdev *subdev;
++      struct v4l2_subdev_format fmt;
++      const struct isp_format_table *formats;
++
++      if (!isp_dev)
++              return -EINVAL;
++
++      sensor = stfcamss_find_sensor(&isp_dev->subdev.entity);
++      if (!sensor)
++              return -EINVAL;
++
++      subdev = media_entity_to_v4l2_subdev(sensor);
++      st_debug(ST_ISP, "Found sensor = %s\n", sensor->name);
++
++      fmt.pad = 0;
++      fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++      ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
++      if (ret) {
++              st_warn(ST_ISP, "Sonser get format failed !!\n");
++              return -EINVAL;
++      }
++
++      st_debug(ST_ISP, "Got sensor format 0x%x !!\n", fmt.format.code);
++
++      formats = &isp_dev->formats[0];         /* isp sink format */
++      for (idx = 0; idx < formats->nfmts; idx++) {
++              if (formats->fmts[idx].code == fmt.format.code) {
++                      st_info(ST_ISP,
++                              "Match sensor format to isp_formats_st7110_sink index %d !!\n",
++                              idx);
++                      return idx;
++              }
++      }
++      return -ERANGE;
++}
++
++static int isp_match_format_get_index(const struct isp_format_table *f_table,
++                      __u32 mbus_code,
++                      unsigned int pad)
++{
++      int i;
++
++      for (i = 0; i < f_table->nfmts; i++) {
++              if (mbus_code == f_table->fmts[i].code) {
++                      break;
++              } else {
++                      if (pad == STF_ISP_PAD_SRC_RAW || pad == STF_ISP_PAD_SRC_SCD_Y) {
++                              if (mbus_code == (isp_formats_st7110_compat_10bit_raw[i].code ||
++                                      isp_formats_st7110_compat_8bit_raw[i].code))
++                                      break;
++                      }
++              }
++      }
++
++      return i;
++}
++
++static void isp_try_format(struct stf_isp_dev *isp_dev,
++                      struct v4l2_subdev_state *state,
++                      unsigned int pad,
++                      struct v4l2_mbus_framefmt *fmt,
++                      enum v4l2_subdev_format_whence which)
++{
++      const struct isp_format_table *formats;
++      unsigned int i;
++      u32 code = fmt->code;
++      u32 bpp;
++
++      if (pad == STF_ISP_PAD_SINK) {
++              /* Set format on sink pad */
++
++              formats = &isp_dev->formats[pad];
++              fmt->width = clamp_t(u32,
++                              fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              fmt->height = clamp_t(u32,
++                              fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++              fmt->height &= ~0x1;
++
++              fmt->field = V4L2_FIELD_NONE;
++              fmt->colorspace = V4L2_COLORSPACE_SRGB;
++              fmt->flags = 0;
++      } else {
++              formats = &isp_dev->formats[pad];
++      }
++
++      i = isp_match_format_get_index(formats, fmt->code, pad);
++      st_debug(ST_ISP, "%s pad=%d, code=%x isp_match_format_get_index = %d\n",
++                                      __func__, pad, code, i);
++
++      if (i >= formats->nfmts &&
++              (pad == STF_ISP_PAD_SRC_RAW || pad == STF_ISP_PAD_SRC_SCD_Y)) {
++              int sensor_idx;
++
++              sensor_idx = isp_match_sensor_format_get_index(isp_dev);
++              if (sensor_idx)
++                      i = sensor_idx;
++      }
++
++      if (pad != STF_ISP_PAD_SINK)
++              *fmt = *__isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, which);
++
++      if (i >= formats->nfmts) {
++              fmt->code = formats->fmts[0].code;
++              bpp = formats->fmts[0].bpp;
++              st_info(ST_ISP, "Use default index 0 format = 0x%x\n", fmt->code);
++      } else {
++              // sink format and raw format must one by one
++              if (pad == STF_ISP_PAD_SRC_RAW || pad == STF_ISP_PAD_SRC_SCD_Y) {
++                      fmt->code = formats->fmts[i].code;
++                      bpp = formats->fmts[i].bpp;
++                      st_info(ST_ISP, "Use mapping format from sink index %d = 0x%x\n",
++                                      i, fmt->code);
++              } else {
++                      fmt->code = code;
++                      bpp = formats->fmts[i].bpp;
++                      st_info(ST_ISP, "Use input format = 0x%x\n", fmt->code);
++              }
++      }
++
++      switch (pad) {
++      case STF_ISP_PAD_SINK:
++              break;
++      case STF_ISP_PAD_SRC:
++              isp_dev->rect[ISP_COMPOSE].bpp = bpp;
++              break;
++      case STF_ISP_PAD_SRC_SS0:
++              isp_dev->rect[ISP_SCALE_SS0].bpp = bpp;
++              break;
++      case STF_ISP_PAD_SRC_SS1:
++              isp_dev->rect[ISP_SCALE_SS1].bpp = bpp;
++              break;
++      case STF_ISP_PAD_SRC_ITIW:
++      case STF_ISP_PAD_SRC_ITIR:
++              isp_dev->rect[ISP_ITIWS].bpp = bpp;
++              break;
++      case STF_ISP_PAD_SRC_RAW:
++              isp_dev->rect[ISP_CROP].bpp = bpp;
++              break;
++      case STF_ISP_PAD_SRC_SCD_Y:
++              break;
++      }
++}
++
++static int isp_enum_mbus_code(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      const struct isp_format_table *formats;
++
++      if (code->index >= isp_dev->formats[code->pad].nfmts)
++              return -EINVAL;
++
++      formats = &isp_dev->formats[code->pad];
++      code->code = formats->fmts[code->index].code;
++      code->flags = 0;
++
++      return 0;
++}
++
++static int isp_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt format;
++
++      if (fse->index != 0)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = 1;
++      format.height = 1;
++      isp_try_format(isp_dev, state, fse->pad, &format, fse->which);
++      fse->min_width = format.width;
++      fse->min_height = format.height;
++
++      if (format.code != fse->code)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = -1;
++      format.height = -1;
++      isp_try_format(isp_dev, state, fse->pad, &format, fse->which);
++      fse->max_width = format.width;
++      fse->max_height = format.height;
++
++      return 0;
++}
++
++static int isp_get_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      fmt->format = *format;
++
++      return 0;
++}
++
++static int isp_set_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++      struct v4l2_subdev_selection sel = { 0 };
++      struct v4l2_rect *rect = NULL;
++      int ret;
++
++      st_debug(ST_ISP, "%s pad=%d, code=%x, which=%d\n",
++                      __func__, fmt->reserved[0], fmt->format.code, fmt->which);
++      format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      mutex_lock(&isp_dev->stream_lock);
++      if (isp_dev->stream_count) {
++              fmt->format = *format;
++              if (fmt->reserved[0] != 0) {
++                      sel.which = fmt->which;
++                      sel.pad = fmt->reserved[0];
++
++                      switch (fmt->reserved[0]) {
++                      case STF_ISP_PAD_SRC:
++                              rect = __isp_get_compose(isp_dev, state, fmt->which);
++                              break;
++                      case STF_ISP_PAD_SRC_SS0:
++                      case STF_ISP_PAD_SRC_SS1:
++                              rect = __isp_get_scale(isp_dev, state, &sel);
++                              break;
++                      case STF_ISP_PAD_SRC_ITIW:
++                      case STF_ISP_PAD_SRC_ITIR:
++                              rect = __isp_get_itiws(isp_dev, state, fmt->which);
++                              break;
++                      case STF_ISP_PAD_SRC_RAW:
++                      case STF_ISP_PAD_SRC_SCD_Y:
++                              rect = __isp_get_crop(isp_dev, state, fmt->which);
++                              break;
++                      default:
++                              break;
++                      }
++                      if (rect != NULL) {
++                              fmt->format.width = rect->width;
++                              fmt->format.height = rect->height;
++                      }
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++              goto out;
++      } else {
++              isp_try_format(isp_dev, state, fmt->pad, &fmt->format, fmt->which);
++              *format = fmt->format;
++      }
++      mutex_unlock(&isp_dev->stream_lock);
++
++      /* Propagate the format from sink to source */
++      if (fmt->pad == STF_ISP_PAD_SINK) {
++              /* Reset sink pad compose selection */
++              sel.which = fmt->which;
++              sel.pad = STF_ISP_PAD_SINK;
++              sel.target = V4L2_SEL_TGT_CROP;
++              sel.r.width = fmt->format.width;
++              sel.r.height = fmt->format.height;
++              ret = isp_set_selection(sd, state, &sel);
++              if (ret < 0)
++                      return ret;
++      }
++
++out:
++      return 0;
++}
++
++static struct v4l2_rect *
++__isp_get_compose(struct stf_isp_dev *isp_dev,
++                struct v4l2_subdev_state *state,
++                enum v4l2_subdev_format_whence which)
++{
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_compose(&isp_dev->subdev, state,
++                                                 STF_ISP_PAD_SINK);
++
++
++      return &isp_dev->rect[ISP_COMPOSE].rect;
++}
++
++static struct v4l2_rect *
++__isp_get_crop(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              enum v4l2_subdev_format_whence which)
++{
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_crop(&isp_dev->subdev, state,
++                                              STF_ISP_PAD_SINK);
++
++      return &isp_dev->rect[ISP_CROP].rect;
++}
++
++static struct v4l2_rect *
++__isp_get_scale(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              struct v4l2_subdev_selection *sel)
++{
++      int pad;
++
++      if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_compose(&isp_dev->subdev, state,
++                                              STF_ISP_PAD_SINK);
++      if (sel->pad != STF_ISP_PAD_SRC_SS0 && sel->pad != STF_ISP_PAD_SRC_SS1)
++              return NULL;
++
++      pad = sel->pad == STF_ISP_PAD_SRC_SS0 ? ISP_SCALE_SS0 : ISP_SCALE_SS1;
++      return &isp_dev->rect[pad].rect;
++}
++
++static struct v4l2_rect *
++__isp_get_itiws(struct stf_isp_dev *isp_dev,
++              struct v4l2_subdev_state *state,
++              enum v4l2_subdev_format_whence which)
++{
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_crop(&isp_dev->subdev, state, STF_ISP_PAD_SINK);
++
++      return &isp_dev->rect[ISP_ITIWS].rect;
++}
++
++static void isp_try_crop(struct stf_isp_dev *isp_dev,
++                          struct v4l2_subdev_state *state,
++                          struct v4l2_rect *rect,
++                          enum v4l2_subdev_format_whence which)
++{
++      struct v4l2_mbus_framefmt *fmt;
++
++      fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, which);
++
++      if (rect->width > fmt->width)
++              rect->width = fmt->width;
++
++      if (rect->width + rect->left > fmt->width)
++              rect->left = fmt->width - rect->width;
++
++      if (rect->height > fmt->height)
++              rect->height = fmt->height;
++
++      if (rect->height + rect->top > fmt->height)
++              rect->top = fmt->height - rect->height;
++
++      if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) {
++              rect->left = 0;
++              rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++      }
++
++      if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) {
++              rect->top = 0;
++              rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++      }
++      rect->height &= ~0x1;
++}
++
++static void isp_try_compose(struct stf_isp_dev *isp_dev,
++                       struct v4l2_subdev_state *state,
++                       struct v4l2_rect *rect,
++                       enum v4l2_subdev_format_whence which)
++{
++      struct v4l2_rect *crop;
++
++      crop = __isp_get_crop(isp_dev, state, which);
++
++      if (rect->width > crop->width)
++              rect->width = crop->width;
++
++      if (rect->height > crop->height)
++              rect->height = crop->height;
++
++      if (crop->width > rect->width * SCALER_RATIO_MAX)
++              rect->width = (crop->width + SCALER_RATIO_MAX - 1) /
++                                                      SCALER_RATIO_MAX;
++
++      if (crop->height > rect->height * SCALER_RATIO_MAX)
++              rect->height = (crop->height + SCALER_RATIO_MAX - 1) /
++                                                      SCALER_RATIO_MAX;
++
++      if (rect->width < STFCAMSS_FRAME_MIN_WIDTH)
++              rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++
++      if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT)
++              rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++      rect->height &= ~0x1;
++}
++
++static void isp_try_scale(struct stf_isp_dev *isp_dev,
++                          struct v4l2_subdev_state *state,
++                          struct v4l2_rect *rect,
++                          enum v4l2_subdev_format_whence which)
++{
++      struct v4l2_rect *compose;
++
++      compose = __isp_get_compose(isp_dev, state, which);
++
++      if (rect->width > compose->width)
++              rect->width = compose->width;
++
++      if (rect->width + rect->left > compose->width)
++              rect->left = compose->width - rect->width;
++
++      if (rect->height > compose->height)
++              rect->height = compose->height;
++
++      if (rect->height + rect->top > compose->height)
++              rect->top = compose->height - rect->height;
++
++      if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) {
++              rect->left = 0;
++              rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++      }
++
++      if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) {
++              rect->top = 0;
++              rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++      }
++      rect->height &= ~0x1;
++}
++
++static void isp_try_itiws(struct stf_isp_dev *isp_dev,
++                          struct v4l2_subdev_state *state,
++                          struct v4l2_rect *rect,
++                          enum v4l2_subdev_format_whence which)
++{
++      struct v4l2_rect *crop;
++
++      crop = __isp_get_crop(isp_dev, state, which);
++
++      if (rect->width > crop->width)
++              rect->width = crop->width;
++
++      if (rect->width + rect->left > crop->width)
++              rect->left = crop->width - rect->width;
++
++      if (rect->height > crop->height)
++              rect->height = crop->height;
++
++      if (rect->height + rect->top > crop->height)
++              rect->top = crop->height - rect->height;
++
++      if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) {
++              rect->left = 0;
++              rect->width = STFCAMSS_FRAME_MIN_WIDTH;
++      }
++
++      if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) {
++              rect->top = 0;
++              rect->height = STFCAMSS_FRAME_MIN_HEIGHT;
++      }
++      rect->height &= ~0x1;
++}
++
++static int isp_get_selection(struct v4l2_subdev *sd,
++                           struct v4l2_subdev_state *state,
++                           struct v4l2_subdev_selection *sel)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_subdev_format fmt = { 0 };
++      struct v4l2_rect *rect;
++      int ret;
++
++      switch (sel->target) {
++      case V4L2_SEL_TGT_CROP_BOUNDS:
++      case V4L2_SEL_TGT_CROP_DEFAULT:
++              fmt.pad = sel->pad;
++              fmt.which = sel->which;
++              ret = isp_get_format(sd, state, &fmt);
++              if (ret < 0)
++                      return ret;
++
++              sel->r.left = 0;
++              sel->r.top = 0;
++              sel->r.width = fmt.format.width;
++              sel->r.height = fmt.format.height;
++              break;
++      case V4L2_SEL_TGT_CROP:
++              rect = __isp_get_crop(isp_dev, state, sel->which);
++              if (rect == NULL)
++                      return -EINVAL;
++
++              sel->r = *rect;
++              break;
++      case V4L2_SEL_TGT_COMPOSE_BOUNDS:
++      case V4L2_SEL_TGT_COMPOSE_DEFAULT:
++              if (sel->pad > STF_ISP_PAD_SRC_ITIR)
++                      return -EINVAL;
++              rect = __isp_get_crop(isp_dev, state, sel->which);
++              if (rect == NULL)
++                      return -EINVAL;
++
++              sel->r.left = rect->left;
++              sel->r.top = rect->top;
++              sel->r.width = rect->width;
++              sel->r.height = rect->height;
++              break;
++      case V4L2_SEL_TGT_COMPOSE:
++              if (sel->pad > STF_ISP_PAD_SRC_ITIR)
++                      return -EINVAL;
++              if (sel->pad == STF_ISP_PAD_SRC_SS0
++                      || sel->pad == STF_ISP_PAD_SRC_SS1) {
++                      rect = __isp_get_scale(isp_dev, state, sel);
++                      if (rect == NULL)
++                              return -EINVAL;
++              } else if (sel->pad == STF_ISP_PAD_SRC_ITIW
++                      || sel->pad == STF_ISP_PAD_SRC_ITIR) {
++                      rect = __isp_get_itiws(isp_dev, state, sel->which);
++                      if (rect == NULL)
++                              return -EINVAL;
++              } else {
++                      rect = __isp_get_compose(isp_dev, state, sel->which);
++                      if (rect == NULL)
++                              return -EINVAL;
++              }
++              sel->r = *rect;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n",
++                      __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
++      return 0;
++}
++
++static int isp_set_selection(struct v4l2_subdev *sd,
++                           struct v4l2_subdev_state *state,
++                           struct v4l2_subdev_selection *sel)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      struct v4l2_rect *rect;
++      int ret = 0;
++
++      if (sel->target == V4L2_SEL_TGT_COMPOSE &&
++                      ((sel->pad == STF_ISP_PAD_SINK)
++                       || (sel->pad == STF_ISP_PAD_SRC))) {
++              struct v4l2_subdev_format fmt = { 0 };
++              int i;
++
++              rect = __isp_get_compose(isp_dev, state, sel->which);
++              if (rect == NULL)
++                      return -EINVAL;
++
++              mutex_lock(&isp_dev->stream_lock);
++              if (isp_dev->stream_count) {
++                      sel->r = *rect;
++                      mutex_unlock(&isp_dev->stream_lock);
++                      ret = 0;
++                      goto out;
++              } else {
++                      isp_try_compose(isp_dev, state, &sel->r, sel->which);
++                      *rect = sel->r;
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++
++              /* Reset source pad format width and height */
++              fmt.which = sel->which;
++              fmt.pad = STF_ISP_PAD_SRC;
++              ret = isp_get_format(sd, state, &fmt);
++              if (ret < 0)
++                      return ret;
++
++              fmt.format.width = rect->width;
++              fmt.format.height = rect->height;
++              ret = isp_set_format(sd, state, &fmt);
++
++              /* Reset scale */
++              for (i = STF_ISP_PAD_SRC_SS0; i <= STF_ISP_PAD_SRC_ITIR; i++) {
++                      struct v4l2_subdev_selection scale = { 0 };
++
++                      scale.which = sel->which;
++                      scale.target = V4L2_SEL_TGT_COMPOSE;
++                      scale.r = *rect;
++                      scale.pad = i;
++                      ret = isp_set_selection(sd, state, &scale);
++              }
++      } else if (sel->target == V4L2_SEL_TGT_COMPOSE
++                      && ((sel->pad == STF_ISP_PAD_SRC_SS0)
++                              || (sel->pad == STF_ISP_PAD_SRC_SS1))) {
++              struct v4l2_subdev_format fmt = { 0 };
++
++              rect = __isp_get_scale(isp_dev, state, sel);
++              if (rect == NULL)
++                      return -EINVAL;
++
++              mutex_lock(&isp_dev->stream_lock);
++              if (isp_dev->stream_count) {
++                      sel->r = *rect;
++                      mutex_unlock(&isp_dev->stream_lock);
++                      ret = 0;
++                      goto out;
++              } else {
++                      isp_try_scale(isp_dev, state, &sel->r, sel->which);
++                      *rect = sel->r;
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++
++              /* Reset source pad format width and height */
++              fmt.which = sel->which;
++              fmt.pad = sel->pad;
++              ret = isp_get_format(sd, state, &fmt);
++              if (ret < 0)
++                      return ret;
++
++              fmt.format.width = rect->width;
++              fmt.format.height = rect->height;
++              ret = isp_set_format(sd, state, &fmt);
++      } else if (sel->target == V4L2_SEL_TGT_COMPOSE
++                      && ((sel->pad == STF_ISP_PAD_SRC_ITIW)
++                              || (sel->pad == STF_ISP_PAD_SRC_ITIR))) {
++              struct v4l2_subdev_format fmt = { 0 };
++
++              rect = __isp_get_itiws(isp_dev, state, sel->which);
++              if (rect == NULL)
++                      return -EINVAL;
++
++              mutex_lock(&isp_dev->stream_lock);
++              if (isp_dev->stream_count) {
++                      sel->r = *rect;
++                      mutex_unlock(&isp_dev->stream_lock);
++                      ret = 0;
++                      goto out;
++              } else {
++                      isp_try_itiws(isp_dev, state, &sel->r, sel->which);
++                      *rect = sel->r;
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++
++              /* Reset source pad format width and height */
++              fmt.which = sel->which;
++              fmt.pad = sel->pad;
++              ret = isp_get_format(sd, state, &fmt);
++              if (ret < 0)
++                      return ret;
++
++              fmt.format.width = rect->width;
++              fmt.format.height = rect->height;
++              ret = isp_set_format(sd, state, &fmt);
++      } else if (sel->target == V4L2_SEL_TGT_CROP) {
++              struct v4l2_subdev_selection compose = { 0 };
++              int i;
++
++              rect = __isp_get_crop(isp_dev, state, sel->which);
++              if (rect == NULL)
++                      return -EINVAL;
++
++              mutex_lock(&isp_dev->stream_lock);
++              if (isp_dev->stream_count) {
++                      sel->r = *rect;
++                      mutex_unlock(&isp_dev->stream_lock);
++                      ret = 0;
++                      goto out;
++              } else {
++                      isp_try_crop(isp_dev, state, &sel->r, sel->which);
++                      *rect = sel->r;
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++
++              /* Reset source compose selection */
++              compose.which = sel->which;
++              compose.target = V4L2_SEL_TGT_COMPOSE;
++              compose.r.width = rect->width;
++              compose.r.height = rect->height;
++              compose.pad = STF_ISP_PAD_SINK;
++              ret = isp_set_selection(sd, state, &compose);
++
++              /* Reset source pad format width and height */
++              for (i = STF_ISP_PAD_SRC_RAW; i < STF_ISP_PAD_MAX; i++) {
++                      struct v4l2_subdev_format fmt = { 0 };
++
++                      fmt.which = sel->which;
++                      fmt.pad = i;
++                      ret = isp_get_format(sd, state, &fmt);
++                      if (ret < 0)
++                              return ret;
++
++                      fmt.format.width = rect->width;
++                      fmt.format.height = rect->height;
++                      ret = isp_set_format(sd, state, &fmt);
++              }
++      } else {
++              ret = -EINVAL;
++      }
++
++      st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n",
++                      __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
++out:
++      return ret;
++}
++
++static int isp_init_formats(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_fh *fh)
++{
++      struct v4l2_subdev_format format = {
++              .pad = STF_ISP_PAD_SINK,
++              .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++                              V4L2_SUBDEV_FORMAT_ACTIVE,
++              .format = {
++                      .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++                      .width = 1920,
++                      .height = 1080
++              }
++      };
++
++      return isp_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static int isp_link_setup(struct media_entity *entity,
++                      const struct media_pad *local,
++                      const struct media_pad *remote, u32 flags)
++{
++      if (flags & MEDIA_LNK_FL_ENABLED)
++              if (media_pad_remote_pad_first(local))
++                      return -EBUSY;
++      return 0;
++}
++
++static int stf_isp_load_setfile(struct stf_isp_dev *isp_dev, char *file_name)
++{
++      struct device *dev = isp_dev->stfcamss->dev;
++      const struct firmware *fw;
++      u8 *buf = NULL;
++      int *regval_num;
++      int ret;
++
++      st_debug(ST_ISP, "%s, file_name %s\n", __func__, file_name);
++      ret = request_firmware(&fw, file_name, dev);
++      if (ret < 0) {
++              st_err(ST_ISP, "firmware request failed (%d)\n", ret);
++              return ret;
++      }
++      buf = devm_kzalloc(dev, fw->size, GFP_KERNEL);
++      if (!buf)
++              return -ENOMEM;
++      memcpy(buf, fw->data, fw->size);
++
++      mutex_lock(&isp_dev->setfile_lock);
++      if (isp_dev->setfile.state == 1)
++              devm_kfree(dev, isp_dev->setfile.data);
++      isp_dev->setfile.data = buf;
++      isp_dev->setfile.size = fw->size;
++      isp_dev->setfile.state = 1;
++      regval_num = (int *)&buf[fw->size - sizeof(unsigned int)];
++      isp_dev->setfile.settings.regval_num = *regval_num;
++      isp_dev->setfile.settings.regval = (struct regval_t *)buf;
++      mutex_unlock(&isp_dev->setfile_lock);
++
++      st_debug(ST_ISP, "stf_isp setfile loaded size: %zu B, reg_nul: %d\n",
++                      fw->size, isp_dev->setfile.settings.regval_num);
++
++      release_firmware(fw);
++      return ret;
++}
++
++static long stf_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++      struct device *dev = isp_dev->stfcamss->dev;
++      int ret = -ENOIOCTLCMD;
++
++      switch (cmd) {
++      case VIDIOC_STFISP_LOAD_FW: {
++              struct stfisp_fw_info *fw_info = arg;
++
++              if (IS_ERR(fw_info)) {
++                      st_err(ST_ISP, "fw_info failed, params invaild\n");
++                      return -EINVAL;
++              }
++
++              ret = stf_isp_load_setfile(isp_dev, fw_info->filename);
++              break;
++      }
++      case VIDIOC_STF_DMABUF_ALLOC:
++      case VIDIOC_STF_DMABUF_FREE:
++              ret = stf_dmabuf_ioctl(dev, cmd, arg);
++              break;
++      case VIDIOC_STFISP_GET_REG:
++              ret = isp_dev->hw_ops->isp_reg_read(isp_dev, arg);
++              break;
++      case VIDIOC_STFISP_SET_REG:
++              ret = isp_dev->hw_ops->isp_reg_write(isp_dev, arg);
++              break;
++      case VIDIOC_STFISP_SHADOW_LOCK:
++              if (atomic_add_unless(&isp_dev->shadow_count, 1, 1))
++                      ret = 0;
++              else
++                      ret = -EBUSY;
++              st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
++              break;
++      case VIDIOC_STFISP_SHADOW_UNLOCK:
++              if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0)
++                      ret = -EINVAL;
++              else
++                      ret = 0;
++              st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
++              break;
++      case VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER:
++              {
++                      isp_dev->hw_ops->isp_shadow_trigger(isp_dev);
++                      if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0)
++                              ret = -EINVAL;
++                      else
++                              ret = 0;
++                      st_debug(ST_ISP, "%s, %d, ret = %d\n", __func__, __LINE__, ret);
++              }
++              break;
++      case VIDIOC_STFISP_SET_USER_CONFIG_ISP:
++              st_debug(ST_ISP, "%s, %d set user_config_isp\n", __func__, __LINE__);
++              user_config_isp = 1;
++              break;
++      default:
++              break;
++      }
++      return ret;
++}
++
++int isp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++      struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
++
++      st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
++      while (atomic_dec_if_positive(&isp_dev->shadow_count) > 0)
++              st_warn(ST_ISP, "user not unlocked the shadow lock, driver unlock it!\n");
++
++      return 0;
++}
++
++static int stf_isp_subscribe_event(struct v4l2_subdev *sd,
++                                 struct v4l2_fh *fh,
++                                 struct v4l2_event_subscription *sub)
++{
++      switch (sub->type) {
++      case V4L2_EVENT_SOURCE_CHANGE:
++              return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
++      case V4L2_EVENT_CTRL:
++              return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
++      default:
++              st_debug(ST_ISP, "unspport subscribe_event\n");
++              return -EINVAL;
++      }
++}
++
++static const struct v4l2_subdev_core_ops isp_core_ops = {
++      .s_power = isp_set_power,
++      .ioctl = stf_isp_ioctl,
++      .log_status = v4l2_ctrl_subdev_log_status,
++      // .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .subscribe_event = stf_isp_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops isp_video_ops = {
++      .s_stream = isp_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops isp_pad_ops = {
++      .enum_mbus_code = isp_enum_mbus_code,
++      .enum_frame_size = isp_enum_frame_size,
++      .get_fmt = isp_get_format,
++      .set_fmt = isp_set_format,
++      .get_selection = isp_get_selection,
++      .set_selection = isp_set_selection,
++};
++
++static const struct v4l2_subdev_ops isp_v4l2_ops = {
++      .core = &isp_core_ops,
++      .video = &isp_video_ops,
++      .pad = &isp_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops isp_v4l2_internal_ops = {
++      .open = isp_init_formats,
++      .close = isp_close,
++};
++
++static const struct media_entity_operations isp_media_ops = {
++      .link_setup = isp_link_setup,
++      .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_isp_register(struct stf_isp_dev *isp_dev,
++              struct v4l2_device *v4l2_dev)
++{
++      struct v4l2_subdev *sd = &isp_dev->subdev;
++      struct media_pad *pads = isp_dev->pads;
++      int ret;
++
++      v4l2_subdev_init(sd, &isp_v4l2_ops);
++      sd->internal_ops = &isp_v4l2_internal_ops;
++      sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
++      snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
++              STF_ISP_NAME, 0);
++      v4l2_set_subdevdata(sd, isp_dev);
++
++      ret = isp_init_formats(sd, NULL);
++      if (ret < 0) {
++              st_err(ST_ISP, "Failed to init format: %d\n", ret);
++              return ret;
++      }
++
++      pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++      pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++      pads[STF_ISP_PAD_SRC_SS0].flags = MEDIA_PAD_FL_SOURCE;
++      pads[STF_ISP_PAD_SRC_SS1].flags = MEDIA_PAD_FL_SOURCE;
++      pads[STF_ISP_PAD_SRC_ITIW].flags = MEDIA_PAD_FL_SOURCE;
++      pads[STF_ISP_PAD_SRC_ITIR].flags = MEDIA_PAD_FL_SOURCE;
++      pads[STF_ISP_PAD_SRC_RAW].flags = MEDIA_PAD_FL_SOURCE;
++      pads[STF_ISP_PAD_SRC_SCD_Y].flags = MEDIA_PAD_FL_SOURCE;
++
++      sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++      sd->entity.ops = &isp_media_ops;
++      ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads);
++      if (ret < 0) {
++              st_err(ST_ISP, "Failed to init media entity: %d\n", ret);
++              return ret;
++      }
++
++      ret = isp_init_controls(isp_dev);
++      if (ret)
++              goto err_sreg;
++
++      ret = v4l2_device_register_subdev(v4l2_dev, sd);
++      if (ret < 0) {
++              st_err(ST_ISP, "Failed to register subdev: %d\n", ret);
++              goto free_ctrls;
++      }
++
++      return 0;
++
++free_ctrls:
++      v4l2_ctrl_handler_free(&isp_dev->ctrls.handler);
++err_sreg:
++      media_entity_cleanup(&sd->entity);
++      return ret;
++}
++
++int stf_isp_unregister(struct stf_isp_dev *isp_dev)
++{
++      v4l2_device_unregister_subdev(&isp_dev->subdev);
++      media_entity_cleanup(&isp_dev->subdev.entity);
++      v4l2_ctrl_handler_free(&isp_dev->ctrls.handler);
++      mutex_destroy(&isp_dev->stream_lock);
++      mutex_destroy(&isp_dev->power_lock);
++      mutex_destroy(&isp_dev->setfile_lock);
++      return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+@@ -0,0 +1,222 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_ISP_H
++#define STF_ISP_H
++
++#include <media/v4l2-subdev.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/media-entity.h>
++#include <video/stf-vin.h>
++
++#define STF_ISP_NAME "stf_isp"
++#define STF_ISP_SETFILE     "stf_isp0_fw.bin"
++
++#define ISP_SCD_BUFFER_SIZE     (19 * 256 * 4)  // align 128
++#define ISP_YHIST_BUFFER_SIZE   (64 * 4)
++#define ISP_SCD_Y_BUFFER_SIZE   (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)
++#define ISP_RAW_DATA_BITS       12
++#define SCALER_RATIO_MAX        1  // no compose function
++#define STF_ISP_REG_OFFSET_MAX  0x0FFF
++#define STF_ISP_REG_DELAY_MAX   100
++
++#define ISP_REG_CSIINTS_ADDR    0x00000008
++#define ISP_REG_SENSOR          0x00000014
++#define ISP_REG_DUMP_CFG_0      0x00000024
++#define ISP_REG_DUMP_CFG_1      0x00000028
++#define ISP_REG_SCD_CFG_0       0x00000098
++#define ISP_REG_SCD_CFG_1       0x0000009C
++#define ISP_REG_SC_CFG_1        0x000000BC
++#define ISP_REG_IESHD_ADDR      0x00000A50
++#define ISP_REG_SS0AY           0x00000A94
++#define ISP_REG_SS0AUV          0x00000A98
++#define ISP_REG_SS0S            0x00000A9C
++#define ISP_REG_SS0IW           0x00000AA8
++#define ISP_REG_SS1AY           0x00000AAC
++#define ISP_REG_SS1AUV          0x00000AB0
++#define ISP_REG_SS1S            0x00000AB4
++#define ISP_REG_SS1IW           0x00000AC0
++#define ISP_REG_YHIST_CFG_4     0x00000CD8
++#define ISP_REG_ITIIWSR         0x00000B20
++#define ISP_REG_ITIDWLSR        0x00000B24
++#define ISP_REG_ITIDWYSAR       0x00000B28
++#define ISP_REG_ITIDWUSAR       0x00000B2C
++#define ISP_REG_ITIDRYSAR       0x00000B30
++#define ISP_REG_ITIDRUSAR       0x00000B34
++#define ISP_REG_ITIPDFR         0x00000B38
++#define ISP_REG_ITIDRLSR        0x00000B3C
++#define ISP_REG_ITIBSR          0x00000B40
++#define ISP_REG_ITIAIR          0x00000B44
++#define ISP_REG_ITIDPSR         0x00000B48
++
++/* The output line of a isp controller */
++enum isp_line_id {
++      STF_ISP_LINE_INVALID = -1,
++      STF_ISP_LINE_SRC = 1,
++      STF_ISP_LINE_SRC_SS0 = 2,
++      STF_ISP_LINE_SRC_SS1 = 3,
++      STF_ISP_LINE_SRC_ITIW = 4,
++      STF_ISP_LINE_SRC_ITIR = 5,
++      STF_ISP_LINE_SRC_RAW = 6,
++      STF_ISP_LINE_SRC_SCD_Y = 7,
++      STF_ISP_LINE_MAX = STF_ISP_LINE_SRC_SCD_Y
++};
++
++/* pad id for media framework */
++enum isp_pad_id {
++      STF_ISP_PAD_SINK = 0,
++      STF_ISP_PAD_SRC = 1,
++      STF_ISP_PAD_SRC_SS0 = 2,
++      STF_ISP_PAD_SRC_SS1 = 3,
++      STF_ISP_PAD_SRC_ITIW = 4,
++      STF_ISP_PAD_SRC_ITIR = 5,
++      STF_ISP_PAD_SRC_RAW = 6,
++      STF_ISP_PAD_SRC_SCD_Y = 7,
++      STF_ISP_PAD_MAX = 8
++};
++
++enum {
++      EN_INT_NONE                 = 0,
++      EN_INT_ISP_DONE             = (0x1 << 24),
++      EN_INT_CSI_DONE             = (0x1 << 25),
++      EN_INT_SC_DONE              = (0x1 << 26),
++      EN_INT_LINE_INT             = (0x1 << 27),
++      EN_INT_ALL                  = (0xF << 24),
++};
++
++enum {
++      DVP_SENSOR = 0,
++      CSI_SENSOR,
++};
++
++#define ISP_AWB_OECF_SKIP_FRAME  0
++// 0x0BC [31:30] SEL - sc0 input mux for sc awb
++// 00 : after DEC, 01 : after OBC, 10 : after OECF, 11 : after AWB
++enum scd_type {
++      DEC_TYPE = 0,
++      OBC_TYPE,
++      OECF_TYPE,
++      AWB_TYPE
++};
++
++struct isp_format {
++      u32 code;
++      u8 bpp;
++};
++
++struct isp_format_table {
++      const struct isp_format *fmts;
++      int nfmts;
++};
++
++struct regval_t {
++      u32 addr;
++      u32 val;
++      u32 mask;
++      u32 delay_ms;
++};
++
++struct reg_table {
++      struct regval_t *regval;
++      int regval_num;
++};
++
++struct isp_stream_format {
++      struct v4l2_rect rect;
++      u32 bpp;
++};
++
++struct stf_isp_dev;
++enum subdev_type;
++
++struct isp_hw_ops {
++      int (*isp_clk_enable)(struct stf_isp_dev *isp_dev);
++      int (*isp_clk_disable)(struct stf_isp_dev *isp_dev);
++      int (*isp_reset)(struct stf_isp_dev *isp_dev);
++      int (*isp_config_set)(struct stf_isp_dev *isp_dev);
++      int (*isp_set_format)(struct stf_isp_dev *isp_dev,
++                      struct isp_stream_format *crop, u32 mcode,
++                      int type);
++                      // u32 width, u32 height);
++      int (*isp_stream_set)(struct stf_isp_dev *isp_dev, int on);
++      int (*isp_reg_read)(struct stf_isp_dev *isp_dev, void *arg);
++      int (*isp_reg_write)(struct stf_isp_dev *isp_dev, void *arg);
++      int (*isp_shadow_trigger)(struct stf_isp_dev *isp_dev);
++};
++
++struct isp_ctrls {
++      struct v4l2_ctrl_handler handler;
++      struct v4l2_ctrl *pixel_rate;
++      struct {
++              struct v4l2_ctrl *auto_exp;
++              struct v4l2_ctrl *exposure;
++      };
++      struct {
++              struct v4l2_ctrl *auto_wb;
++              struct v4l2_ctrl *blue_balance;
++              struct v4l2_ctrl *red_balance;
++      };
++      struct {
++              struct v4l2_ctrl *auto_gain;
++              struct v4l2_ctrl *gain;
++      };
++      struct v4l2_ctrl *brightness;
++      struct v4l2_ctrl *light_freq;
++      struct v4l2_ctrl *saturation;
++      struct v4l2_ctrl *contrast;
++      struct v4l2_ctrl *hue;
++      struct v4l2_ctrl *test_pattern;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vflip;
++};
++
++struct isp_setfile {
++      struct reg_table settings;
++      const u8 *data;
++      unsigned int size;
++      unsigned int state;
++};
++
++enum {
++      ISP_CROP = 0,
++      ISP_COMPOSE,
++      ISP_SCALE_SS0,
++      ISP_SCALE_SS1,
++      ISP_ITIWS,
++      ISP_RECT_MAX
++};
++
++struct stf_isp_dev {
++      enum subdev_type sdev_type;  // must be frist
++      struct stfcamss *stfcamss;
++      struct v4l2_subdev subdev;
++      struct media_pad pads[STF_ISP_PAD_MAX];
++      struct v4l2_mbus_framefmt fmt[STF_ISP_PAD_MAX];
++      struct isp_stream_format rect[ISP_RECT_MAX];
++      const struct isp_format_table *formats;
++      unsigned int nformats;
++      struct isp_hw_ops *hw_ops;
++      struct mutex power_lock;
++      int power_count;
++      struct mutex stream_lock;
++      int stream_count;
++      atomic_t shadow_count;
++
++      struct isp_ctrls ctrls;
++      struct mutex setfile_lock;
++      struct isp_setfile setfile;
++      struct reg_table *context_regs;
++};
++
++extern int stf_isp_subdev_init(struct stfcamss *stfcamss);
++extern int stf_isp_register(struct stf_isp_dev *isp_dev,
++              struct v4l2_device *v4l2_dev);
++extern int stf_isp_unregister(struct stf_isp_dev *isp_dev);
++extern struct isp_hw_ops isp_ops;
++extern void dump_isp_reg(void *__iomem ispbase);
++
++#endif /* STF_ISP_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c
+@@ -0,0 +1,1550 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/io.h>
++#include <linux/fb.h>
++#include <linux/module.h>
++#include <video/stf-vin.h>
++#include "stf_isp_ioctl.h"
++#include "stf_isp.h"
++#include <linux/delay.h>
++#include <linux/clk.h>
++#define USE_NEW_CONFIG_SETTING
++
++static struct regval_t isp_reg_init_config_list[] = {
++      /* config DC(0040H~0044H) */
++      {0x00000044, 0x00000000, 0, 0},
++      /* config DEC(0030H) */
++      {0x00000030, 0x00000000, 0, 0},
++      /* config OBC(0034H, 02E0H~02FCH) */
++      {0x00000034, 0x000000BB, 0, 0},
++      {0x000002E0, 0x40404040, 0, 0},
++      {0x000002E4, 0x40404040, 0, 0},
++      {0x000002E8, 0x40404040, 0, 0},
++      {0x000002EC, 0x40404040, 0, 0},
++      {0x000002F0, 0x00000000, 0, 0},
++      {0x000002F4, 0x00000000, 0, 0},
++      {0x000002F8, 0x00000000, 0, 0},
++      {0x000002FC, 0x00000000, 0, 0},
++      /* config LCBQ(0074H, 007CH, 0300H~039FH, and 0400H~049FH) */
++      {0x00000074, 0x00009900, 0, 0},
++      {0x0000007C, 0x01E40040, 0, 0},
++      {0x00000300, 0x01000100, 0, 0},
++      {0x00000304, 0x01000100, 0, 0},
++      {0x00000308, 0x01000100, 0, 0},
++      {0x0000030C, 0x01000100, 0, 0},
++      {0x00000310, 0x01000100, 0, 0},
++      {0x00000314, 0x01000100, 0, 0},
++      {0x00000318, 0x01000100, 0, 0},
++      {0x0000031C, 0x01000100, 0, 0},
++      {0x00000320, 0x01000100, 0, 0},
++      {0x00000324, 0x01000100, 0, 0},
++      {0x00000328, 0x01000100, 0, 0},
++      {0x0000032C, 0x01000100, 0, 0},
++      {0x00000330, 0x00000100, 0, 0},
++      {0x00000334, 0x01000100, 0, 0},
++      {0x00000338, 0x01000100, 0, 0},
++      {0x0000033C, 0x01000100, 0, 0},
++      {0x00000340, 0x01000100, 0, 0},
++      {0x00000344, 0x01000100, 0, 0},
++      {0x00000348, 0x01000100, 0, 0},
++      {0x0000034C, 0x01000100, 0, 0},
++      {0x00000350, 0x01000100, 0, 0},
++      {0x00000354, 0x01000100, 0, 0},
++      {0x00000358, 0x01000100, 0, 0},
++      {0x0000035C, 0x01000100, 0, 0},
++      {0x00000360, 0x01000100, 0, 0},
++      {0x00000364, 0x00000100, 0, 0},
++      {0x00000368, 0x01000100, 0, 0},
++      {0x0000036C, 0x01000100, 0, 0},
++      {0x00000370, 0x01000100, 0, 0},
++      {0x00000374, 0x01000100, 0, 0},
++      {0x00000378, 0x01000100, 0, 0},
++      {0x0000037C, 0x01000100, 0, 0},
++      {0x00000380, 0x01000100, 0, 0},
++      {0x00000384, 0x01000100, 0, 0},
++      {0x00000388, 0x01000100, 0, 0},
++      {0x0000038C, 0x01000100, 0, 0},
++      {0x00000390, 0x01000100, 0, 0},
++      {0x00000394, 0x01000100, 0, 0},
++      {0x00000398, 0x00000100, 0, 0},
++      {0x0000039C, 0x01000100, 0, 0},
++      {0x000003A0, 0x01000100, 0, 0},
++      {0x000003A4, 0x01000100, 0, 0},
++      {0x000003A8, 0x01000100, 0, 0},
++      {0x000003AC, 0x01000100, 0, 0},
++      {0x000003B0, 0x01000100, 0, 0},
++      {0x000003B4, 0x01000100, 0, 0},
++      {0x000003B8, 0x01000100, 0, 0},
++      {0x000003BC, 0x01000100, 0, 0},
++      {0x000003C0, 0x01000100, 0, 0},
++      {0x000003C4, 0x01000100, 0, 0},
++      {0x000003C8, 0x01000100, 0, 0},
++      {0x000003CC, 0x00000100, 0, 0},
++      {0x00000400, 0x00000000, 0, 0},
++      {0x00000404, 0x00000000, 0, 0},
++      {0x00000408, 0x00000000, 0, 0},
++      {0x0000040C, 0x00000000, 0, 0},
++      {0x00000410, 0x00000000, 0, 0},
++      {0x00000414, 0x00000000, 0, 0},
++      {0x00000418, 0x00000000, 0, 0},
++      {0x0000041C, 0x00000000, 0, 0},
++      {0x00000420, 0x00000000, 0, 0},
++      {0x00000424, 0x00000000, 0, 0},
++      {0x00000428, 0x00000000, 0, 0},
++      {0x0000042C, 0x00000000, 0, 0},
++      {0x00000430, 0x00000000, 0, 0},
++      {0x00000434, 0x00000000, 0, 0},
++      {0x00000438, 0x00000000, 0, 0},
++      {0x0000043C, 0x00000000, 0, 0},
++      {0x00000440, 0x00000000, 0, 0},
++      {0x00000444, 0x00000000, 0, 0},
++      {0x00000448, 0x00000000, 0, 0},
++      {0x0000044C, 0x00000000, 0, 0},
++      {0x00000450, 0x00000000, 0, 0},
++      {0x00000454, 0x00000000, 0, 0},
++      {0x00000458, 0x00000000, 0, 0},
++      {0x0000045C, 0x00000000, 0, 0},
++      {0x00000460, 0x00000000, 0, 0},
++      {0x00000464, 0x00000000, 0, 0},
++      {0x00000468, 0x00000000, 0, 0},
++      {0x0000046C, 0x00000000, 0, 0},
++      {0x00000470, 0x00000000, 0, 0},
++      {0x00000474, 0x00000000, 0, 0},
++      {0x00000478, 0x00000000, 0, 0},
++      {0x0000047C, 0x00000000, 0, 0},
++      {0x00000480, 0x00000000, 0, 0},
++      {0x00000484, 0x00000000, 0, 0},
++      {0x00000488, 0x00000000, 0, 0},
++      {0x0000048C, 0x00000000, 0, 0},
++      {0x00000490, 0x00000000, 0, 0},
++      {0x00000494, 0x00000000, 0, 0},
++      {0x00000498, 0x00000000, 0, 0},
++      {0x0000049C, 0x00000000, 0, 0},
++      {0x000004A0, 0x00000000, 0, 0},
++      {0x000004A4, 0x00000000, 0, 0},
++      {0x000004A8, 0x00000000, 0, 0},
++      {0x000004AC, 0x00000000, 0, 0},
++      {0x000004B0, 0x00000000, 0, 0},
++      {0x000004B4, 0x00000000, 0, 0},
++      {0x000004B8, 0x00000000, 0, 0},
++      {0x000004BC, 0x00000000, 0, 0},
++      {0x000004C0, 0x00000000, 0, 0},
++      {0x000004C4, 0x00000000, 0, 0},
++      {0x000004C8, 0x00000000, 0, 0},
++      {0x000004CC, 0x00000000, 0, 0},
++      /* config OECF(0100H~027CH) */
++      {0x00000100, 0x00100000, 0, 0},
++      {0x00000104, 0x00400020, 0, 0},
++      {0x00000108, 0x00800060, 0, 0},
++      {0x0000010C, 0x00C000A0, 0, 0},
++      {0x00000110, 0x010000E0, 0, 0},
++      {0x00000114, 0x02000180, 0, 0},
++      {0x00000118, 0x03000280, 0, 0},
++      {0x0000011C, 0x03FE0380, 0, 0},
++      {0x00000120, 0x00100000, 0, 0},
++      {0x00000124, 0x00400020, 0, 0},
++      {0x00000128, 0x00800060, 0, 0},
++      {0x0000012C, 0x00C000A0, 0, 0},
++      {0x00000130, 0x010000E0, 0, 0},
++      {0x00000134, 0x02000180, 0, 0},
++      {0x00000138, 0x03000280, 0, 0},
++      {0x0000013C, 0x03FE0380, 0, 0},
++      {0x00000140, 0x00100000, 0, 0},
++      {0x00000144, 0x00400020, 0, 0},
++      {0x00000148, 0x00800060, 0, 0},
++      {0x0000014C, 0x00C000A0, 0, 0},
++      {0x00000150, 0x010000E0, 0, 0},
++      {0x00000154, 0x02000180, 0, 0},
++      {0x00000158, 0x03000280, 0, 0},
++      {0x0000015C, 0x03FE0380, 0, 0},
++      {0x00000160, 0x00100000, 0, 0},
++      {0x00000164, 0x00400020, 0, 0},
++      {0x00000168, 0x00800060, 0, 0},
++      {0x0000016C, 0x00C000A0, 0, 0},
++      {0x00000170, 0x010000E0, 0, 0},
++      {0x00000174, 0x02000180, 0, 0},
++      {0x00000178, 0x03000280, 0, 0},
++      {0x0000017C, 0x03FE0380, 0, 0},
++      {0x00000180, 0x00100000, 0, 0},
++      {0x00000184, 0x00400020, 0, 0},
++      {0x00000188, 0x00800060, 0, 0},
++      {0x0000018C, 0x00C000A0, 0, 0},
++      {0x00000190, 0x010000E0, 0, 0},
++      {0x00000194, 0x02000180, 0, 0},
++      {0x00000198, 0x03000280, 0, 0},
++      {0x0000019C, 0x03FE0380, 0, 0},
++      {0x000001A0, 0x00100000, 0, 0},
++      {0x000001A4, 0x00400020, 0, 0},
++      {0x000001A8, 0x00800060, 0, 0},
++      {0x000001AC, 0x00C000A0, 0, 0},
++      {0x000001B0, 0x010000E0, 0, 0},
++      {0x000001B4, 0x02000180, 0, 0},
++      {0x000001B8, 0x03000280, 0, 0},
++      {0x000001BC, 0x03FE0380, 0, 0},
++      {0x000001C0, 0x00100000, 0, 0},
++      {0x000001C4, 0x00400020, 0, 0},
++      {0x000001C8, 0x00800060, 0, 0},
++      {0x000001CC, 0x00C000A0, 0, 0},
++      {0x000001D0, 0x010000E0, 0, 0},
++      {0x000001D4, 0x02000180, 0, 0},
++      {0x000001D8, 0x03000280, 0, 0},
++      {0x000001DC, 0x03FE0380, 0, 0},
++      {0x000001E0, 0x00100000, 0, 0},
++      {0x000001E4, 0x00400020, 0, 0},
++      {0x000001E8, 0x00800060, 0, 0},
++      {0x000001EC, 0x00C000A0, 0, 0},
++      {0x000001F0, 0x010000E0, 0, 0},
++      {0x000001F4, 0x02000180, 0, 0},
++      {0x000001F8, 0x03000280, 0, 0},
++      {0x000001FC, 0x03FE0380, 0, 0},
++      {0x00000200, 0x00800080, 0, 0},
++      {0x00000204, 0x00800080, 0, 0},
++      {0x00000208, 0x00800080, 0, 0},
++      {0x0000020C, 0x00800080, 0, 0},
++      {0x00000210, 0x00800080, 0, 0},
++      {0x00000214, 0x00800080, 0, 0},
++      {0x00000218, 0x00800080, 0, 0},
++      {0x0000021C, 0x00800080, 0, 0},
++      {0x00000220, 0x00800080, 0, 0},
++      {0x00000224, 0x00800080, 0, 0},
++      {0x00000228, 0x00800080, 0, 0},
++      {0x0000022C, 0x00800080, 0, 0},
++      {0x00000230, 0x00800080, 0, 0},
++      {0x00000234, 0x00800080, 0, 0},
++      {0x00000238, 0x00800080, 0, 0},
++      {0x0000023C, 0x00800080, 0, 0},
++      {0x00000240, 0x00800080, 0, 0},
++      {0x00000244, 0x00800080, 0, 0},
++      {0x00000248, 0x00800080, 0, 0},
++      {0x0000024C, 0x00800080, 0, 0},
++      {0x00000250, 0x00800080, 0, 0},
++      {0x00000254, 0x00800080, 0, 0},
++      {0x00000258, 0x00800080, 0, 0},
++      {0x0000025C, 0x00800080, 0, 0},
++      {0x00000260, 0x00800080, 0, 0},
++      {0x00000264, 0x00800080, 0, 0},
++      {0x00000268, 0x00800080, 0, 0},
++      {0x0000026C, 0x00800080, 0, 0},
++      {0x00000270, 0x00800080, 0, 0},
++      {0x00000274, 0x00800080, 0, 0},
++      {0x00000278, 0x00800080, 0, 0},
++      {0x0000027C, 0x00800080, 0, 0},
++      /* config OECFHM(03D0H~03E4H) */
++      {0x000003D0, 0x04000000, 0, 0},
++      {0x000003D4, 0x0C000800, 0, 0},
++      {0x000003D8, 0x00000FFF, 0, 0},
++      {0x000003DC, 0x08000800, 0, 0},
++      {0x000003E0, 0x08000800, 0, 0},
++      {0x000003E4, 0x00000800, 0, 0},
++      /* config LCCF(0050H, 0058H, 00E0H~00ECH) */
++      {0x00000050, 0x021C03C0, 0, 0},
++      {0x00000058, 0x0000000B, 0, 0},
++      {0x000000E0, 0x00000000, 0, 0},
++      {0x000000E4, 0x00000000, 0, 0},
++      {0x000000E8, 0x00000000, 0, 0},
++      {0x000000EC, 0x00000000, 0, 0},
++      /* config AWB(0280H~02DCH) */
++      {0x00000280, 0x00000000, 0, 0},
++      {0x00000284, 0x00000000, 0, 0},
++      {0x00000288, 0x00000000, 0, 0},
++      {0x0000028C, 0x00000000, 0, 0},
++      {0x00000290, 0x00000000, 0, 0},
++      {0x00000294, 0x00000000, 0, 0},
++      {0x00000298, 0x00000000, 0, 0},
++      {0x0000029C, 0x00000000, 0, 0},
++      {0x000002A0, 0x00000000, 0, 0},
++      {0x000002A4, 0x00000000, 0, 0},
++      {0x000002A8, 0x00000000, 0, 0},
++      {0x000002AC, 0x00000000, 0, 0},
++      {0x000002B0, 0x00000000, 0, 0},
++      {0x000002B4, 0x00000000, 0, 0},
++      {0x000002B8, 0x00000000, 0, 0},
++      {0x000002BC, 0x00000000, 0, 0},
++      {0x000002C0, 0x00800080, 0, 0},
++      {0x000002C4, 0x00800080, 0, 0},
++      {0x000002C8, 0x00800080, 0, 0},
++      {0x000002CC, 0x00800080, 0, 0},
++      {0x000002D0, 0x00800080, 0, 0},
++      {0x000002D4, 0x00800080, 0, 0},
++      {0x000002D8, 0x00800080, 0, 0},
++      {0x000002DC, 0x00800080, 0, 0},
++      /* config CTC(0A10H) and DBC(0A14H) filter */
++      {0x00000A10, 0x41400040, 0, 0},
++      {0x00000A14, 0x02000200, 0, 0},
++      /* config CFA(0018H, 0A1CH) */
++      {0x00000018, 0x000011BB, 0, 0},
++      {0x00000A1C, 0x00000032, 0, 0},
++      /* config CCM(0C40H~0CA4H) */
++      {0x00000C40, 0x00060000, 0, 0},
++      {0x00000C44, 0x00000000, 0, 0},
++      {0x00000C48, 0x00000000, 0, 0},
++      {0x00000C4C, 0x00000000, 0, 0},
++      {0x00000C50, 0x00000000, 0, 0},
++      {0x00000C54, 0x00000000, 0, 0},
++      {0x00000C58, 0x00000000, 0, 0},
++      {0x00000C5C, 0x00000000, 0, 0},
++      {0x00000C60, 0x00000000, 0, 0},
++      {0x00000C64, 0x00000000, 0, 0},
++      {0x00000C68, 0x00000000, 0, 0},
++      {0x00000C6C, 0x00000000, 0, 0},
++      {0x00000C70, 0x00000080, 0, 0},
++      {0x00000C74, 0x00000000, 0, 0},
++      {0x00000C78, 0x00000000, 0, 0},
++      {0x00000C7C, 0x00000000, 0, 0},
++      {0x00000C80, 0x00000080, 0, 0},
++      {0x00000C84, 0x00000000, 0, 0},
++      {0x00000C88, 0x00000000, 0, 0},
++      {0x00000C8C, 0x00000000, 0, 0},
++      {0x00000C90, 0x00000080, 0, 0},
++      {0x00000C94, 0x00000000, 0, 0},
++      {0x00000C98, 0x00000000, 0, 0},
++      {0x00000C9C, 0x00000000, 0, 0},
++      {0x00000CA0, 0x00000700, 0, 0},
++      {0x00000CA4, 0x00000200, 0, 0},
++      /* config GMARGB(0E00H~0E38H) */
++      {0x00000E00, 0x24000000, 0, 0},
++      {0x00000E04, 0x08000020, 0, 0},
++      {0x00000E08, 0x08000040, 0, 0},
++      {0x00000E0C, 0x08000060, 0, 0},
++      {0x00000E10, 0x08000080, 0, 0},
++      {0x00000E14, 0x080000A0, 0, 0},
++      {0x00000E18, 0x080000C0, 0, 0},
++      {0x00000E1C, 0x080000E0, 0, 0},
++      {0x00000E20, 0x08000100, 0, 0},
++      {0x00000E24, 0x08000180, 0, 0},
++      {0x00000E28, 0x08000200, 0, 0},
++      {0x00000E2C, 0x08000280, 0, 0},
++      {0x00000E30, 0x08000300, 0, 0},
++      {0x00000E34, 0x08000380, 0, 0},
++      {0x00000E38, 0x080003FE, 0, 0},
++      /* config R2Y(0E40H~0E60H) */
++      {0x00000E40, 0x0000004C, 0, 0},
++      {0x00000E44, 0x00000097, 0, 0},
++      {0x00000E48, 0x0000001D, 0, 0},
++      {0x00000E4C, 0x000001D5, 0, 0},
++      {0x00000E50, 0x000001AC, 0, 0},
++      {0x00000E54, 0x00000080, 0, 0},
++      {0x00000E58, 0x00000080, 0, 0},
++      {0x00000E5C, 0x00000194, 0, 0},
++      {0x00000E60, 0x000001EC, 0, 0},
++      /* config YCRV(0F00H~0FFCH) */
++      {0x00000F00, 0x00000000, 0, 0},
++      {0x00000F04, 0x00000010, 0, 0},
++      {0x00000F08, 0x00000020, 0, 0},
++      {0x00000F0C, 0x00000030, 0, 0},
++      {0x00000F10, 0x00000040, 0, 0},
++      {0x00000F14, 0x00000050, 0, 0},
++      {0x00000F18, 0x00000060, 0, 0},
++      {0x00000F1C, 0x00000070, 0, 0},
++      {0x00000F20, 0x00000080, 0, 0},
++      {0x00000F24, 0x00000090, 0, 0},
++      {0x00000F28, 0x000000A0, 0, 0},
++      {0x00000F2C, 0x000000B0, 0, 0},
++      {0x00000F30, 0x000000C0, 0, 0},
++      {0x00000F34, 0x000000D0, 0, 0},
++      {0x00000F38, 0x000000E0, 0, 0},
++      {0x00000F3C, 0x000000F0, 0, 0},
++      {0x00000F40, 0x00000100, 0, 0},
++      {0x00000F44, 0x00000110, 0, 0},
++      {0x00000F48, 0x00000120, 0, 0},
++      {0x00000F4C, 0x00000130, 0, 0},
++      {0x00000F50, 0x00000140, 0, 0},
++      {0x00000F54, 0x00000150, 0, 0},
++      {0x00000F58, 0x00000160, 0, 0},
++      {0x00000F5C, 0x00000170, 0, 0},
++      {0x00000F60, 0x00000180, 0, 0},
++      {0x00000F64, 0x00000190, 0, 0},
++      {0x00000F68, 0x000001A0, 0, 0},
++      {0x00000F6C, 0x000001B0, 0, 0},
++      {0x00000F70, 0x000001C0, 0, 0},
++      {0x00000F74, 0x000001D0, 0, 0},
++      {0x00000F78, 0x000001E0, 0, 0},
++      {0x00000F7C, 0x000001F0, 0, 0},
++      {0x00000F80, 0x00000200, 0, 0},
++      {0x00000F84, 0x00000210, 0, 0},
++      {0x00000F88, 0x00000220, 0, 0},
++      {0x00000F8C, 0x00000230, 0, 0},
++      {0x00000F90, 0x00000240, 0, 0},
++      {0x00000F94, 0x00000250, 0, 0},
++      {0x00000F98, 0x00000260, 0, 0},
++      {0x00000F9C, 0x00000270, 0, 0},
++      {0x00000FA0, 0x00000280, 0, 0},
++      {0x00000FA4, 0x00000290, 0, 0},
++      {0x00000FA8, 0x000002A0, 0, 0},
++      {0x00000FAC, 0x000002B0, 0, 0},
++      {0x00000FB0, 0x000002C0, 0, 0},
++      {0x00000FB4, 0x000002D0, 0, 0},
++      {0x00000FB8, 0x000002E0, 0, 0},
++      {0x00000FBC, 0x000002F0, 0, 0},
++      {0x00000FC0, 0x00000300, 0, 0},
++      {0x00000FC4, 0x00000310, 0, 0},
++      {0x00000FC8, 0x00000320, 0, 0},
++      {0x00000FCC, 0x00000330, 0, 0},
++      {0x00000FD0, 0x00000340, 0, 0},
++      {0x00000FD4, 0x00000350, 0, 0},
++      {0x00000FD8, 0x00000360, 0, 0},
++      {0x00000FDC, 0x00000370, 0, 0},
++      {0x00000FE0, 0x00000380, 0, 0},
++      {0x00000FE4, 0x00000390, 0, 0},
++      {0x00000FE8, 0x000003A0, 0, 0},
++      {0x00000FEC, 0x000003B0, 0, 0},
++      {0x00000FF0, 0x000003C0, 0, 0},
++      {0x00000FF4, 0x000003D0, 0, 0},
++      {0x00000FF8, 0x000003E0, 0, 0},
++      {0x00000FFC, 0x000003F0, 0, 0},
++      /* config Shrp(0E80H~0EE8H) */
++      {0x00000E80, 0x00070F00, 0, 0},
++      {0x00000E84, 0x00180F00, 0, 0},
++      {0x00000E88, 0x00800F00, 0, 0},
++      {0x00000E8C, 0x01000F00, 0, 0},
++      {0x00000E90, 0x00100F00, 0, 0},
++      {0x00000E94, 0x00600F00, 0, 0},
++      {0x00000E98, 0x01000F00, 0, 0},
++      {0x00000E9C, 0x01900F00, 0, 0},
++      {0x00000EA0, 0x00000F00, 0, 0},
++      {0x00000EA4, 0x00000F00, 0, 0},
++      {0x00000EA8, 0x00000F00, 0, 0},
++      {0x00000EAC, 0x00000F00, 0, 0},
++      {0x00000EB0, 0x00000F00, 0, 0},
++      {0x00000EB4, 0x00000F00, 0, 0},
++      {0x00000EB8, 0x00000F00, 0, 0},
++      {0x00000EBC, 0x10000000, 0, 0},
++      {0x00000EC0, 0x10000000, 0, 0},
++      {0x00000EC4, 0x10000000, 0, 0},
++      {0x00000EC8, 0x10000000, 0, 0},
++      {0x00000ECC, 0x10000000, 0, 0},
++      {0x00000ED0, 0x10000000, 0, 0},
++      {0x00000ED4, 0x88000D7C, 0, 0},
++      {0x00000ED8, 0x00C00040, 0, 0},
++      {0x00000EDC, 0xFF000000, 0, 0},
++      {0x00000EE0, 0x00A00040, 0, 0},
++      {0x00000EE4, 0x00000000, 0, 0},
++      {0x00000EE8, 0x00000000, 0, 0},
++      /* config DNYUV(0C00H~0C24H) */
++      {0x00000C00, 0x00777777, 0, 0},
++      {0x00000C04, 0x00007777, 0, 0},
++      {0x00000C08, 0x00777777, 0, 0},
++      {0x00000C0C, 0x00007777, 0, 0},
++      {0x00000C10, 0x00600040, 0, 0},
++      {0x00000C14, 0x00D80090, 0, 0},
++      {0x00000C18, 0x01E60144, 0, 0},
++      {0x00000C1C, 0x00600040, 0, 0},
++      {0x00000C20, 0x00D80090, 0, 0},
++      {0x00000C24, 0x01E60144, 0, 0},
++      /* config SAT(0A30H~0A40H, 0A54H~0A58H) */
++      {0x00000A30, 0x00000100, 0, 0},
++      {0x00000A34, 0x001F0001, 0, 0},
++      {0x00000A38, 0x00000000, 0, 0},
++      {0x00000A3C, 0x00000100, 0, 0},
++      {0x00000A40, 0x00000008, 0, 0},
++      {0x00000A54, 0x04010001, 0, 0},
++      {0x00000A58, 0x03FF0001, 0, 0},
++      /* config OBA(0090H~0094H) */
++      {0x00000090, 0x04380000, 0, 0},
++      {0x00000094, 0x04390780, 0, 0},
++      /* config SC(0098H~009CH, 00B8H~00BCH,
++       * 00C0H, 0C4H~0D4H, 04D0H~054CH, 5D0H~5D4H)
++       */
++      {0x0000009C, 0x01000000, 0, 0},
++      {0x000000B8, 0x000C0000, 0, 0},
++      {0x000000BC, 0xC010151D, 0, 0},
++      {0x000000C0, 0x01F1BF08, 0, 0},
++      {0x000000C4, 0xFF00FF00, 0, 0},
++      {0x000000C8, 0xFF00FF00, 0, 0},
++      {0x000000CC, 0xFFFF0000, 0, 0},
++      {0x000000D0, 0xFFFF0000, 0, 0},
++      {0x000000D4, 0xFFFF0000, 0, 0},
++      {0x000000D8, 0x01050107, 0, 0},
++      {0x000004D0, 0x00000000, 0, 0},
++      {0x000004D4, 0x00000000, 0, 0},
++      {0x000004D8, 0x00000000, 0, 0},
++      {0x000004DC, 0x00000000, 0, 0},
++      {0x000004E0, 0x00000000, 0, 0},
++      {0x000004E4, 0x00000000, 0, 0},
++      {0x000004E8, 0x00000000, 0, 0},
++      {0x000004EC, 0x00000000, 0, 0},
++      {0x000004F0, 0x00100000, 0, 0},
++      {0x000004F4, 0x00000000, 0, 0},
++      {0x000004F8, 0x03D20000, 0, 0},
++      {0x000004FC, 0x00000000, 0, 0},
++      {0x00000500, 0x00950000, 0, 0},
++      {0x00000504, 0x00000000, 0, 0},
++      {0x00000508, 0x00253000, 0, 0},
++      {0x0000050C, 0x00000000, 0, 0},
++      {0x00000510, 0x00000000, 0, 0},
++      {0x00000514, 0x00000000, 0, 0},
++      {0x00000518, 0x00000000, 0, 0},
++      {0x0000051C, 0x00000000, 0, 0},
++      {0x00000520, 0x00000000, 0, 0},
++      {0x00000524, 0x00000000, 0, 0},
++      {0x00000528, 0x00000000, 0, 0},
++      {0x0000052C, 0x00000000, 0, 0},
++      {0x00000530, 0x00000000, 0, 0},
++      {0x00000534, 0x00000000, 0, 0},
++      {0x00000538, 0xFFFFFFF0, 0, 0},
++      {0x0000053C, 0x8FFFFFFF, 0, 0},
++      {0x00000540, 0x0000001E, 0, 0},
++      {0x00000544, 0x00000000, 0, 0},
++      {0x00000548, 0x00000000, 0, 0},
++      {0x0000054C, 0xF0F20000, 0, 0},
++      {0x000005D0, 0xFF00FF00, 0, 0},
++      {0x000005D4, 0xFF00FF00, 0, 0},
++      /* config YHIST(0CC8H~0CD8H) */
++      {0x00000CC8, 0x00000000, 0, 0},
++      {0x00000CCC, 0x0437077F, 0, 0},
++      {0x00000CD0, 0x00010002, 0, 0},
++      {0x00000CD4, 0x00000000, 0, 0},
++      /* config CBAR(0600H-0653H) */
++      {0x00000600, 0x043E0782, 0, 0},
++      {0x00000604, 0x00000000, 0, 0},
++      {0x00000608, 0x0437077F, 0, 0},
++      {0x0000060C, 0x00443150, 0, 0},
++      {0x00000610, 0x00000000, 0, 0},
++      {0x00000614, 0x08880888, 0, 0},
++      {0x00000618, 0x02220222, 0, 0},
++      {0x0000061C, 0x04440444, 0, 0},
++      {0x00000620, 0x08880888, 0, 0},
++      {0x00000624, 0x0AAA0AAA, 0, 0},
++      {0x00000628, 0x0CCC0CCC, 0, 0},
++      {0x0000062C, 0x0EEE0EEE, 0, 0},
++      {0x00000630, 0x0FFF0FFF, 0, 0},
++      {0x00000634, 0x08880888, 0, 0},
++      {0x00000638, 0x02220222, 0, 0},
++      {0x0000063C, 0x04440444, 0, 0},
++      {0x00000640, 0x08880888, 0, 0},
++      {0x00000644, 0x0AAA0AAA, 0, 0},
++      {0x00000648, 0x0CCC0CCC, 0, 0},
++      {0x0000064C, 0x0EEE0EEE, 0, 0},
++      {0x00000650, 0x0FFF0FFF, 0, 0},
++      /* config sensor(0014H) */
++      {0x00000014, 0x0000000c, 0, 0},
++      /* config CROP(001CH, 0020H) */
++      {0x0000001C, 0x00000000, 0, 0},
++      {0x00000020, 0x0437077F, 0, 0},
++      /* config isp pileline X/Y size(A0CH) */
++      {0x00000A0C, 0x04380780, 0, 0},
++      /* config CSI dump (24H/28H) */
++      {0x00000028, 0x00030B80, 0, 0},
++      /* Video Output */
++      /* config UO(0A80H~0A90H) */
++      {0x00000A88, 0x00000780, 0, 0},
++      /* NV12 */
++      {0x00000A8C, 0x00000000, 0, 0},
++      /* NV21
++       *{0x00000A8C, 0x00000020, 0, 0},
++       */
++      {0x00000A90, 0x00000000, 0, 0},
++      {0x00000A9C, 0x00000780, 0, 0},
++      {0x00000AA0, 0x00000002, 0, 0},
++      {0x00000AA4, 0x00000002, 0, 0},
++      {0x00000AA8, 0x07800438, 0, 0},
++      {0x00000AB4, 0x00000780, 0, 0},
++      {0x00000AB8, 0x00000002, 0, 0},
++      {0x00000ABC, 0x00000002, 0, 0},
++      {0x00000AC0, 0x07800438, 0, 0},
++      {0x00000AC4, 0x00000000, 0, 0},
++      /* config TIL(0B20H~0B48H) */
++      {0x00000B20, 0x04380780, 0, 0},
++      {0x00000B24, 0x00000960, 0, 0},
++      {0x00000B38, 0x00030003, 0, 0},
++      {0x00000B3C, 0x00000960, 0, 0},
++      {0x00000B44, 0x00000000, 0, 0},
++      {0x00000B48, 0x00000000, 0, 0},
++      /* Enable DEC/OBC/OECF/LCCF/AWB/SC/DUMP */
++      {0x00000010, 0x000A00D6, 0x00000000, 0x00},
++      /* Enable CFA/CAR/CCM/GMARGB/R2Y/SHRP/SAT/DNYUV/YCRV/YHIST/CTC/DBC */
++      {0x00000A08, 0x107A01BE, 0x00000000, 0x00},
++};
++
++const struct reg_table isp_reg_init_settings[] = {
++      {isp_reg_init_config_list,
++      ARRAY_SIZE(isp_reg_init_config_list)},
++};
++
++static struct regval_t isp_reg_start_config_list[] = {
++#if defined(ENABLE_SS0_SS1)
++      /* ENABLE UO/SS0/SS1/Multi-Frame and Reset ISP */
++      {0x00000A00, 0x00121802, 0x00000000, 0x0A},
++      /* ENABLE UO/SS0/SS1/Multi-Frame and Leave ISP reset */
++      {0x00000A00, 0x00121800, 0x00000000, 0x0A},
++#else
++      /* ENABLE UO/Multi-Frame and Reset ISP */
++      {0x00000A00, 0x00120002, 0x00000000, 0x0A},
++      /* ENABLE UO/Multi-Frame and Leave ISP reset */
++      {0x00000A00, 0x00120000, 0x00000000, 0x0A},
++#endif
++      /* Config ISP shadow mode as next-vsync */
++      {0x00000A50, 0x00000002, 0x00000000, 0x00},
++#if defined(ENABLE_SS0_SS1)
++      /* ENABLE UO/SS0/SS1/Multi-Frame and Enable ISP */
++      {0x00000A00, 0x00121801, 0x00000000, 0x0A},
++#else
++      /* ENABLE UO/Multi-Frame and Enable ISP */
++      {0x00000A00, 0x00120001, 0x00000000, 0x0A},
++#endif
++      /* Config CSI shadow mode as immediate to fetch current setting */
++      {0x00000008, 0x00010004, 0x00000000, 0x0A},
++      /* Config CSI shadow mode as next-vsync */
++      {0x00000008, 0x00020004, 0x00000000, 0x00},
++      /* Enable CSI */
++      {0x00000000, 0x00000001, 0x00000000, 0x0A},
++};
++
++const struct reg_table isp_reg_start_settings[] = {
++      {isp_reg_start_config_list,
++      ARRAY_SIZE(isp_reg_start_config_list)},
++};
++
++static struct regval_t isp_imx_219_reg_config_list[] = {
++      /* MIPI sensor */
++      {0x00000014, 0x0000000D, 0, 0},
++      /* config CFA(0018H, 0A1CH) */
++      {0x00000A1C, 0x00000032, 0, 0},
++      {0x00000A8C, 0x00000000, 0, 0},
++      {0x00000A90, 0x00000000, 0, 0},
++      /* config R2Y(0E40H~0E60H) */
++      {0x00000E40, 0x0000004C, 0, 0},
++      {0x00000E44, 0x00000097, 0, 0},
++      {0x00000E48, 0x0000001D, 0, 0},
++      {0x00000E4C, 0x000001D5, 0, 0},
++      {0x00000E50, 0x000001AC, 0, 0},
++      {0x00000E54, 0x00000080, 0, 0},
++      {0x00000E58, 0x00000080, 0, 0},
++      {0x00000E5C, 0x00000194, 0, 0},
++      {0x00000E60, 0x000001EC, 0, 0},
++      /* Config AWB(0280H~02DCH). Fixed WB gain for IMX-219 sensor. */
++      {0x00000280, 0x00000000, 0, 0},
++      {0x00000284, 0x00000000, 0, 0},
++      {0x00000288, 0x00000000, 0, 0},
++      {0x0000028C, 0x00000000, 0, 0},
++      {0x00000290, 0x00000000, 0, 0},
++      {0x00000294, 0x00000000, 0, 0},
++      {0x00000298, 0x00000000, 0, 0},
++      {0x0000029C, 0x00000000, 0, 0},
++      {0x000002A0, 0x00000000, 0, 0},
++      {0x000002A4, 0x00000000, 0, 0},
++      {0x000002A8, 0x00000000, 0, 0},
++      {0x000002AC, 0x00000000, 0, 0},
++      {0x000002B0, 0x00000000, 0, 0},
++      {0x000002B4, 0x00000000, 0, 0},
++      {0x000002B8, 0x00000000, 0, 0},
++      {0x000002BC, 0x00000000, 0, 0},
++      {0x000002C0, 0x00F000F0, 0, 0},
++      {0x000002C4, 0x00F000F0, 0, 0},
++      {0x000002C8, 0x00800080, 0, 0},
++      {0x000002CC, 0x00800080, 0, 0},
++      {0x000002D0, 0x00800080, 0, 0},
++      {0x000002D4, 0x00800080, 0, 0},
++      {0x000002D8, 0x00B000B0, 0, 0},
++      {0x000002DC, 0x00B000B0, 0, 0},
++      /* config GMARGB(0E00H~0E38H)
++       * Gamma RGB 1.9 for IMX-219 sensor
++       */
++      {0x00000E00, 0x24000000, 0, 0},
++      {0x00000E04, 0x159500A5, 0, 0},
++      {0x00000E08, 0x0F9900EE, 0, 0},
++      {0x00000E0C, 0x0CE40127, 0, 0},
++      {0x00000E10, 0x0B410157, 0, 0},
++      {0x00000E14, 0x0A210181, 0, 0},
++      {0x00000E18, 0x094B01A8, 0, 0},
++      {0x00000E1C, 0x08A401CC, 0, 0},
++      {0x00000E20, 0x081D01EE, 0, 0},
++      {0x00000E24, 0x06B20263, 0, 0},
++      {0x00000E28, 0x05D802C7, 0, 0},
++      {0x00000E2C, 0x05420320, 0, 0},
++      {0x00000E30, 0x04D30370, 0, 0},
++      {0x00000E34, 0x047C03BB, 0, 0},
++      {0x00000E38, 0x043703FF, 0, 0},
++      {0x00000010, 0x00000080, 0, 0},
++      /* Enable CFA/GMARGB/R2Y */
++      {0x00000A08, 0x10000032, 0x0FFFFFFF, 0x00},
++      {0x00000A00, 0x00120002, 0, 0},
++      {0x00000A00, 0x00120000, 0, 0},
++      {0x00000A50, 0x00000002, 0, 0},
++      {0x00000008, 0x00010000, 0, 0},
++      {0x00000008, 0x0002000A, 0, 0},
++      {0x00000000, 0x00000001, 0, 0},
++};
++
++const struct reg_table isp_imx_219_settings[] = {
++      {isp_imx_219_reg_config_list,
++      ARRAY_SIZE(isp_imx_219_reg_config_list)},
++};
++
++static struct regval_t isp_format_reg_list[] = {
++      {0x0000001C, 0x00000000, 0x00000000, 0},
++      {0x00000020, 0x0437077F, 0x00000000, 0},
++      {0x00000A0C, 0x04380780, 0x00000000, 0},
++      {0x00000A88, 0x00000780, 0x00000000, 0},
++      {0x00000018, 0x000011BB, 0x00000000, 0},
++      {0x00000A08, 0x10000000, 0xF0000000, 0},
++      {0x00000028, 0x00030B80, 0x0003FFFF, 0},
++      {0x00000AA8, 0x07800438, 0x00000000, 0},
++      {0x00000A9C, 0x00000780, 0x00000000, 0},
++      {0x00000AC0, 0x07800438, 0x00000000, 0},
++      {0x00000AB4, 0x00000780, 0x00000000, 0},
++      {0x00000B20, 0x04380780, 0x00000000, 0},
++      {0x00000B24, 0x00000960, 0x00000000, 0},
++      {0x00000B3C, 0x00000960, 0x00000000, 0},
++      {0x00000014, 0x00000008, 0x00000000, 0},
++};
++
++const struct reg_table  isp_format_settings[] = {
++      {isp_format_reg_list,
++      ARRAY_SIZE(isp_format_reg_list)},
++};
++
++#if defined(USE_NEW_CONFIG_SETTING)
++#else
++static struct reg_table  *isp_settings = (struct reg_table *)isp_imx_219_settings;
++#endif
++
++static void isp_load_regs(void __iomem *ispbase, const struct reg_table *table)
++{
++      int j;
++      u32 delay_ms, reg_addr, mask, val;
++
++      for (j = 0; j < table->regval_num; j++) {
++              delay_ms = table->regval[j].delay_ms;
++              reg_addr = table->regval[j].addr;
++              val = table->regval[j].val;
++              mask = table->regval[j].mask;
++
++              if (reg_addr % 4
++                      || reg_addr > STF_ISP_REG_OFFSET_MAX
++                      || delay_ms > STF_ISP_REG_DELAY_MAX)
++                      continue;
++
++              if (mask)
++                      reg_set_bit(ispbase, reg_addr, mask, val);
++              else
++                      reg_write(ispbase, reg_addr, val);
++              if (delay_ms)
++                      usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++      }
++}
++
++static void isp_load_regs_exclude_csi_isp_enable(
++                      void __iomem *ispbase,
++                      const struct reg_table *table)
++{
++      int j;
++      u32 delay_ms, reg_addr, mask, val;
++
++      for (j = 0; j < table->regval_num; j++) {
++              delay_ms = table->regval[j].delay_ms;
++              reg_addr = table->regval[j].addr;
++              val = table->regval[j].val;
++              mask = table->regval[j].mask;
++
++              if (reg_addr % 4
++                      || reg_addr > STF_ISP_REG_OFFSET_MAX
++                      || delay_ms > STF_ISP_REG_DELAY_MAX
++                      || ((reg_addr == ISP_REG_CSI_INPUT_EN_AND_STATUS) && (val & 0x01))
++                      || ((reg_addr == ISP_REG_ISP_CTRL_0) && (val & 0x01)))
++                      continue;
++
++              if (mask)
++                      reg_set_bit(ispbase, reg_addr, mask, val);
++              else
++                      reg_write(ispbase, reg_addr, val);
++              if (delay_ms)
++                      usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
++      }
++}
++
++static int stf_isp_clk_enable(struct stf_isp_dev *isp_dev)
++{
++      struct stfcamss *stfcamss = isp_dev->stfcamss;
++
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_WRAPPER_C].rstc);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_WRAPPER_P].rstc);
++
++      return 0;
++}
++
++static int stf_isp_clk_disable(struct stf_isp_dev *isp_dev)
++{
++      struct stfcamss *stfcamss = isp_dev->stfcamss;
++
++      reset_control_assert(stfcamss->sys_rst[STFRST_WRAPPER_C].rstc);
++      reset_control_assert(stfcamss->sys_rst[STFRST_WRAPPER_P].rstc);
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_WRAPPER_CLK_C].clk);
++
++      return 0;
++}
++
++static  void __iomem *stf_isp_get_ispbase(struct stf_vin_dev *vin)
++{
++      void __iomem *base = vin->isp_base;
++
++      return base;
++}
++
++static int stf_isp_save_ctx_regs(struct stf_isp_dev *isp_dev)
++{
++      int j;
++      u32 addr, val;
++      void __iomem *ispbase;
++      struct device *dev = isp_dev->stfcamss->dev;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      if (!isp_dev->context_regs) {
++              int regs_size =
++                      sizeof(struct regval_t) * isp_reg_init_settings->regval_num;
++              isp_dev->context_regs =
++                      devm_kzalloc(dev, sizeof(struct reg_table), GFP_KERNEL);
++              isp_dev->context_regs->regval =
++                      devm_kzalloc(dev, regs_size, GFP_KERNEL);
++              isp_dev->context_regs->regval_num = isp_reg_init_settings->regval_num;
++      }
++
++      if (!isp_dev->context_regs || !isp_dev->context_regs->regval)
++              return -ENOMEM;
++
++      st_debug(ST_ISP, "Saving ISP context registers\n");
++      for (j = 0; j < isp_reg_init_settings->regval_num; j++) {
++              addr = isp_reg_init_settings->regval[j].addr;
++              val = ioread32(ispbase + addr);
++              isp_dev->context_regs->regval[j].addr = addr;
++              isp_dev->context_regs->regval[j].val = val;
++      }
++      st_debug(ST_ISP, "ISP context registers have been saved\n");
++
++      return 0;
++};
++
++static int stf_isp_restore_ctx_regs(struct stf_isp_dev *isp_dev)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      if (isp_dev->context_regs) {
++              isp_load_regs(ispbase, isp_dev->context_regs);
++              st_debug(ST_ISP, "Restored ISP register: isp_reg_init_settings.\n");
++      }
++
++      return 0;
++}
++
++static int stf_isp_reset(struct stf_isp_dev *isp_dev)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), BIT(1));
++      reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), 0);
++
++      return 0;
++}
++
++static int stf_isp_config_set(struct stf_isp_dev *isp_dev)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      st_debug(ST_ISP, "%s\n", __func__);
++
++#if defined(USE_NEW_CONFIG_SETTING)
++      mutex_lock(&isp_dev->setfile_lock);
++
++      if (isp_dev->context_regs) {
++              stf_isp_restore_ctx_regs(isp_dev);
++              st_debug(ST_ISP, "%s context regs restore done\n", __func__);
++      } else {
++              isp_load_regs(ispbase, isp_reg_init_settings);
++              st_debug(ST_ISP, "%s isp_reg_init_settings done\n", __func__);
++      }
++      if (isp_dev->setfile.state) {
++              st_info(ST_ISP, "%s, Program extra ISP setting!\n", __func__);
++              isp_load_regs_exclude_csi_isp_enable(ispbase,
++                      &isp_dev->setfile.settings);
++      }
++
++      mutex_unlock(&isp_dev->setfile_lock);
++#else
++      mutex_lock(&isp_dev->setfile_lock);
++      if (isp_dev->setfile.state)
++              isp_load_regs(ispbase, &isp_dev->setfile.settings);
++      else
++              isp_load_regs(ispbase, isp_settings);
++      mutex_unlock(&isp_dev->setfile_lock);
++
++      st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++                      isp_format_reg_list[0].addr,
++                      isp_format_reg_list[0].val);
++      st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++                      isp_format_reg_list[1].addr,
++                      isp_format_reg_list[1].val);
++      st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++                      isp_format_reg_list[2].addr,
++                      isp_format_reg_list[2].val);
++      st_debug(ST_ISP, "config 0x%x = 0x%x\n",
++                      isp_format_reg_list[3].addr,
++                      isp_format_reg_list[3].val);
++#endif
++
++      return 0;
++}
++
++static int stf_isp_set_format(struct stf_isp_dev *isp_dev,
++              struct isp_stream_format *crop_array, u32 mcode,
++              int type)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      struct stf_dvp_dev *dvp_dev = isp_dev->stfcamss->dvp_dev;
++      struct v4l2_rect *crop = &crop_array[ISP_COMPOSE].rect;
++      u32 bpp = crop_array[ISP_COMPOSE].bpp;
++      void __iomem *ispbase;
++      u32 val, val1;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      st_debug(ST_ISP, "interface type is %d(%s)\n",
++                      type, type == CSI_SENSOR ? "CSI" : "DVP");
++
++      if (type == DVP_SENSOR) {
++              unsigned int flags = dvp_dev->dvp->flags;
++
++              st_debug(ST_ISP, "dvp flags = 0x%x, hsync active is %s, vsync active is %s\n",
++                      flags, flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH ? "high" : "low",
++                      flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ? "high" : "low");
++      }
++
++      val = crop->left + (crop->top << 16);
++      isp_format_reg_list[0].addr = ISP_REG_PIC_CAPTURE_START_CFG;
++      isp_format_reg_list[0].val = val;
++
++      val = (crop->width + crop->left - 1)
++              + ((crop->height + crop->top - 1) << 16);
++      isp_format_reg_list[1].addr = ISP_REG_PIC_CAPTURE_END_CFG;
++      isp_format_reg_list[1].val = val;
++
++      val = crop->width + (crop->height << 16);
++      isp_format_reg_list[2].addr = ISP_REG_PIPELINE_XY_SIZE;
++      isp_format_reg_list[2].val = val;
++
++      isp_format_reg_list[3].addr = ISP_REG_STRIDE;
++      isp_format_reg_list[3].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++      switch (mcode) {
++      case MEDIA_BUS_FMT_SRGGB10_1X10:
++      case MEDIA_BUS_FMT_SRGGB8_1X8:
++              // 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R
++              val = 0x0000EE44;
++              val1 = 0x00000000;
++              break;
++      case MEDIA_BUS_FMT_SGRBG10_1X10:
++      case MEDIA_BUS_FMT_SGRBG8_1X8:
++              // 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr
++              val = 0x0000BB11;
++              val1 = 0x20000000;
++              break;
++      case MEDIA_BUS_FMT_SGBRG10_1X10:
++      case MEDIA_BUS_FMT_SGBRG8_1X8:
++              // 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb
++              val = 0x000044EE;
++              val1 = 0x30000000;
++              break;
++      case MEDIA_BUS_FMT_SBGGR10_1X10:
++      case MEDIA_BUS_FMT_SBGGR8_1X8:
++              // 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B
++              val = 0x000011BB;
++              val1 = 0x10000000;
++              break;
++      default:
++              st_err(ST_ISP, "UNKNOW format\n");
++              val = 0x000011BB;
++              val1 = 0x10000000;
++              break;
++      }
++
++      isp_format_reg_list[4].addr = ISP_REG_RAW_FORMAT_CFG;
++      isp_format_reg_list[4].val = val;
++
++      isp_format_reg_list[5].addr = ISP_REG_ISP_CTRL_1;
++      isp_format_reg_list[5].val = val1;
++      isp_format_reg_list[5].mask = 0xF0000000;
++
++      st_info(ST_ISP, "src left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++              crop->left, crop->top, crop->width, crop->height, bpp);
++
++      crop = &crop_array[ISP_CROP].rect;
++      bpp = crop_array[ISP_CROP].bpp;
++      val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_128);
++      isp_format_reg_list[6].addr = ISP_REG_DUMP_CFG_1;
++      isp_format_reg_list[6].val = val | 3 << 16;
++      isp_format_reg_list[6].mask = 0x0003FFFF;
++
++      st_info(ST_ISP, "raw left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++              crop->left, crop->top, crop->width, crop->height, bpp);
++
++      crop = &crop_array[ISP_SCALE_SS0].rect;
++      bpp = crop_array[ISP_SCALE_SS0].bpp;
++      isp_format_reg_list[7].addr = ISP_REG_SS0IW;
++      isp_format_reg_list[7].val = (crop->width << 16) + crop->height;
++      isp_format_reg_list[8].addr = ISP_REG_SS0S;
++      isp_format_reg_list[8].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++      st_info(ST_ISP, "ss0 left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++              crop->left, crop->top, crop->width, crop->height, bpp);
++
++      crop = &crop_array[ISP_SCALE_SS1].rect;
++      bpp = crop_array[ISP_SCALE_SS1].bpp;
++      isp_format_reg_list[9].addr = ISP_REG_SS1IW;
++      isp_format_reg_list[9].val = (crop->width << 16) + crop->height;
++      isp_format_reg_list[10].addr = ISP_REG_SS1S;
++      isp_format_reg_list[10].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++      crop = &crop_array[ISP_ITIWS].rect;
++      bpp = crop_array[ISP_ITIWS].bpp;
++      isp_format_reg_list[11].addr = ISP_REG_ITIIWSR;
++      isp_format_reg_list[11].val = (crop->height << 16) + crop->width;
++      isp_format_reg_list[12].addr = ISP_REG_ITIDWLSR;
++      isp_format_reg_list[12].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++      isp_format_reg_list[13].addr = ISP_REG_ITIDRLSR;
++      isp_format_reg_list[13].val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++
++      st_info(ST_ISP, "iti left: %d, top: %d, width = %d, height = %d, bpp = %d\n",
++              crop->left, crop->top, crop->width, crop->height, bpp);
++
++      isp_format_reg_list[14].addr = ISP_REG_SENSOR;
++      isp_format_reg_list[14].val = 0x00000000;
++      if (type == DVP_SENSOR) {
++              unsigned int flags = dvp_dev->dvp->flags;
++
++              if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
++                      isp_format_reg_list[14].val |= 0x08;
++              if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
++                      isp_format_reg_list[14].val |= 0x04;
++      } else {
++              isp_format_reg_list[14].val |= 0x01;
++      }
++
++      isp_load_regs(ispbase, isp_format_settings);
++      return 0;
++}
++
++static int stf_isp_stream_set(struct stf_isp_dev *isp_dev, int on)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++
++      void __iomem *ispbase;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      if (on) {
++#if defined(USE_NEW_CONFIG_SETTING)
++              isp_load_regs(ispbase, isp_reg_start_settings);
++#else
++              reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, 0x3FFFF, 0x3000a);
++              reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, BIT(1) | BIT(0), 0x3);
++              reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(0), 1);
++#endif //#if defined(USE_NEW_CONFIG_SETTING)
++      } else {
++              /* NOTE: Clear bit 0 of ISP_REG_ISP_CTRL_0 here will get crash. */
++              stf_isp_save_ctx_regs(isp_dev);
++      }
++
++      return 0;
++}
++
++static union reg_buf reg_buf;
++static int stf_isp_reg_read(struct stf_isp_dev *isp_dev, void *arg)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++      struct isp_reg_param *reg_param = arg;
++      u32 size;
++      unsigned long r;
++
++      if (reg_param->reg_buf == NULL) {
++              st_err(ST_ISP, "Failed to access register. The pointer is NULL!!!\n");
++              return -EINVAL;
++      }
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      size = 0;
++      switch (reg_param->reg_info.method) {
++      case STF_ISP_REG_METHOD_ONE_REG:
++              break;
++
++      case STF_ISP_REG_METHOD_SERIES:
++              if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_BUF_SIZE);
++                      return -EINVAL;
++              }
++              break;
++
++      case STF_ISP_REG_METHOD_MODULE:
++              /* This mode is not supported in the V4L2 version. */
++              st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
++                      STF_ISP_REG_METHOD_MODULE is not supported!!!\n");
++              return -ENOTTY;
++
++      case STF_ISP_REG_METHOD_TABLE:
++              if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_TBL_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 2;
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_2:
++              if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_TBL_2_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 3;
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_3:
++              if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_TBL_3_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 4;
++              break;
++
++      case STF_ISP_REG_METHOD_SMPL_PACK:
++              st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
++                      STF_ISP_REG_METHOD_SMPL_PACK is not supported!!!\n");
++              return -ENOTTY;
++
++      case STF_ISP_REG_METHOD_SOFT_RDMA:
++              // This mode is not supported in the V4L2 version.
++              st_err(ST_ISP, "Reg Read - Failed to access register. The method = \
++                      STF_ISP_REG_METHOD_SOFT_RDMA is not supported!!!\n");
++              return -ENOTTY;
++
++      default:
++              st_err(ST_ISP, "Failed to access register. The method=%d \
++                      is not supported!!!\n", reg_param->reg_info.method);
++              return -ENOTTY;
++      }
++
++      memset(&reg_buf, 0, sizeof(union reg_buf));
++      if (size) {
++              r = copy_from_user((u8 *)reg_buf.buffer,
++                      (u8 *)reg_param->reg_buf->buffer, size);
++              if (r) {
++                      st_err(ST_ISP, "Failed to call copy_from_user for the \
++                              reg_param->reg_buf value\n");
++                      return -EIO;
++              }
++      }
++
++      size = 0;
++      switch (reg_param->reg_info.method) {
++      case STF_ISP_REG_METHOD_ONE_REG:
++              reg_buf.buffer[0] = reg_read(ispbase, reg_param->reg_info.offset);
++              size = sizeof(u32);
++              break;
++
++      case STF_ISP_REG_METHOD_SERIES:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      reg_buf.buffer[r] = reg_read(ispbase,
++                              reg_param->reg_info.offset + (r * 4));
++              }
++              size = sizeof(u32) * reg_param->reg_info.length;
++              break;
++
++      case STF_ISP_REG_METHOD_MODULE:
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      reg_buf.reg_tbl[r].value = reg_read(ispbase,
++                              reg_buf.reg_tbl[r].offset);
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 2;
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_2:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      if (reg_buf.reg_tbl2[r].mask) {
++                              reg_buf.reg_tbl2[r].value = (reg_read(ispbase,
++                                      reg_buf.reg_tbl2[r].offset)
++                                              & reg_buf.reg_tbl2[r].mask);
++                      } else {
++                              reg_buf.reg_tbl2[r].value = reg_read(ispbase,
++                                      reg_buf.reg_tbl2[r].offset);
++                      }
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 3;
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_3:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      if (reg_buf.reg_tbl3[r].mask) {
++                              reg_buf.reg_tbl3[r].value = (reg_read(ispbase,
++                                      reg_buf.reg_tbl3[r].offset)
++                                              & reg_buf.reg_tbl3[r].mask);
++                      } else {
++                              reg_buf.reg_tbl3[r].value = reg_read(ispbase,
++                                      reg_buf.reg_tbl3[r].offset);
++                      }
++                      if (reg_buf.reg_tbl3[r].delay_ms) {
++                              usleep_range(1000 * reg_buf.reg_tbl3[r].delay_ms,
++                                      1000 * reg_buf.reg_tbl3[r].delay_ms + 100);
++                      }
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 4;
++              break;
++
++      case STF_ISP_REG_METHOD_SMPL_PACK:
++              break;
++
++      case STF_ISP_REG_METHOD_SOFT_RDMA:
++              break;
++
++      default:
++              break;
++      }
++
++      r = copy_to_user((u8 *)reg_param->reg_buf->buffer, (u8 *)reg_buf.buffer,
++              size);
++      if (r) {
++              st_err(ST_ISP, "Failed to call copy_to_user for the \
++                      reg_param->buffer value\n");
++              return -EIO;
++      }
++
++      return 0;
++}
++
++static int stf_isp_soft_rdma(struct stf_isp_dev *isp_dev, u32 rdma_addr)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++      struct isp_rdma_info *rdma_info = NULL;
++      s32 len;
++      u32 offset;
++      int ret = 0;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      rdma_info = phys_to_virt(rdma_addr);
++      while (1) {
++              if (rdma_info->tag == RDMA_WR_ONE) {
++                      reg_write(ispbase, rdma_info->offset, rdma_info->param);
++                      rdma_info++;
++              } else if (rdma_info->tag == RDMA_WR_SRL) {
++                      offset = rdma_info->offset;
++                      len = rdma_info->param;
++                      rdma_info++;
++                      while (len > 0) {
++                              reg_write(ispbase, offset, rdma_info->param);
++                              offset += 4;
++                              len--;
++                              if (len > 0) {
++                                      reg_write(ispbase, offset, rdma_info->value);
++                                      len--;
++                              }
++                              offset += 4;
++                              rdma_info++;
++                      }
++              } else if (rdma_info->tag == RDMA_LINK) {
++                      rdma_info = phys_to_virt(rdma_info->param);
++              } else if (rdma_info->tag == RDMA_SINT) {
++                      /* Software not support this command. */
++                      rdma_info++;
++              } else if (rdma_info->tag == RDMA_END) {
++                      break;
++              } else
++                      rdma_info++;
++      }
++
++      return ret;
++}
++
++static int stf_isp_reg_write(struct stf_isp_dev *isp_dev, void *arg)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++      struct isp_reg_param *reg_param = arg;
++      struct isp_rdma_info *rdma_info = NULL;
++      s32 len;
++      u32 offset;
++      u32 size;
++      unsigned long r;
++      int ret = 0;
++
++      if ((reg_param->reg_buf == NULL)
++              && (reg_param->reg_info.method != STF_ISP_REG_METHOD_SOFT_RDMA)) {
++              st_err(ST_ISP, "Failed to access register. \
++                      The register buffer pointer is NULL!!!\n");
++              return -EINVAL;
++      }
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      size = 0;
++      switch (reg_param->reg_info.method) {
++      case STF_ISP_REG_METHOD_ONE_REG:
++              size = sizeof(u32);
++              break;
++
++      case STF_ISP_REG_METHOD_SERIES:
++              if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length;
++              break;
++
++      case STF_ISP_REG_METHOD_MODULE:
++              // This mode is not supported in the V4L2 version.
++              st_err(ST_ISP, "Reg Write - Failed to access register. \
++                      The method = STF_ISP_REG_METHOD_MODULE is not supported!!!\n");
++              return -ENOTTY;
++
++      case STF_ISP_REG_METHOD_TABLE:
++              if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_TBL_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 2;
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_2:
++              if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_TBL_2_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 3;
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_3:
++              if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_TBL_3_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 4;
++              break;
++
++      case STF_ISP_REG_METHOD_SMPL_PACK:
++              if (reg_param->reg_info.length > STF_ISP_REG_SMPL_PACK_BUF_SIZE) {
++                      st_err(ST_ISP, "Failed to access register. \
++                              The (length=0x%08X > 0x%08X) is out of size!!!\n",
++                              reg_param->reg_info.length, STF_ISP_REG_SMPL_PACK_BUF_SIZE);
++                      return -EINVAL;
++              }
++              size = sizeof(u32) * reg_param->reg_info.length * 2;
++              break;
++
++      case STF_ISP_REG_METHOD_SOFT_RDMA:
++              break;
++
++      default:
++              st_err(ST_ISP, "Failed to access register. The method=%d \
++                      is not supported!!!\n", reg_param->reg_info.method);
++              return -ENOTTY;
++      }
++
++      memset(&reg_buf, 0, sizeof(union reg_buf));
++      if (size) {
++              r = copy_from_user((u8 *)reg_buf.buffer,
++                      (u8 *)reg_param->reg_buf->buffer, size);
++              if (r) {
++                      st_err(ST_ISP, "Failed to call copy_from_user for the \
++                              reg_param->reg_buf value\n");
++                      return -EIO;
++              }
++      }
++
++      switch (reg_param->reg_info.method) {
++      case STF_ISP_REG_METHOD_ONE_REG:
++              reg_write(ispbase, reg_param->reg_info.offset, reg_buf.buffer[0]);
++              break;
++
++      case STF_ISP_REG_METHOD_SERIES:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      reg_write(ispbase, reg_param->reg_info.offset + (r * 4),
++                              reg_buf.buffer[r]);
++              }
++              break;
++
++      case STF_ISP_REG_METHOD_MODULE:
++              /* This mode is not supported in the V4L2 version. */
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      reg_write(ispbase, reg_buf.reg_tbl[r].offset,
++                              reg_buf.reg_tbl[r].value);
++              }
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_2:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      if (reg_buf.reg_tbl2[r].mask) {
++                              reg_set_bit(ispbase, reg_buf.reg_tbl2[r].offset,
++                                      reg_buf.reg_tbl2[r].mask, reg_buf.reg_tbl2[r].value);
++                      } else {
++                              reg_write(ispbase, reg_buf.reg_tbl2[r].offset,
++                                      reg_buf.reg_tbl2[r].value);
++                      }
++              }
++              break;
++
++      case STF_ISP_REG_METHOD_TABLE_3:
++              for (r = 0; r < reg_param->reg_info.length; r++) {
++                      if (reg_buf.reg_tbl3[r].mask) {
++                              reg_set_bit(ispbase, reg_buf.reg_tbl3[r].offset,
++                                      reg_buf.reg_tbl3[r].mask, reg_buf.reg_tbl3[r].value);
++                      } else {
++                              reg_write(ispbase, reg_buf.reg_tbl3[r].offset,
++                                      reg_buf.reg_tbl3[r].value);
++                      }
++                      if (reg_buf.reg_tbl3[r].delay_ms) {
++                              usleep_range(1000 * reg_buf.reg_tbl3[r].delay_ms,
++                                      1000 * reg_buf.reg_tbl3[r].delay_ms + 100);
++                      }
++              }
++              break;
++
++      case STF_ISP_REG_METHOD_SMPL_PACK:
++              size = reg_param->reg_info.length;
++              rdma_info = &reg_buf.rdma_cmd[0];
++              while (size) {
++                      if (rdma_info->tag == RDMA_WR_ONE) {
++                              reg_write(ispbase, rdma_info->offset, rdma_info->param);
++                              rdma_info++;
++                              size--;
++                      } else if (rdma_info->tag == RDMA_WR_SRL) {
++                              offset = rdma_info->offset;
++                              len = rdma_info->param;
++                              rdma_info++;
++                              size--;
++                              while (size && (len > 0)) {
++                                      reg_write(ispbase, offset, rdma_info->param);
++                                      offset += 4;
++                                      len--;
++                                      if (len > 0) {
++                                              reg_write(ispbase, offset, rdma_info->value);
++                                              len--;
++                                      }
++                                      offset += 4;
++                                      rdma_info++;
++                                      size--;
++                              }
++                      } else if (rdma_info->tag == RDMA_END) {
++                              break;
++                      } else {
++                              rdma_info++;
++                              size--;
++                      }
++              }
++              break;
++
++      case STF_ISP_REG_METHOD_SOFT_RDMA:
++              /*
++               * Simulation the hardware RDMA behavior to debug and verify
++               * the RDMA chain.
++               */
++              ret = stf_isp_soft_rdma(isp_dev, reg_param->reg_info.offset);
++              break;
++
++      default:
++              break;
++      }
++
++      return ret;
++}
++
++static int stf_isp_shadow_trigger(struct stf_isp_dev *isp_dev)
++{
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase;
++
++      ispbase = stf_isp_get_ispbase(vin);
++
++      // shadow update
++      reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, (BIT(17) | BIT(16)), 0x30000);
++      reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, (BIT(1) | BIT(0)), 0x3);
++      return 0;
++}
++
++void dump_isp_reg(void *__iomem ispbase)
++{
++      int j;
++      u32 addr, val;
++
++      st_debug(ST_ISP, "DUMP ISP register:\n -- isp_reg_init_settings --\n");
++      for (j = 0; j < isp_reg_init_settings->regval_num; j++) {
++              addr = isp_reg_init_settings->regval[j].addr;
++              val = ioread32(ispbase + addr);
++              st_debug(ST_ISP, "{0x%08x, 0x%08x}\n", addr, val);
++      }
++
++      st_debug(ST_ISP, " --- isp_format_settings ---\n");
++      for (j = 0; j < isp_format_settings->regval_num; j++) {
++              addr = isp_format_settings->regval[j].addr;
++              val = ioread32(ispbase + addr);
++              st_debug(ST_ISP, "{0x%08x, 0x%08x}\n", addr, val);
++      }
++
++      val = ioread32(ispbase + ISP_REG_Y_PLANE_START_ADDR);
++      st_debug(ST_ISP, "-- ISP_REG_Y_PLANE_START_ADDR --\n {0x%08x, 0x%08x}\n",
++               ISP_REG_Y_PLANE_START_ADDR, val);
++      val = ioread32(ispbase + ISP_REG_UV_PLANE_START_ADDR);
++      st_debug(ST_ISP, "-- ISP_REG_UV_PLANE_START_ADDR --\n {0x%08x, 0x%08x}\n",
++               ISP_REG_UV_PLANE_START_ADDR, val);
++      val = ioread32(ispbase + ISP_REG_DUMP_CFG_0);
++      st_debug(ST_ISP, "-- ISP_REG_DUMP_CFG_0 --\n {0x%08x, 0x%08x}\n",
++               ISP_REG_DUMP_CFG_0, val);
++      val = ioread32(ispbase + ISP_REG_DUMP_CFG_1);
++      st_debug(ST_ISP, " --- ISP_REG_DUMP_CFG_1 ---\n {0x%08x, 0x%08x}\n",
++               ISP_REG_DUMP_CFG_1, val);
++
++      st_debug(ST_ISP, " --- isp_reg_start_settings ---\n");
++      for (j = 0; j < isp_reg_start_settings->regval_num; j++) {
++              addr = isp_reg_start_settings->regval[j].addr;
++              val = ioread32(ispbase + addr);
++              st_debug(ST_ISP, "{0x%08x, 0x%08x}\n", addr, val);
++      }
++}
++
++struct isp_hw_ops isp_ops = {
++      .isp_clk_enable        = stf_isp_clk_enable,
++      .isp_clk_disable       = stf_isp_clk_disable,
++      .isp_reset             = stf_isp_reset,
++      .isp_config_set        = stf_isp_config_set,
++      .isp_set_format        = stf_isp_set_format,
++      .isp_stream_set        = stf_isp_stream_set,
++      .isp_reg_read          = stf_isp_reg_read,
++      .isp_reg_write         = stf_isp_reg_write,
++      .isp_shadow_trigger    = stf_isp_shadow_trigger,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h
+@@ -0,0 +1,133 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_ISP_IOCTL_H
++#define STF_ISP_IOCTL_H
++
++
++#include <media/v4l2-ctrls.h>
++
++
++#define FILENAME_MAX_LEN     30
++
++#define ISP_IOC                         ('V')
++#define STF_ISP_REG_BUF_SIZE            (768)
++#define STF_ISP_REG_TBL_BUF_SIZE        (STF_ISP_REG_BUF_SIZE / 2)
++#define STF_ISP_REG_TBL_2_BUF_SIZE      (STF_ISP_REG_BUF_SIZE / 3)
++#define STF_ISP_REG_TBL_3_BUF_SIZE      (STF_ISP_REG_BUF_SIZE / 4)
++#define STF_ISP_REG_SMPL_PACK_BUF_SIZE  (STF_ISP_REG_BUF_SIZE / 2)
++#define RDMA_WR_ONE                     (0xA0)
++#define RDMA_WR_SRL                     (0xA1)
++#define RDMA_LINK                       (0xA2)
++#define RDMA_SINT                       (0xA3)
++#define RDMA_END                        (0xAF)
++#define ENABLE_SS0_SS1
++
++enum _STF_ISP_IOCTL {
++      STF_ISP_IOCTL_LOAD_FW = BASE_VIDIOC_PRIVATE + 1,
++      STF_ISP_IOCTL_DMABUF_ALLOC,
++      STF_ISP_IOCTL_DMABUF_FREE,
++      STF_ISP_IOCTL_GET_HW_VER,
++      STF_ISP_IOCTL_REG,
++      STF_ISP_IOCTL_SHADOW_LOCK,
++      STF_ISP_IOCTL_SHADOW_UNLOCK,
++      STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER,
++      STF_ISP_IOCTL_SET_USER_CONFIG_ISP,
++      STF_ISP_IOCTL_MAX
++};
++
++enum _STF_ISP_REG_METHOD {
++      STF_ISP_REG_METHOD_ONE_REG = 0,
++      STF_ISP_REG_METHOD_SERIES,
++      STF_ISP_REG_METHOD_MODULE,
++      STF_ISP_REG_METHOD_TABLE,
++      STF_ISP_REG_METHOD_TABLE_2,
++      STF_ISP_REG_METHOD_TABLE_3,
++      STF_ISP_REG_METHOD_SMPL_PACK,
++      STF_ISP_REG_METHOD_SOFT_RDMA,
++      STF_ISP_REG_METHOD_MAX
++};
++
++
++struct stfisp_fw_info {
++      char __user filename[FILENAME_MAX_LEN];
++};
++
++struct dmabuf_create {
++      __u32 fd;
++      __u32 size;
++      __u32 paddr;
++};
++
++struct isp_rdma_info {
++      u32 param;
++      union {
++              u32 value;
++              struct {
++                      u32 offset  : 24;
++                      u32 tag     : 8;
++              };
++      };
++};
++
++struct isp_reg_info {
++      /** @brief [in] access method of register */
++      u8 method;
++      /** @brief [in] offset indicated which register will be read/write */
++      u32 offset;
++      /** @brief [in] length for indicated how much register will be read/write */
++      u32 length;
++};
++
++union reg_buf {
++      u32 buffer[STF_ISP_REG_BUF_SIZE];
++      struct {
++              u32 offset;
++              u32 value;
++      } reg_tbl[STF_ISP_REG_TBL_BUF_SIZE];
++      struct {
++              u32 offset;
++              u32 value;
++              u32 mask;
++      } reg_tbl2[STF_ISP_REG_TBL_2_BUF_SIZE];
++      struct {
++              u32 offset;
++              u32 value;
++              u32 mask;
++              u32 delay_ms;
++      } reg_tbl3[STF_ISP_REG_TBL_3_BUF_SIZE];
++      struct isp_rdma_info rdma_cmd[STF_ISP_REG_SMPL_PACK_BUF_SIZE];
++};
++
++struct isp_reg_param {
++      /** @brief [in, out] register read/write information */
++      struct isp_reg_info reg_info;
++      /** @brief [in, out] buffer */
++      union reg_buf *reg_buf;
++};
++
++
++#define VIDIOC_STFISP_LOAD_FW \
++      _IOW(ISP_IOC, STF_ISP_IOCTL_LOAD_FW, struct stfisp_fw_info)
++#define VIDIOC_STF_DMABUF_ALLOC \
++      _IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_ALLOC, struct dmabuf_create)
++#define VIDIOC_STF_DMABUF_FREE \
++      _IOWR(ISP_IOC, STF_ISP_IOCTL_DMABUF_FREE, struct dmabuf_create)
++#define VIDIOC_STFISP_GET_REG \
++      _IOWR(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
++#define VIDIOC_STFISP_SET_REG \
++      _IOW(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param)
++#define VIDIOC_STFISP_SHADOW_LOCK \
++      _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_LOCK)
++#define VIDIOC_STFISP_SHADOW_UNLOCK \
++      _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK)
++#define VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER \
++      _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER)
++#define VIDIOC_STFISP_SET_USER_CONFIG_ISP \
++      _IO(ISP_IOC, STF_ISP_IOCTL_SET_USER_CONFIG_ISP)
++
++
++#endif /* STF_ISP_IOCTL_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+@@ -0,0 +1,1552 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include "stf_video.h"
++#include <media/media-entity.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-mc.h>
++#include <media/videobuf2-dma-sg.h>
++#include <media/videobuf2-vmalloc.h>
++#include <media/videobuf2-dma-contig.h>
++
++static const struct stfcamss_format_info formats_pix_st7110_wr[] = {
++      { MEDIA_BUS_FMT_AYUV8_1X32, V4L2_PIX_FMT_AYUV32, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 32 } },
++      { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 16 } },
++      { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_PIX_FMT_RGB565, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 16 } },
++      { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++      { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++      { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++      { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 8 } },
++      { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++};
++
++static const struct stfcamss_format_info formats_raw_st7110_isp[] = {
++      { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++      { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++      { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++      { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++};
++
++static const struct stfcamss_format_info formats_pix_st7110_isp[] = {
++      // { MEDIA_BUS_FMT_YUYV12_2X12, V4L2_PIX_FMT_NV12M, 2,
++      //  { { 1, 1 }, { 1, 1 } }, { { 1, 1 }, { 1, 1 } }, { 8 , 4 } },
++      { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV12, 1,
++        { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++      { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV21, 1,
++        { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++};
++
++static const struct stfcamss_format_info formats_st7110_isp_iti[] = {
++      //  raw format
++      { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 10 } },
++      { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++      { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++      { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++      { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 1,
++        { { 1, 1 } }, { { 1, 1 } }, { 12 } },
++
++      // YUV420
++      { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV12, 1,
++        { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++      { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV21, 1,
++        { { 1, 1 } }, { { 2, 3 } }, { 8 } },
++
++      // YUV444
++      { MEDIA_BUS_FMT_YUV8_1X24, V4L2_PIX_FMT_NV24, 1,
++        { { 1, 1 } }, { { 1, 3 } }, { 8 } },
++      { MEDIA_BUS_FMT_VUY8_1X24, V4L2_PIX_FMT_NV42, 1,
++        { { 1, 1 } }, { { 1, 3 } }, { 8 } },
++};
++
++static int video_find_format(u32 code, u32 pixelformat,
++                              const struct stfcamss_format_info *formats,
++                              unsigned int nformats)
++{
++      int i;
++
++      for (i = 0; i < nformats; i++) {
++              if (formats[i].code == code &&
++                      formats[i].pixelformat == pixelformat)
++                      return i;
++      }
++
++      for (i = 0; i < nformats; i++)
++              if (formats[i].code == code)
++                      return i;
++
++      for (i = 0; i < nformats; i++)
++              if (formats[i].pixelformat == pixelformat)
++                      return i;
++
++      return -EINVAL;
++}
++
++static int __video_try_fmt(struct stfcamss_video *video,
++              struct v4l2_format *f, int is_mp)
++{
++      struct v4l2_pix_format *pix;
++      struct v4l2_pix_format_mplane *pix_mp;
++      const struct stfcamss_format_info *fi;
++      u32 width, height;
++      u32 bpl;
++      int i, j;
++
++      st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++      pix = &f->fmt.pix;
++      pix_mp = &f->fmt.pix_mp;
++
++      if (is_mp) {
++              for (i = 0; i < video->nformats; i++)
++                      if (pix_mp->pixelformat
++                              == video->formats[i].pixelformat)
++                              break;
++
++              if (i == video->nformats)
++                      i = 0; /* default format */
++
++              fi = &video->formats[i];
++              width = pix_mp->width;
++              height = pix_mp->height;
++
++              memset(pix_mp, 0, sizeof(*pix_mp));
++
++              pix_mp->pixelformat = fi->pixelformat;
++              pix_mp->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              pix_mp->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++              pix_mp->num_planes = fi->planes;
++              for (j = 0; j < pix_mp->num_planes; j++) {
++                      bpl = pix_mp->width / fi->hsub[j].numerator *
++                              fi->hsub[j].denominator * fi->bpp[j] / 8;
++                      bpl = ALIGN(bpl, video->bpl_alignment);
++                      pix_mp->plane_fmt[j].bytesperline = bpl;
++                      pix_mp->plane_fmt[j].sizeimage = pix_mp->height /
++                              fi->vsub[j].numerator
++                              * fi->vsub[j].denominator * bpl;
++              }
++
++              pix_mp->field = V4L2_FIELD_NONE;
++              pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
++              pix_mp->flags = 0;
++              pix_mp->ycbcr_enc =
++                      V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
++              pix_mp->quantization =
++                      V4L2_MAP_QUANTIZATION_DEFAULT(true,
++                              pix_mp->colorspace, pix_mp->ycbcr_enc);
++              pix_mp->xfer_func =
++                      V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
++
++              st_info(ST_VIDEO, "w, h = %d, %d, bpp = %d\n", pix_mp->width,
++                              pix_mp->height, fi->bpp[0]);
++              st_info(ST_VIDEO, "i = %d, p = %d, s = 0x%x\n", i,
++                              pix_mp->num_planes, pix_mp->plane_fmt[0].sizeimage);
++
++      } else {
++              for (i = 0; i < video->nformats; i++)
++                      if (pix->pixelformat == video->formats[i].pixelformat)
++                              break;
++
++              if (i == video->nformats)
++                      i = 0; /* default format */
++
++              fi = &video->formats[i];
++              width = pix->width;
++              height = pix->height;
++
++              memset(pix, 0, sizeof(*pix));
++
++              pix->pixelformat = fi->pixelformat;
++              pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH,
++                              STFCAMSS_FRAME_MAX_WIDTH);
++              pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT,
++                              STFCAMSS_FRAME_MAX_HEIGHT);
++              bpl = pix->width / fi->hsub[0].numerator *
++                      fi->hsub[0].denominator * fi->bpp[0] / 8;
++              bpl = ALIGN(bpl, video->bpl_alignment);
++              pix->bytesperline = bpl;
++              pix->sizeimage = pix->height /
++                      fi->vsub[0].numerator
++                      * fi->vsub[0].denominator * bpl;
++
++              pix->field = V4L2_FIELD_NONE;
++              pix->colorspace = V4L2_COLORSPACE_SRGB;
++              pix->flags = 0;
++              pix->ycbcr_enc =
++                      V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
++              pix->quantization =
++                      V4L2_MAP_QUANTIZATION_DEFAULT(true,
++                              pix->colorspace, pix->ycbcr_enc);
++              pix->xfer_func =
++                      V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
++
++              st_info(ST_VIDEO, "w, h = %d, %d, bpp = %d\n", pix->width,
++                              pix->height, fi->bpp[0]);
++              st_info(ST_VIDEO, "i = %d, s = 0x%x\n", i, pix->sizeimage);
++      }
++      return 0;
++}
++
++static int stf_video_init_format(struct stfcamss_video *video, int is_mp)
++{
++      int ret;
++      struct v4l2_format format = {
++              .type = video->type,
++              .fmt.pix = {
++                      .width = 1920,
++                      .height = 1080,
++                      .pixelformat = V4L2_PIX_FMT_RGB565,
++              },
++      };
++
++      ret = __video_try_fmt(video, &format, is_mp);
++
++      if (ret < 0)
++              return ret;
++
++      video->active_fmt = format;
++
++      return 0;
++}
++
++static int video_queue_setup(struct vb2_queue *q,
++      unsigned int *num_buffers, unsigned int *num_planes,
++      unsigned int sizes[], struct device *alloc_devs[])
++{
++      struct stfcamss_video *video = vb2_get_drv_priv(q);
++      const struct v4l2_pix_format *format =
++                      &video->active_fmt.fmt.pix;
++      const struct v4l2_pix_format_mplane *format_mp =
++                      &video->active_fmt.fmt.pix_mp;
++      unsigned int i;
++
++      st_debug(ST_VIDEO, "%s, planes = %d\n", __func__, *num_planes);
++
++      if (video->is_mp) {
++              if (*num_planes) {
++                      if (*num_planes != format_mp->num_planes)
++                              return -EINVAL;
++
++                      for (i = 0; i < *num_planes; i++)
++                              if (sizes[i] <
++                                      format_mp->plane_fmt[i].sizeimage)
++                                      return -EINVAL;
++
++                      return 0;
++              }
++
++              *num_planes = format_mp->num_planes;
++
++              for (i = 0; i < *num_planes; i++)
++                      sizes[i] = format_mp->plane_fmt[i].sizeimage;
++      } else {
++              if (*num_planes) {
++                      if (*num_planes != 1)
++                              return -EINVAL;
++
++                      if (sizes[0] < format->sizeimage)
++                              return -EINVAL;
++              }
++
++              *num_planes  = 1;
++              sizes[0] = format->sizeimage;
++              if (!sizes[0])
++                      st_err(ST_VIDEO, "%s: error size is zero!!!\n", __func__);
++      }
++      if ((stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC)
++              == STF_ISP_PAD_SRC_SCD_Y) &&
++              sizes[0] < ISP_SCD_Y_BUFFER_SIZE) {
++              sizes[0] = ISP_SCD_Y_BUFFER_SIZE;
++      }
++
++      st_info(ST_VIDEO, "%s, planes = %d, size = %d\n",
++                      __func__, *num_planes, sizes[0]);
++      return 0;
++}
++
++static int video_buf_init(struct vb2_buffer *vb)
++{
++      struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++      struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
++      struct stfcamss_buffer *buffer =
++              container_of(vbuf, struct stfcamss_buffer, vb);
++      const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
++      const struct v4l2_pix_format_mplane *fmt_mp =
++                              &video->active_fmt.fmt.pix_mp;
++      //struct sg_table *sgt;
++      dma_addr_t *paddr;
++      unsigned int i;
++
++      buffer->sizeimage = 0;
++
++      if (video->is_mp) {
++              for (i = 0; i < fmt_mp->num_planes; i++) {
++                      paddr = vb2_plane_cookie(vb, i);
++                      buffer->addr[i] = *paddr;
++              buffer->sizeimage += vb2_plane_size(vb, i);
++              }
++
++              if (fmt_mp->num_planes == 1
++                      && (fmt_mp->pixelformat == V4L2_PIX_FMT_NV12
++                      || fmt_mp->pixelformat == V4L2_PIX_FMT_NV21
++                      || fmt_mp->pixelformat == V4L2_PIX_FMT_NV16
++                      || fmt_mp->pixelformat == V4L2_PIX_FMT_NV61))
++                      buffer->addr[1] = buffer->addr[0] +
++                                      fmt_mp->plane_fmt[0].bytesperline *
++                                      fmt_mp->height;
++      } else {
++              paddr = vb2_plane_cookie(vb, 0);
++              buffer->sizeimage = vb2_plane_size(vb, 0);
++              buffer->addr[0] = *paddr;
++              if (fmt->pixelformat == V4L2_PIX_FMT_NV12
++                      || fmt->pixelformat == V4L2_PIX_FMT_NV21
++                      || fmt->pixelformat == V4L2_PIX_FMT_NV16
++                      || fmt->pixelformat == V4L2_PIX_FMT_NV61)
++                      buffer->addr[1] = buffer->addr[0] +
++                              fmt->bytesperline *
++                              fmt->height;
++      }
++
++      if (stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC)
++              == STF_ISP_PAD_SRC_SCD_Y) {
++              buffer->addr[1] = buffer->addr[0] + ISP_YHIST_BUFFER_SIZE;
++              buffer->vaddr_sc = vb2_plane_vaddr(vb, 0);
++      }
++
++      return 0;
++}
++
++static int video_buf_prepare(struct vb2_buffer *vb)
++{
++      struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++      struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
++      const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix;
++      const struct v4l2_pix_format_mplane *fmt_mp =
++                                      &video->active_fmt.fmt.pix_mp;
++      unsigned int i;
++
++      if (video->is_mp) {
++              for (i = 0; i < fmt_mp->num_planes; i++) {
++                      if (fmt_mp->plane_fmt[i].sizeimage
++                                      > vb2_plane_size(vb, i))
++                              return -EINVAL;
++
++                      vb2_set_plane_payload(vb, i,
++                                      fmt_mp->plane_fmt[i].sizeimage);
++              }
++      } else {
++              if (fmt->sizeimage > vb2_plane_size(vb, 0)) {
++                      st_err(ST_VIDEO, "sizeimage = %d, plane size = %d\n",
++                              fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0));
++                      return -EINVAL;
++              }
++              vb2_set_plane_payload(vb, 0, fmt->sizeimage);
++      }
++
++      vbuf->field = V4L2_FIELD_NONE;
++
++      return 0;
++}
++
++static void video_buf_queue(struct vb2_buffer *vb)
++{
++      struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++      struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue);
++      struct stfcamss_buffer *buffer =
++              container_of(vbuf, struct stfcamss_buffer, vb);
++
++      video->ops->queue_buffer(video, buffer);
++}
++
++static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
++                              struct v4l2_pix_format_mplane *pix,
++                              const struct stfcamss_format_info *f,
++                              unsigned int alignment)
++{
++      unsigned int i;
++      u32 bytesperline;
++
++      memset(pix, 0, sizeof(*pix));
++      v4l2_fill_pix_format_mplane(pix, mbus);
++      pix->pixelformat = f->pixelformat;
++      pix->num_planes = f->planes;
++      for (i = 0; i < pix->num_planes; i++) {
++              bytesperline = pix->width / f->hsub[i].numerator *
++                      f->hsub[i].denominator * f->bpp[i] / 8;
++              bytesperline = ALIGN(bytesperline, alignment);
++              pix->plane_fmt[i].bytesperline = bytesperline;
++              pix->plane_fmt[i].sizeimage = pix->height /
++                              f->vsub[i].numerator * f->vsub[i].denominator *
++                              bytesperline;
++      }
++
++      return 0;
++}
++
++static int video_mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
++                      struct v4l2_pix_format *pix,
++                      const struct stfcamss_format_info *f,
++                      unsigned int alignment)
++{
++      u32 bytesperline;
++
++      memset(pix, 0, sizeof(*pix));
++      v4l2_fill_pix_format(pix, mbus);
++      pix->pixelformat = f->pixelformat;
++      bytesperline = pix->width / f->hsub[0].numerator *
++              f->hsub[0].denominator * f->bpp[0] / 8;
++      bytesperline = ALIGN(bytesperline, alignment);
++      pix->bytesperline = bytesperline;
++      pix->sizeimage = pix->height /
++                      f->vsub[0].numerator * f->vsub[0].denominator *
++                      bytesperline;
++      return 0;
++}
++
++static struct v4l2_subdev *video_remote_subdev(
++              struct stfcamss_video *video, u32 *pad)
++{
++      struct media_pad *remote;
++
++      remote = media_pad_remote_pad_first(&video->pad);
++
++      if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
++              return NULL;
++
++      if (pad)
++              *pad = remote->index;
++
++      return media_entity_to_v4l2_subdev(remote->entity);
++}
++
++static int video_get_subdev_format(struct stfcamss_video *video,
++              struct v4l2_format *format)
++{
++      struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
++      struct v4l2_pix_format_mplane *pix_mp =
++                              &video->active_fmt.fmt.pix_mp;
++      struct v4l2_subdev_format fmt;
++      struct v4l2_subdev *subdev;
++      u32 pixelformat;
++      u32 pad;
++      int ret;
++
++      subdev = video_remote_subdev(video, &pad);
++      if (subdev == NULL)
++              return -EPIPE;
++
++      fmt.pad = pad;
++      fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++
++      ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
++      if (ret)
++              return ret;
++
++      if (video->is_mp)
++              pixelformat = pix_mp->pixelformat;
++      else
++              pixelformat = pix->pixelformat;
++      ret = video_find_format(fmt.format.code, pixelformat,
++                              video->formats, video->nformats);
++      if (ret < 0)
++              return ret;
++
++      format->type = video->type;
++
++      if (video->is_mp)
++              return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp,
++                              &video->formats[ret], video->bpl_alignment);
++      else
++              return video_mbus_to_pix(&fmt.format, &format->fmt.pix,
++                              &video->formats[ret], video->bpl_alignment);
++}
++
++static int video_check_format(struct stfcamss_video *video)
++{
++      struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix;
++      struct v4l2_pix_format_mplane *pix_mp =
++                              &video->active_fmt.fmt.pix_mp;
++      struct v4l2_format format;
++      struct v4l2_pix_format *sd_pix = &format.fmt.pix;
++      struct v4l2_pix_format_mplane *sd_pix_mp = &format.fmt.pix_mp;
++      int ret;
++
++      if (video->is_mp) {
++              sd_pix_mp->pixelformat = pix_mp->pixelformat;
++              ret = video_get_subdev_format(video, &format);
++              if (ret < 0)
++                      return ret;
++
++              if (pix_mp->pixelformat != sd_pix_mp->pixelformat ||
++                      pix_mp->height > sd_pix_mp->height ||
++                      pix_mp->width > sd_pix_mp->width ||
++                      pix_mp->num_planes != sd_pix_mp->num_planes ||
++                      pix_mp->field != format.fmt.pix_mp.field) {
++                      st_err(ST_VIDEO,
++                              "%s, not match:\n"
++                              "0x%x 0x%x\n0x%x 0x%x\n0x%x 0x%x\n",
++                              __func__,
++                              pix_mp->pixelformat, sd_pix_mp->pixelformat,
++                              pix_mp->height, sd_pix_mp->height,
++                              pix_mp->field, format.fmt.pix_mp.field);
++                      return -EPIPE;
++              }
++
++      } else {
++              sd_pix->pixelformat = pix->pixelformat;
++              ret = video_get_subdev_format(video, &format);
++              if (ret < 0)
++                      return ret;
++
++              if (pix->pixelformat != sd_pix->pixelformat ||
++                      pix->height > sd_pix->height ||
++                      pix->width > sd_pix->width ||
++                      pix->field != format.fmt.pix.field) {
++                      st_err(ST_VIDEO,
++                              "%s, not match:\n"
++                              "0x%x 0x%x\n0x%x 0x%x\n0x%x 0x%x\n",
++                              __func__,
++                              pix->pixelformat, sd_pix->pixelformat,
++                              pix->height, sd_pix->height,
++                              pix->field, format.fmt.pix.field);
++                      return -EPIPE;
++              }
++      }
++      return 0;
++}
++
++static int video_start_streaming(struct vb2_queue *q, unsigned int count)
++{
++      struct stfcamss_video *video = vb2_get_drv_priv(q);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity;
++      struct media_pad *pad;
++      struct v4l2_subdev *subdev;
++      int ret;
++
++      ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe);
++      if (ret < 0) {
++              st_err(ST_VIDEO,
++                      "Failed to video_device_pipeline_start: %d\n", ret);
++              return ret;
++      }
++
++      ret = video_check_format(video);
++      if (ret < 0)
++              goto error;
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              ret = v4l2_subdev_call(subdev, video, s_stream, 1);
++              if (ret < 0 && ret != -ENOIOCTLCMD)
++                      goto error;
++      }
++      return 0;
++
++error:
++      video_device_pipeline_stop(vdev);
++      video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
++      return ret;
++}
++
++static void video_stop_streaming(struct vb2_queue *q)
++{
++      struct stfcamss_video *video = vb2_get_drv_priv(q);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity;
++      struct media_pad *pad;
++      struct v4l2_subdev *subdev;
++
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              v4l2_subdev_call(subdev, video, s_stream, 0);
++      }
++
++      video_device_pipeline_stop(vdev);
++      video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
++}
++
++static const struct vb2_ops stf_video_vb2_q_ops = {
++      .queue_setup     = video_queue_setup,
++      .wait_prepare    = vb2_ops_wait_prepare,
++      .wait_finish     = vb2_ops_wait_finish,
++      .buf_init        = video_buf_init,
++      .buf_prepare     = video_buf_prepare,
++      .buf_queue       = video_buf_queue,
++      .start_streaming = video_start_streaming,
++      .stop_streaming  = video_stop_streaming,
++};
++
++/* -----------------------------------------------------
++ * V4L2 ioctls
++ */
++
++static int getcrop_pad_id(int video_id)
++{
++      return stf_vin_map_isp_pad(video_id, STF_ISP_PAD_SRC);
++}
++
++static int video_querycap(struct file *file, void *fh,
++                      struct v4l2_capability *cap)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++
++      strscpy(cap->driver, "stf camss", sizeof(cap->driver));
++      strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card));
++      snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++              dev_name(video->stfcamss->dev));
++      return 0;
++}
++
++static int video_get_unique_pixelformat_by_index(struct stfcamss_video *video,
++                                              int ndx)
++{
++      int i, j, k;
++
++      /* find index "i" of "k"th unique pixelformat in formats array */
++      k = -1;
++      for (i = 0; i < video->nformats; i++) {
++              for (j = 0; j < i; j++) {
++                      if (video->formats[i].pixelformat ==
++                              video->formats[j].pixelformat)
++                              break;
++              }
++
++              if (j == i)
++                      k++;
++
++              if (k == ndx)
++                      return i;
++      }
++
++      return -EINVAL;
++}
++
++static int video_get_pixelformat_by_mbus_code(struct stfcamss_video *video,
++                                              u32 mcode)
++{
++      int i;
++
++      for (i = 0; i < video->nformats; i++) {
++              if (video->formats[i].code == mcode)
++                      return i;
++      }
++
++      return -EINVAL;
++}
++
++static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      int i;
++
++      st_debug(ST_VIDEO, "%s:\n0x%x 0x%x\n 0x%x, 0x%x\n0x%x\n",
++              __func__,
++              f->type, video->type,
++              f->index, video->nformats,
++              f->mbus_code);
++
++      if (f->type != video->type)
++              return -EINVAL;
++      if (f->index >= video->nformats)
++              return -EINVAL;
++
++      if (f->mbus_code) {
++              /* Each entry in formats[] table has unique mbus_code */
++              if (f->index > 0)
++                      return -EINVAL;
++
++              i = video_get_pixelformat_by_mbus_code(video, f->mbus_code);
++      } else {
++              i = video_get_unique_pixelformat_by_index(video, f->index);
++      }
++
++      if (i < 0)
++              return -EINVAL;
++
++      f->pixelformat = video->formats[i].pixelformat;
++
++      return 0;
++}
++
++static int video_enum_framesizes(struct file *file, void *fh,
++                              struct v4l2_frmsizeenum *fsize)
++{
++      struct v4l2_subdev_frame_size_enum fse = {0};
++      struct v4l2_subdev_mbus_code_enum code = {0};
++      struct stfcamss_video *video = video_drvdata(file);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity = &vdev->entity;
++      struct media_entity *sensor;
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad;
++      bool support_selection = false;
++      int i;
++      int ret;
++
++      for (i = 0; i < video->nformats; i++) {
++              if (video->formats[i].pixelformat == fsize->pixel_format)
++                      break;
++      }
++
++      if (i == video->nformats)
++              return -EINVAL;
++
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              if (subdev->ops->pad->set_selection) {
++                      support_selection = true;
++                      break;
++              }
++      }
++
++      if (support_selection) {
++              if (fsize->index)
++                      return -EINVAL;
++              fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
++              fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH;
++              fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH;
++              fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT;
++              fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT;
++              fsize->stepwise.step_width = 1;
++              fsize->stepwise.step_height = 1;
++      } else {
++              entity = &vdev->entity;
++              sensor = stfcamss_find_sensor(entity);
++              if (!sensor)
++                      return -ENOTTY;
++
++              subdev = media_entity_to_v4l2_subdev(sensor);
++              code.index = 0;
++              code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++              ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code);
++              if (ret < 0)
++                      return -EINVAL;
++              fse.index = fsize->index;
++              fse.code = code.code;
++              fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++              ret = v4l2_subdev_call(subdev, pad, enum_frame_size, NULL, &fse);
++              if (ret < 0)
++                      return -EINVAL;
++              fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++              fsize->discrete.width = fse.min_width;
++              fsize->discrete.height = fse.min_height;
++      }
++
++      return 0;
++}
++
++static int video_enum_frameintervals(struct file *file, void *fh,
++                              struct v4l2_frmivalenum *fival)
++{
++      int ret = 0;
++      struct stfcamss_video *video = video_drvdata(file);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity = &vdev->entity;
++      struct media_entity *sensor;
++      struct v4l2_subdev *subdev;
++      struct v4l2_subdev_mbus_code_enum code = {0};
++      struct v4l2_subdev_frame_interval_enum fie = {0};
++
++      sensor = stfcamss_find_sensor(entity);
++      if (!sensor)
++              return -ENOTTY;
++      fie.index = fival->index;
++      fie.width = fival->width;
++      fie.height = fival->height;
++      fie.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++      subdev = media_entity_to_v4l2_subdev(sensor);
++
++      code.index = 0;
++      code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++
++      /* Don't care about the code, just find by pixelformat */
++      ret = video_find_format(0, fival->pixel_format,
++                              video->formats, video->nformats);
++      if (ret < 0)
++              return -EINVAL;
++
++      ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code);
++      if (ret < 0)
++              return -EINVAL;
++
++      fie.code = code.code;
++      ret = v4l2_subdev_call(subdev, pad, enum_frame_interval, NULL, &fie);
++      if (ret < 0)
++              return ret;
++
++      fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++      fival->discrete = fie.interval;
++
++      return 0;
++}
++
++static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++
++      st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++      st_debug(ST_VIDEO, "%s, active_fmt.type = 0x%x,0x%x\n",
++                      __func__, video->active_fmt.type,
++                      video->active_fmt.fmt.pix.pixelformat);
++      *f = video->active_fmt;
++      return 0;
++}
++
++static int video_g_fmt_mp(struct file *file, void *fh, struct v4l2_format *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++
++      st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++      st_debug(ST_VIDEO, "%s, active_fmt.type = 0x%x\n",
++                      __func__, video->active_fmt.type);
++      *f = video->active_fmt;
++      return 0;
++}
++
++static int video_entity_s_fmt(struct stfcamss_video *video,
++                      struct media_entity *entity,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad;
++      struct v4l2_mbus_framefmt *mf = &fmt->format;
++      struct v4l2_subdev_format fmt_src = {
++              .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++      };
++      u32 width, height, code;
++      int ret, index = 0;
++
++      code = mf->code;
++      width = mf->width;
++      height = mf->height;
++      subdev = media_entity_to_v4l2_subdev(entity);
++      while (1) {
++              if (index >= entity->num_pads)
++                      break;
++              pad = &entity->pads[index];
++              pad = media_pad_remote_pad_first(pad);
++              if (pad && is_media_entity_v4l2_subdev(pad->entity)) {
++                      fmt->pad = index;
++                      ret = v4l2_subdev_call(subdev, pad, set_fmt, state, fmt);
++                      if (mf->code != code ||
++                              mf->width != width || mf->height != height) {
++                              st_warn(ST_VIDEO,
++                                      "\"%s\":%d pad fmt has been"
++                                      " changed to 0x%x %ux%u\n",
++                                      subdev->name, fmt->pad, mf->code,
++                                      mf->width, mf->height);
++                      }
++                      if (index) {
++                              fmt_src.pad = index;
++                              ret = v4l2_subdev_call(subdev, pad, get_fmt, state, &fmt_src);
++                              if (ret)
++                                      return ret;
++
++                              fmt->format.code = fmt_src.format.code;
++                              ret = video_entity_s_fmt(video, pad->entity, state, fmt);
++                      }
++              }
++
++              if (ret < 0 && ret != -ENOIOCTLCMD)
++                      break;
++              index++;
++      }
++      return ret;
++}
++
++static int video_pipeline_s_fmt(struct stfcamss_video *video,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_format *f)
++{
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity = &vdev->entity;
++      struct v4l2_subdev *subdev;
++      int ret, index;
++      struct v4l2_subdev_format fmt = {
++              .pad = 0,
++              .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++              .reserved = {getcrop_pad_id(video->id)}
++      };
++      struct v4l2_mbus_framefmt *mf = &fmt.format;
++      struct v4l2_pix_format *pix = &f->fmt.pix;
++      struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
++      struct media_entity *sensor;
++      u32 width, height;
++      struct media_pad *pad;
++
++      /* pix to mbus format */
++      if (video->is_mp) {
++              index = video_find_format(mf->code,
++                                      pix_mp->pixelformat,
++                                      video->formats, video->nformats);
++              if (index < 0)
++                      return index;
++              v4l2_fill_mbus_format_mplane(mf, pix_mp);
++              mf->code = video->formats[index].code;
++      } else {
++              index = video_find_format(mf->code,
++                                      pix->pixelformat,
++                                      video->formats, video->nformats);
++              if (index < 0)
++                      return index;
++              v4l2_fill_mbus_format(mf, pix, video->formats[index].code);
++      }
++
++      width = mf->width;
++      height = mf->height;
++
++      sensor = stfcamss_find_sensor(entity);
++      if (!sensor) {
++              st_err(ST_VIDEO, "Can't find sensor\n");
++              return -ENOTTY;
++      }
++
++      subdev = media_entity_to_v4l2_subdev(sensor);
++      ret = v4l2_subdev_call(subdev, pad, get_fmt, state, &fmt);
++      if (ret)
++              return ret;
++
++      /*
++       * Starting from sensor subdevice, walk within
++       * pipeline and set format on each subdevice
++       */
++      pad = media_pad_remote_pad_first(&sensor->pads[0]);
++      ret = video_entity_s_fmt(video, pad->entity, state, &fmt);
++      if (ret < 0 && ret != -ENOIOCTLCMD)
++              return ret;
++
++      index = video_find_format(mf->code,
++                              video->formats[index].pixelformat,
++                              video->formats, video->nformats);
++      st_debug(ST_VIDEO, "%s, code=%x, index=%d\n",
++                      __func__, mf->code, index);
++
++      if (index < 0)
++              return index;
++
++      if (video->is_mp)
++              video_mbus_to_pix_mp(mf, pix_mp,
++                              &video->formats[index], video->bpl_alignment);
++      else
++              video_mbus_to_pix(mf, pix,
++                              &video->formats[index], video->bpl_alignment);
++
++      ret = __video_try_fmt(video, f, video->is_mp);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      int ret;
++
++      st_debug(ST_VIDEO, "%s, fmt.type = 0x%x, v4l2fmt=%x\n",
++                      __func__, f->type, f->fmt.pix.pixelformat);
++
++      if (vb2_is_busy(&video->vb2_q))
++              return -EBUSY;
++
++      ret = __video_try_fmt(video, f, false);
++      if (ret < 0)
++              return ret;
++
++      ret = video_pipeline_s_fmt(video, NULL, f);
++
++      st_debug(ST_VIDEO, "%s, pixelformat=0x%x, ret=%d\n",
++                      __func__, f->fmt.pix.pixelformat, ret);
++      if (ret < 0)
++              return ret;
++
++      video->active_fmt = *f;
++
++      return 0;
++}
++
++static int video_s_fmt_mp(struct file *file, void *fh, struct v4l2_format *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      int ret;
++
++      st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type);
++      if (vb2_is_busy(&video->vb2_q))
++              return -EBUSY;
++
++      ret = __video_try_fmt(video, f, true);
++      if (ret < 0)
++              return ret;
++
++      ret = video_pipeline_s_fmt(video, NULL, f);
++      if (ret < 0)
++              return ret;
++
++      video->active_fmt = *f;
++
++      return 0;
++}
++
++static int video_try_fmt(struct file *file,
++              void *fh, struct v4l2_format *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++
++      return __video_try_fmt(video, f, false);
++}
++
++static int video_try_fmt_mp(struct file *file,
++              void *fh, struct v4l2_format *f)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++
++      return __video_try_fmt(video, f, true);
++}
++
++static int video_enum_input(struct file *file, void *fh,
++                      struct v4l2_input *input)
++{
++      if (input->index > 0)
++              return -EINVAL;
++
++      strscpy(input->name, "camera", sizeof(input->name));
++      input->type = V4L2_INPUT_TYPE_CAMERA;
++
++      return 0;
++}
++
++static int video_g_input(struct file *file, void *fh, unsigned int *input)
++{
++      *input = 0;
++
++      return 0;
++}
++
++static int video_s_input(struct file *file, void *fh, unsigned int input)
++{
++      return input == 0 ? 0 : -EINVAL;
++}
++
++static int video_g_parm(struct file *file, void *priv,
++                      struct v4l2_streamparm *p)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity;
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad;
++      int ret, is_support = 0;
++
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              ret = v4l2_g_parm_cap(vdev, subdev, p);
++              if (ret < 0 && ret != -ENOIOCTLCMD)
++                      break;
++              if (!ret)
++                      is_support = 1;
++      }
++
++      return is_support ? 0 : ret;
++}
++
++static int video_s_parm(struct file *file, void *priv,
++                      struct v4l2_streamparm *p)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity;
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad;
++      struct v4l2_streamparm tmp_p;
++      int ret, is_support = 0;
++
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              tmp_p = *p;
++              ret = v4l2_s_parm_cap(vdev, subdev, &tmp_p);
++              if (ret < 0 && ret != -ENOIOCTLCMD)
++                      break;
++              if (!ret) {
++                      is_support = 1;
++                      *p = tmp_p;
++              }
++      }
++
++      return is_support ? 0 : ret;
++}
++
++/* Crop ioctls */
++int video_g_pixelaspect(struct file *file, void *fh,
++                          int buf_type, struct v4l2_fract *aspect)
++{
++      return 0;
++}
++
++int video_g_selection(struct file *file, void *fh,
++                        struct v4l2_selection *s)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity;
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad;
++      struct v4l2_subdev_selection sel = {
++              .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++              .pad = getcrop_pad_id(video->id),
++              .target = s->target,
++              .r = s->r,
++              .flags = s->flags,
++      };
++      int ret;
++
++      st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n",
++                      __func__, sel.target, s->target);
++      if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
++              && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++              return -EINVAL;
++
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sel);
++              if (!ret) {
++                      s->r = sel.r;
++                      s->flags = sel.flags;
++                      break;
++              }
++              if (ret != -ENOIOCTLCMD)
++                      break;
++      }
++
++      return ret;
++}
++
++int video_s_selection(struct file *file, void *fh,
++                      struct v4l2_selection *s)
++{
++      struct stfcamss_video *video = video_drvdata(file);
++      struct video_device *vdev = &video->vdev;
++      struct media_entity *entity;
++      struct v4l2_subdev *subdev;
++      struct media_pad *pad;
++      struct v4l2_subdev_selection sel = {
++              .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++              .pad = getcrop_pad_id(video->id),
++              .target = s->target,
++              .r = s->r,
++              .flags = s->flags,
++      };
++      struct v4l2_pix_format *format = &video->active_fmt.fmt.pix;
++      struct v4l2_pix_format_mplane *format_mp =
++                                              &video->active_fmt.fmt.pix_mp;
++      int ret;
++
++      st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n",
++                      __func__, sel.target, s->target);
++      if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
++              && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++              return -EINVAL;
++
++      entity = &vdev->entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      break;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      break;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++
++              ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sel);
++              if (!ret) {
++                      s->r = sel.r;
++                      s->flags = sel.flags;
++                      format->width = s->r.width;
++                      format->height = s->r.height;
++                      format_mp->width = s->r.width;
++                      format_mp->height = s->r.height;
++                      ret = __video_try_fmt(video, &video->active_fmt,
++                                      video->is_mp);
++                      if (ret < 0)
++                              return ret;
++                      break;
++              }
++              if (ret != -ENOIOCTLCMD)
++                      break;
++      }
++
++      st_debug(ST_VIDEO, "ret = 0x%x, -EINVAL = 0x%x\n", ret, -EINVAL);
++
++      return ret;
++}
++
++static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
++      .vidioc_querycap                = video_querycap,
++      .vidioc_enum_fmt_vid_cap        = video_enum_fmt,
++      .vidioc_enum_framesizes         = video_enum_framesizes,
++      .vidioc_enum_frameintervals     = video_enum_frameintervals,
++      .vidioc_g_fmt_vid_cap           = video_g_fmt,
++      .vidioc_s_fmt_vid_cap           = video_s_fmt,
++      .vidioc_try_fmt_vid_cap         = video_try_fmt,
++      .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
++      .vidioc_querybuf                = vb2_ioctl_querybuf,
++      .vidioc_qbuf                    = vb2_ioctl_qbuf,
++      .vidioc_expbuf                  = vb2_ioctl_expbuf,
++      .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
++      .vidioc_create_bufs             = vb2_ioctl_create_bufs,
++      .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
++      .vidioc_streamon                = vb2_ioctl_streamon,
++      .vidioc_streamoff               = vb2_ioctl_streamoff,
++      .vidioc_enum_input              = video_enum_input,
++      .vidioc_g_input                 = video_g_input,
++      .vidioc_s_input                 = video_s_input,
++      .vidioc_g_parm                  = video_g_parm,
++      .vidioc_s_parm                  = video_s_parm,
++      .vidioc_s_selection             = video_s_selection,
++      .vidioc_g_selection             = video_g_selection,
++};
++
++static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_mp = {
++      .vidioc_querycap                = video_querycap,
++      .vidioc_enum_fmt_vid_cap        = video_enum_fmt,
++      .vidioc_enum_framesizes         = video_enum_framesizes,
++      .vidioc_enum_frameintervals     = video_enum_frameintervals,
++      .vidioc_g_fmt_vid_cap_mplane    = video_g_fmt_mp,
++      .vidioc_s_fmt_vid_cap_mplane    = video_s_fmt_mp,
++      .vidioc_try_fmt_vid_cap_mplane  = video_try_fmt_mp,
++      .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
++      .vidioc_querybuf                = vb2_ioctl_querybuf,
++      .vidioc_qbuf                    = vb2_ioctl_qbuf,
++      .vidioc_expbuf                  = vb2_ioctl_expbuf,
++      .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
++      .vidioc_create_bufs             = vb2_ioctl_create_bufs,
++      .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
++      .vidioc_streamon                = vb2_ioctl_streamon,
++      .vidioc_streamoff               = vb2_ioctl_streamoff,
++      .vidioc_enum_input              = video_enum_input,
++      .vidioc_g_input                 = video_g_input,
++      .vidioc_s_input                 = video_s_input,
++      .vidioc_g_parm                  = video_g_parm,
++      .vidioc_s_parm                  = video_s_parm,
++      .vidioc_s_selection             = video_s_selection,
++      .vidioc_g_selection             = video_g_selection,
++};
++
++static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_out = {
++      .vidioc_querycap                = video_querycap,
++      .vidioc_enum_fmt_vid_out        = video_enum_fmt,
++      .vidioc_enum_framesizes         = video_enum_framesizes,
++      .vidioc_enum_frameintervals     = video_enum_frameintervals,
++      .vidioc_g_fmt_vid_out           = video_g_fmt,
++      .vidioc_s_fmt_vid_out           = video_s_fmt,
++      .vidioc_try_fmt_vid_out         = video_try_fmt,
++      .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
++      .vidioc_querybuf                = vb2_ioctl_querybuf,
++      .vidioc_qbuf                    = vb2_ioctl_qbuf,
++      .vidioc_expbuf                  = vb2_ioctl_expbuf,
++      .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
++      .vidioc_create_bufs             = vb2_ioctl_create_bufs,
++      .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
++      .vidioc_streamon                = vb2_ioctl_streamon,
++      .vidioc_streamoff               = vb2_ioctl_streamoff,
++};
++
++static int video_open(struct file *file)
++{
++      struct video_device *vdev = video_devdata(file);
++      struct stfcamss_video *video = video_drvdata(file);
++      struct v4l2_fh *vfh;
++      int ret;
++
++      mutex_lock(&video->lock);
++
++      vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
++      if (vfh == NULL) {
++              ret = -ENOMEM;
++              goto error_alloc;
++      }
++
++      v4l2_fh_init(vfh, vdev);
++      v4l2_fh_add(vfh);
++
++      file->private_data = vfh;
++
++      if (!video->pm_count) {
++              ret = v4l2_pipeline_pm_get(&vdev->entity);
++              if (ret < 0) {
++                      st_err(ST_VIDEO,
++                              "Failed to power up pipeline: %d\n", ret);
++                      goto error_pm_use;
++              }
++      }
++
++      video->pm_count++;
++
++      mutex_unlock(&video->lock);
++
++      return 0;
++
++error_pm_use:
++      v4l2_fh_release(file);
++error_alloc:
++      mutex_unlock(&video->lock);
++      return ret;
++}
++
++static int video_release(struct file *file)
++{
++      struct video_device *vdev = video_devdata(file);
++      struct stfcamss_video *video = video_drvdata(file);
++
++      vb2_fop_release(file);
++
++      video->pm_count--;
++
++      if (!video->pm_count)
++              v4l2_pipeline_pm_put(&vdev->entity);
++
++      file->private_data = NULL;
++
++      return 0;
++}
++
++static const struct v4l2_file_operations stf_vid_fops = {
++      .owner          = THIS_MODULE,
++      .unlocked_ioctl = video_ioctl2,
++      .open           = video_open,
++      .release        = video_release,
++      .poll           = vb2_fop_poll,
++      .mmap           = vb2_fop_mmap,
++      .read           = vb2_fop_read,
++};
++
++static void stf_video_release(struct video_device *vdev)
++{
++      struct stfcamss_video *video = video_get_drvdata(vdev);
++
++      media_entity_cleanup(&vdev->entity);
++
++      mutex_destroy(&video->q_lock);
++      mutex_destroy(&video->lock);
++}
++
++int stf_video_register(struct stfcamss_video *video,
++                      struct v4l2_device *v4l2_dev,
++                      const char *name, int is_mp)
++{
++      struct video_device *vdev;
++      struct vb2_queue *q;
++      struct media_pad *pad = &video->pad;
++      int ret;
++      enum isp_pad_id isp_pad;
++
++      vdev = &video->vdev;
++
++      mutex_init(&video->q_lock);
++
++      q = &video->vb2_q;
++      q->drv_priv = video;
++      q->mem_ops = &vb2_dma_contig_memops;
++      q->ops = &stf_video_vb2_q_ops;
++      //q->type = is_mp ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
++      //      V4L2_BUF_TYPE_VIDEO_CAPTURE;
++      q->type = video->type;
++      q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
++      q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++      q->buf_struct_size = sizeof(struct stfcamss_buffer);
++      q->dev = video->stfcamss->dev;
++      q->lock = &video->q_lock;
++      q->min_buffers_needed = STFCAMSS_MIN_BUFFERS;
++      ret = vb2_queue_init(q);
++      if (ret < 0) {
++              st_err(ST_VIDEO,
++                      "Failed to init vb2 queue: %d\n", ret);
++              goto err_vb2_init;
++      }
++
++      pad->flags = MEDIA_PAD_FL_SINK;
++      ret = media_entity_pads_init(&vdev->entity, 1, pad);
++      if (ret < 0) {
++              st_err(ST_VIDEO,
++                      "Failed to init video entity: %d\n",
++                      ret);
++              goto err_vb2_init;
++      }
++
++      mutex_init(&video->lock);
++
++      isp_pad = stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC);
++      if (video->id == VIN_LINE_WR) {
++              video->formats = formats_pix_st7110_wr;
++              video->nformats = ARRAY_SIZE(formats_pix_st7110_wr);
++              video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8;
++      } else if (isp_pad == STF_ISP_PAD_SRC
++              || isp_pad == STF_ISP_PAD_SRC_SS0
++              || isp_pad == STF_ISP_PAD_SRC_SS1) {
++              video->formats = formats_pix_st7110_isp;
++              video->nformats = ARRAY_SIZE(formats_pix_st7110_isp);
++              video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8;
++      } else if (isp_pad == STF_ISP_PAD_SRC_ITIW
++              || isp_pad == STF_ISP_PAD_SRC_ITIR) {
++              video->formats = formats_st7110_isp_iti;
++              video->nformats = ARRAY_SIZE(formats_st7110_isp_iti);
++              video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8;
++      } else { // raw/scdump/yhist
++              video->formats = formats_raw_st7110_isp;
++              video->nformats = ARRAY_SIZE(formats_raw_st7110_isp);
++              video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_128;
++      }
++      video->is_mp = is_mp;
++
++      ret = stf_video_init_format(video, is_mp);
++      if (ret < 0) {
++              st_err(ST_VIDEO, "Failed to init format: %d\n", ret);
++              goto err_vid_init_format;
++      }
++
++      vdev->fops = &stf_vid_fops;
++      if (isp_pad == STF_ISP_PAD_SRC_ITIR) {
++              vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT;
++              vdev->vfl_dir = VFL_DIR_TX;
++      } else {
++              vdev->device_caps = is_mp ? V4L2_CAP_VIDEO_CAPTURE_MPLANE :
++                      V4L2_CAP_VIDEO_CAPTURE;
++              vdev->vfl_dir = VFL_DIR_RX;
++      }
++      vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
++      if (video->type == V4L2_CAP_VIDEO_OUTPUT)
++              vdev->ioctl_ops = &stf_vid_ioctl_ops_out;
++      else
++              vdev->ioctl_ops = is_mp ? &stf_vid_ioctl_ops_mp : &stf_vid_ioctl_ops;
++      vdev->release = stf_video_release;
++      vdev->v4l2_dev = v4l2_dev;
++      vdev->queue = &video->vb2_q;
++      vdev->lock = &video->lock;
++      //strlcpy(vdev->name, name, sizeof(vdev->name));
++      strscpy(vdev->name, name, sizeof(vdev->name));
++
++      ret = video_register_device(vdev, VFL_TYPE_VIDEO, video->id);
++      if (ret < 0) {
++              st_err(ST_VIDEO,
++                      "Failed to register video device: %d\n",
++                      ret);
++              goto err_vid_reg;
++      }
++
++      video_set_drvdata(vdev, video);
++      return 0;
++
++err_vid_reg:
++err_vid_init_format:
++      media_entity_cleanup(&vdev->entity);
++      mutex_destroy(&video->lock);
++err_vb2_init:
++      mutex_destroy(&video->q_lock);
++      return ret;
++}
++
++void stf_video_unregister(struct stfcamss_video *video)
++{
++      vb2_video_unregister_device(&video->vdev);
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.h
+@@ -0,0 +1,83 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_VIDEO_H
++#define STF_VIDEO_H
++
++#include <linux/mutex.h>
++#include <media/videobuf2-v4l2.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-fh.h>
++#include <media/v4l2-ioctl.h>
++
++#define STFCAMSS_FRAME_MIN_WIDTH              64
++#define STFCAMSS_FRAME_MAX_WIDTH              1920
++#define STFCAMSS_FRAME_MIN_HEIGHT             64
++#define STFCAMSS_FRAME_MAX_HEIGHT             1080
++#define STFCAMSS_FRAME_WIDTH_ALIGN_8          8
++#define STFCAMSS_FRAME_WIDTH_ALIGN_128                128
++#define STFCAMSS_MIN_BUFFERS                  2
++
++#define STFCAMSS_MAX_ENTITY_NAME_LEN          27
++
++struct stfcamss_buffer {
++      struct vb2_v4l2_buffer vb;
++      dma_addr_t addr[3];
++      void *vaddr_sc;         /* Use for isp sc data */
++      struct list_head queue;
++      int sizeimage;
++};
++
++struct stfcamss_video;
++
++struct stfcamss_video_ops {
++      int (*queue_buffer)(struct stfcamss_video *vid,
++                      struct stfcamss_buffer *buf);
++      int (*flush_buffers)(struct stfcamss_video *vid,
++                      enum vb2_buffer_state state);
++};
++
++struct fract {
++      u8 numerator;
++      u8 denominator;
++};
++
++struct stfcamss_format_info {
++      u32 code;
++      u32 pixelformat;
++      u8 planes;
++      struct fract hsub[3];
++      struct fract vsub[3];
++      u8 bpp[3];
++};
++
++struct stfcamss_video {
++      struct stfcamss *stfcamss;
++      u8 id;
++      struct vb2_queue vb2_q;
++      struct video_device vdev;
++      struct media_pad pad;
++      struct media_pipeline pipe;
++      struct v4l2_format active_fmt;
++      enum v4l2_buf_type type;
++      const struct stfcamss_video_ops *ops;
++      struct mutex lock;
++      struct mutex q_lock;
++      unsigned int bpl_alignment;
++      const struct stfcamss_format_info *formats;
++      unsigned int nformats;
++      unsigned int is_mp;
++      unsigned int pm_count;
++};
++
++int stf_video_register(struct stfcamss_video *video,
++              struct v4l2_device *v4l2_dev, const char *name, int is_mp);
++
++void stf_video_unregister(struct stfcamss_video *video);
++
++#endif /* STF_VIDEO_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+@@ -0,0 +1,1515 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/pm_runtime.h>
++#include <media/v4l2-event.h>
++
++#include "stfcamss.h"
++
++#define vin_line_array(ptr_line) \
++              ((const struct vin_line (*)[]) &(ptr_line[-(ptr_line->id)]))
++
++#define line_to_vin2_dev(ptr_line) \
++              container_of(vin_line_array(ptr_line), struct stf_vin2_dev, line)
++
++#define VIN_FRAME_DROP_MAX_VAL 90
++#define VIN_FRAME_DROP_MIN_VAL 4
++#define VIN_FRAME_PER_SEC_MAX_VAL 90
++
++/* ISP ctrl need 1 sec to let frames become stable. */
++#define VIN_FRAME_DROP_SEC_FOR_ISP_CTRL 1
++
++
++// #define VIN_TWO_BUFFER
++
++static const struct vin2_format vin2_formats_st7110[] = {
++      { MEDIA_BUS_FMT_YUYV8_2X8, 16},
++      { MEDIA_BUS_FMT_RGB565_2X8_LE, 16},
++      { MEDIA_BUS_FMT_SRGGB8_1X8, 8},
++      { MEDIA_BUS_FMT_SGRBG8_1X8, 8},
++      { MEDIA_BUS_FMT_SGBRG8_1X8, 8},
++      { MEDIA_BUS_FMT_SBGGR8_1X8, 8},
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++      { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++      { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++      { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++      { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++      { MEDIA_BUS_FMT_Y12_1X12, 8},
++      { MEDIA_BUS_FMT_YUV8_1X24, 8},
++      { MEDIA_BUS_FMT_AYUV8_1X32, 32},
++};
++
++static const struct vin2_format isp_formats_st7110_raw[] = {
++      { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++      { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++      { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++      { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++};
++
++static const struct vin2_format isp_formats_st7110_uo[] = {
++      { MEDIA_BUS_FMT_Y12_1X12, 8},
++};
++
++static const struct vin2_format isp_formats_st7110_iti[] = {
++      { MEDIA_BUS_FMT_SRGGB10_1X10, 10},
++      { MEDIA_BUS_FMT_SGRBG10_1X10, 10},
++      { MEDIA_BUS_FMT_SGBRG10_1X10, 10},
++      { MEDIA_BUS_FMT_SBGGR10_1X10, 10},
++      { MEDIA_BUS_FMT_SRGGB12_1X12, 12},
++      { MEDIA_BUS_FMT_SGRBG12_1X12, 12},
++      { MEDIA_BUS_FMT_SGBRG12_1X12, 12},
++      { MEDIA_BUS_FMT_SBGGR12_1X12, 12},
++      { MEDIA_BUS_FMT_Y12_1X12, 8},
++      { MEDIA_BUS_FMT_YUV8_1X24, 8},
++};
++
++static const struct vin2_format_table vin2_formats_table[] = {
++      /* VIN_LINE_WR */
++      { vin2_formats_st7110, ARRAY_SIZE(vin2_formats_st7110) },
++      /* VIN_LINE_ISP */
++      { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },
++      /* VIN_LINE_ISP_SS0 */
++      { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },
++      /* VIN_LINE_ISP_SS1 */
++      { isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },
++      /* VIN_LINE_ISP_ITIW */
++      { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },
++      /* VIN_LINE_ISP_ITIR */
++      { isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },
++      /* VIN_LINE_ISP_RAW */
++      { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },
++      /* VIN_LINE_ISP_SCD_Y */
++      { isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },
++};
++
++static void vin_buffer_done(struct vin_line *line, struct vin_params *params);
++static void vin_change_buffer(struct vin_line *line);
++static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output);
++static void vin_output_init_addrs(struct vin_line *line);
++static void vin_init_outputs(struct vin_line *line);
++static struct v4l2_mbus_framefmt *
++__vin_get_format(struct vin_line *line,
++              struct v4l2_subdev_state *state,
++              unsigned int pad,
++              enum v4l2_subdev_format_whence which);
++
++static char *get_line_subdevname(int line_id)
++{
++      char *name = NULL;
++
++      switch (line_id) {
++      case VIN_LINE_WR:
++              name = "wr";
++              break;
++      case VIN_LINE_ISP:
++              name = "isp0";
++              break;
++      case VIN_LINE_ISP_SS0:
++              name = "isp0_ss0";
++              break;
++      case VIN_LINE_ISP_SS1:
++              name = "isp0_ss1";
++              break;
++      case VIN_LINE_ISP_ITIW:
++              name = "isp0_itiw";
++              break;
++      case VIN_LINE_ISP_ITIR:
++              name = "isp0_itir";
++              break;
++      case VIN_LINE_ISP_RAW:
++              name = "isp0_raw";
++              break;
++      case VIN_LINE_ISP_SCD_Y:
++              name = "isp0_scd_y";
++              break;
++      default:
++              name = "unknow";
++              break;
++      }
++      return name;
++}
++
++static enum isp_line_id stf_vin_map_isp_line(enum vin_line_id line)
++{
++      enum isp_line_id line_id;
++
++      if ((line > VIN_LINE_WR) && (line < VIN_LINE_MAX)) {
++              line_id = line % STF_ISP_LINE_MAX;
++              if (line_id == 0)
++                      line_id = STF_ISP_LINE_SRC_SCD_Y;
++      } else
++              line_id = STF_ISP_LINE_INVALID;
++
++      return line_id;
++}
++
++enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line, enum isp_pad_id def)
++{
++      enum isp_pad_id pad_id;
++
++      if (line == VIN_LINE_WR)
++              pad_id = STF_ISP_PAD_SINK;
++      else if ((line > VIN_LINE_WR) && (line < VIN_LINE_MAX))
++              pad_id = stf_vin_map_isp_line(line);
++      else
++              pad_id = def;
++
++      return pad_id;
++}
++
++int stf_vin_subdev_init(struct stfcamss *stfcamss)
++{
++      struct stf_vin_dev *vin;
++      struct device *dev = stfcamss->dev;
++      struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++      int i, ret = 0;
++
++      vin_dev->stfcamss = stfcamss;
++      vin_dev->hw_ops = &vin_ops;
++      vin_dev->hw_ops->isr_buffer_done = vin_buffer_done;
++      vin_dev->hw_ops->isr_change_buffer = vin_change_buffer;
++
++      vin = stfcamss->vin;
++      atomic_set(&vin_dev->ref_count, 0);
++
++      ret = devm_request_irq(dev,
++                      vin->irq, vin_dev->hw_ops->vin_wr_irq_handler,
++                      0, "vin_axiwr_irq", vin_dev);
++      if (ret) {
++              st_err(ST_VIN, "failed to request irq\n");
++              goto out;
++      }
++
++      ret = devm_request_irq(dev,
++                      vin->isp_irq, vin_dev->hw_ops->vin_isp_irq_handler,
++                      0, "vin_isp_irq", vin_dev);
++      if (ret) {
++              st_err(ST_VIN, "failed to request isp irq\n");
++              goto out;
++      }
++
++      st_info(ST_CAMSS, "%s, %d!\n", __func__, __LINE__);
++#ifdef ISP_USE_CSI_AND_SC_DONE_INTERRUPT
++      ret = devm_request_irq(dev,
++                      vin->isp_csi_irq, vin_dev->hw_ops->vin_isp_csi_irq_handler,
++                      0, "vin_isp_csi_irq", vin_dev);
++      if (ret) {
++              st_err(ST_VIN, "failed to request isp raw irq\n");
++              goto out;
++      }
++
++      ret = devm_request_irq(dev,
++                      vin->isp_scd_irq, vin_dev->hw_ops->vin_isp_scd_irq_handler,
++                      0, "vin_isp_scd_irq", vin_dev);
++      if (ret) {
++              st_err(ST_VIN, "failed to request isp scd irq\n");
++              goto out;
++      }
++#endif
++
++      ret = devm_request_irq(dev,
++                      vin->isp_irq_csiline, vin_dev->hw_ops->vin_isp_irq_csiline_handler,
++                      0, "vin_isp_irq_csiline", vin_dev);
++      if (ret) {
++              st_err(ST_VIN, "failed to request isp irq csiline\n");
++              goto out;
++      }
++
++      mutex_init(&vin_dev->power_lock);
++      vin_dev->power_count = 0;
++
++      for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++) {
++              struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[i];
++
++              mutex_init(&dummy_buffer->stream_lock);
++              dummy_buffer->nums = i == 0 ? VIN_DUMMY_BUFFER_NUMS : ISP_DUMMY_BUFFER_NUMS;
++              dummy_buffer->stream_count = 0;
++              dummy_buffer->buffer = devm_kzalloc(dev,
++                      dummy_buffer->nums * sizeof(struct vin_dummy_buffer), GFP_KERNEL);
++              atomic_set(&dummy_buffer->frame_skip, 0);
++      }
++
++      for (i = VIN_LINE_WR;
++              i < STF_ISP_LINE_MAX + 1; i++) {
++              struct vin_line *l = &vin_dev->line[i];
++              int is_mp;
++
++              is_mp = i == VIN_LINE_WR ? false : true;
++              is_mp = false;
++              if (stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC_ITIR)
++                      l->video_out.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++              else
++                      l->video_out.type = is_mp ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
++                              V4L2_BUF_TYPE_VIDEO_CAPTURE;
++              l->video_out.stfcamss = stfcamss;
++              l->id = i;
++              l->sdev_type = VIN_DEV_TYPE;
++              l->formats = vin2_formats_table[i].fmts;
++              l->nformats = vin2_formats_table[i].nfmts;
++              spin_lock_init(&l->output_lock);
++
++              mutex_init(&l->stream_lock);
++              l->stream_count = 0;
++              mutex_init(&l->power_lock);
++              l->power_count = 0;
++      }
++
++      return 0;
++out:
++      return ret;
++}
++
++static int vin_set_power(struct v4l2_subdev *sd, int on)
++{
++      struct vin_line *line = v4l2_get_subdevdata(sd);
++      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++      struct stfcamss *stfcamss = vin_dev->stfcamss;
++
++      mutex_lock(&line->power_lock);
++      if (on) {
++              if (line->power_count == 0)
++                      vin_init_outputs(line);
++              line->power_count++;
++      } else {
++              if (line->power_count == 0) {
++                      st_err(ST_VIN,
++                              "line power off on power_count == 0\n");
++                      goto exit_line;
++              }
++              line->power_count--;
++      }
++exit_line:
++      mutex_unlock(&line->power_lock);
++
++      mutex_lock(&vin_dev->power_lock);
++      if (on) {
++              if (vin_dev->power_count == 0) {
++                      pm_runtime_get_sync(stfcamss->dev);
++                      vin_dev->hw_ops->vin_clk_enable(vin_dev);
++                      vin_dev->hw_ops->vin_config_set(vin_dev);
++              }
++              vin_dev->power_count++;
++      } else {
++              if (vin_dev->power_count == 0) {
++                      st_err(ST_VIN,
++                              "vin_dev power off on power_count == 0\n");
++                      goto exit;
++              }
++              if (vin_dev->power_count == 1) {
++                      vin_dev->hw_ops->vin_clk_disable(vin_dev);
++                      pm_runtime_put_sync(stfcamss->dev);
++              }
++              vin_dev->power_count--;
++      }
++exit:
++
++      mutex_unlock(&vin_dev->power_lock);
++
++      return 0;
++}
++
++static unsigned int get_frame_skip(struct vin_line *line)
++{
++      unsigned int frame_skip = 0;
++      unsigned int isp_ctrl_skip_frames = 0;
++      struct media_entity *sensor;
++      struct v4l2_subdev_frame_interval fi;
++
++      sensor = stfcamss_find_sensor(&line->subdev.entity);
++      if (sensor) {
++              int fps = 0;
++              struct v4l2_subdev *subdev =
++                                      media_entity_to_v4l2_subdev(sensor);
++
++              if (subdev->ops->video->g_frame_interval) {
++                      if (!subdev->ops->video->g_frame_interval(subdev, &fi))
++                              fps = fi.interval.denominator;
++
++                      if (fps > 0 && fps <= 90)
++                              isp_ctrl_skip_frames = fps * VIN_FRAME_DROP_SEC_FOR_ISP_CTRL;
++              }
++              if (!fps)
++                      st_debug(ST_VIN, "%s, Failed to get sensor fps !\n", __func__);
++
++              if (isp_ctrl_skip_frames <= VIN_FRAME_DROP_MIN_VAL)
++                      isp_ctrl_skip_frames = VIN_FRAME_DROP_MIN_VAL;
++
++              v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
++
++              frame_skip += isp_ctrl_skip_frames;
++
++              if (frame_skip > VIN_FRAME_DROP_MAX_VAL)
++                      frame_skip = VIN_FRAME_DROP_MAX_VAL;
++              st_debug(ST_VIN, "%s, frame_skip %d\n", __func__, frame_skip);
++      }
++
++      return frame_skip;
++}
++
++static void vin_buf_l2cache_flush(struct vin_output *output)
++{
++      struct stfcamss_buffer *buffer = NULL;
++
++      if (!list_empty(&output->pending_bufs)) {
++              list_for_each_entry(buffer, &output->pending_bufs, queue) {
++                      sifive_l2_flush64_range(buffer->addr[0], buffer->sizeimage);
++              }
++      }
++}
++
++static int vin_enable_output(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++      unsigned long flags;
++
++      spin_lock_irqsave(&line->output_lock, flags);
++
++      output->state = VIN_OUTPUT_IDLE;
++
++      vin_buf_l2cache_flush(output);
++
++      output->buf[0] = vin_buf_get_pending(output);
++#ifdef VIN_TWO_BUFFER
++      if (line->id == VIN_LINE_WR)
++              output->buf[1] = vin_buf_get_pending(output);
++#endif
++      if (!output->buf[0] && output->buf[1]) {
++              output->buf[0] = output->buf[1];
++              output->buf[1] = NULL;
++      }
++
++      if (output->buf[0])
++              output->state = VIN_OUTPUT_SINGLE;
++
++#ifdef VIN_TWO_BUFFER
++      if (output->buf[1] && line->id == VIN_LINE_WR)
++              output->state = VIN_OUTPUT_CONTINUOUS;
++#endif
++      output->sequence = 0;
++
++      vin_output_init_addrs(line);
++      spin_unlock_irqrestore(&line->output_lock, flags);
++      return 0;
++}
++
++static int vin_disable_output(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++      unsigned long flags;
++
++      spin_lock_irqsave(&line->output_lock, flags);
++
++      output->state = VIN_OUTPUT_OFF;
++
++      spin_unlock_irqrestore(&line->output_lock, flags);
++      return 0;
++}
++
++static u32 line_to_dummy_module(struct vin_line *line)
++{
++      u32 dummy_module = 0;
++
++      switch (line->id) {
++      case VIN_LINE_WR:
++              dummy_module = STF_DUMMY_VIN;
++              break;
++      case VIN_LINE_ISP:
++      case VIN_LINE_ISP_SS0:
++      case VIN_LINE_ISP_SS1:
++      case VIN_LINE_ISP_ITIW:
++      case VIN_LINE_ISP_ITIR:
++      case VIN_LINE_ISP_RAW:
++      case VIN_LINE_ISP_SCD_Y:
++              dummy_module = STF_DUMMY_ISP;
++              break;
++      default:
++              dummy_module = STF_DUMMY_VIN;
++              break;
++      }
++
++      return dummy_module;
++}
++
++static int vin_alloc_dummy_buffer(struct stf_vin2_dev *vin_dev,
++              struct v4l2_mbus_framefmt *fmt, int dummy_module)
++{
++      struct device *dev = vin_dev->stfcamss->dev;
++      struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++      struct vin_dummy_buffer *buffer = NULL;
++      int ret = 0, i;
++      u32 aligns;
++
++      for (i = 0; i < dummy_buffer->nums; i++) {
++              buffer = &vin_dev->dummy_buffer[dummy_module].buffer[i];
++              buffer->width = fmt->width;
++              buffer->height = fmt->height;
++              buffer->mcode = fmt->code;
++              if (i == STF_VIN_PAD_SINK) {
++                      aligns = ALIGN(fmt->width * 4, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++                      buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height);
++              } else if (i == STF_ISP_PAD_SRC
++                      || i == STF_ISP_PAD_SRC_SS0
++                      || i == STF_ISP_PAD_SRC_SS1) {
++                      aligns = ALIGN(fmt->width, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++                      buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height * 3 / 2);
++              } else if (i == STF_ISP_PAD_SRC_ITIW
++                      || i == STF_ISP_PAD_SRC_ITIR) {
++                      aligns = ALIGN(fmt->width, STFCAMSS_FRAME_WIDTH_ALIGN_8);
++                      buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height * 3);
++              } else if (i == STF_ISP_PAD_SRC_RAW) {
++                      aligns = ALIGN(fmt->width * ISP_RAW_DATA_BITS / 8,
++                                      STFCAMSS_FRAME_WIDTH_ALIGN_128);
++                      buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height);
++              } else if (i == STF_ISP_PAD_SRC_SCD_Y)
++                      buffer->buffer_size = PAGE_ALIGN(ISP_SCD_Y_BUFFER_SIZE);
++              else
++                      continue;
++
++              buffer->vaddr = dma_alloc_coherent(dev, buffer->buffer_size,
++                              &buffer->paddr[0], GFP_DMA | GFP_KERNEL);
++
++              if (buffer->vaddr) {
++                      if (i == STF_ISP_PAD_SRC
++                              || i == STF_ISP_PAD_SRC_SS0
++                              || i == STF_ISP_PAD_SRC_SS1
++                              || i == STF_ISP_PAD_SRC_ITIW
++                              || i == STF_ISP_PAD_SRC_ITIR)
++                              buffer->paddr[1] = (dma_addr_t)(buffer->paddr[0] +
++                                                                      aligns * fmt->height);
++                      else if (i == STF_ISP_PAD_SRC_SCD_Y)
++                              buffer->paddr[1] = (dma_addr_t)(buffer->paddr[0] +
++                                                                      ISP_YHIST_BUFFER_SIZE);
++                      else
++                              st_debug(ST_VIN, "signal plane\n");
++              }
++              {
++                      char szPadName[][32] = {
++                              "VIN_PAD_SINK",
++                              "ISP_PAD_SRC",
++                              "ISP_PAD_SRC_SS0",
++                              "ISP_PAD_SRC_SS1",
++                              "ISP_PAD_SRC_ITIW",
++                              "ISP_PAD_SRC_ITIR",
++                              "ISP_PAD_SRC_RAW",
++                              "ISP_PAD_SRC_SCD_Y",
++                              "Unknown Pad"
++                      };
++
++                      st_debug(ST_VIN, "%s: i = %d(%s) addr[0] = %llx, addr[1] = %llx, size = %u bytes\n",
++                              __func__,
++                              i,
++                              szPadName[i],
++                              buffer->paddr[0],
++                              buffer->paddr[1],
++                              buffer->buffer_size
++                              );
++              }
++      }
++
++      return ret;
++}
++
++static void vin_free_dummy_buffer(struct stf_vin2_dev *vin_dev, int dummy_module)
++{
++      struct device *dev = vin_dev->stfcamss->dev;
++      struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++      struct vin_dummy_buffer *buffer = NULL;
++      int i;
++
++      for (i = 0; i < dummy_buffer->nums; i++) {
++              buffer = &dummy_buffer->buffer[i];
++              if (buffer->vaddr)
++                      dma_free_coherent(dev, buffer->buffer_size,
++                                              buffer->vaddr, buffer->paddr[0]);
++              memset(buffer, 0, sizeof(struct vin_dummy_buffer));
++      }
++}
++
++static void vin_set_dummy_buffer(struct vin_line *line, u32 pad)
++{
++      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++      int dummy_module = line_to_dummy_module(line);
++      struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++      struct vin_dummy_buffer *buffer = NULL;
++
++      switch (pad) {
++      case STF_VIN_PAD_SINK:
++              if (line->id == VIN_LINE_WR) {
++                      buffer = &dummy_buffer->buffer[STF_VIN_PAD_SINK];
++                      vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, buffer->paddr[0]);
++                      vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, buffer->paddr[0]);
++              } else {
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC];
++                      vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++                              buffer->paddr[0], buffer->paddr[1]);
++
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0];
++                      vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++                              buffer->paddr[0], buffer->paddr[1]);
++
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1];
++                      vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++                              buffer->paddr[0], buffer->paddr[1]);
++
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIW];
++                      vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++                              buffer->paddr[0], buffer->paddr[1]);
++
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIR];
++                      vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++                              buffer->paddr[0], buffer->paddr[1]);
++
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW];
++                      vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, buffer->paddr[0]);
++
++                      buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SCD_Y];
++                      vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++                              buffer->paddr[0], buffer->paddr[1], AWB_TYPE);
++              }
++              break;
++      case STF_ISP_PAD_SRC:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC];
++              vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++                      buffer->paddr[0], buffer->paddr[1]);
++              break;
++      case STF_ISP_PAD_SRC_SS0:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0];
++              vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++                      buffer->paddr[0], buffer->paddr[1]);
++              break;
++      case STF_ISP_PAD_SRC_SS1:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1];
++              vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++                      buffer->paddr[0], buffer->paddr[1]);
++              break;
++      case STF_ISP_PAD_SRC_ITIW:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIW];
++              vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++                      buffer->paddr[0], buffer->paddr[1]);
++              break;
++      case STF_ISP_PAD_SRC_ITIR:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_ITIR];
++              vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++                      buffer->paddr[0], buffer->paddr[1]);
++              break;
++      case STF_ISP_PAD_SRC_RAW:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW];
++              vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, buffer->paddr[0]);
++              break;
++      case STF_ISP_PAD_SRC_SCD_Y:
++              buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SCD_Y];
++              vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++                      buffer->paddr[0], buffer->paddr[1], AWB_TYPE);
++              break;
++      default:
++              break;
++      }
++}
++
++static int vin_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct vin_line *line = v4l2_get_subdevdata(sd);
++      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++      int dummy_module = line_to_dummy_module(line);
++      struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[dummy_module];
++      struct v4l2_mbus_framefmt *fmt;
++
++      st_debug(ST_VIN, "%s, %d\n", __func__, __LINE__);
++      fmt = __vin_get_format(line, NULL, STF_VIN_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE);
++      mutex_lock(&dummy_buffer->stream_lock);
++      if (enable) {
++              if (dummy_buffer->stream_count == 0) {
++                      vin_alloc_dummy_buffer(vin_dev, fmt, dummy_module);
++                      vin_set_dummy_buffer(line, STF_VIN_PAD_SINK);
++                      atomic_set(&dummy_buffer->frame_skip, get_frame_skip(line));
++              }
++              dummy_buffer->stream_count++;
++      } else {
++              if (dummy_buffer->stream_count == 1) {
++                      vin_free_dummy_buffer(vin_dev, dummy_module);
++                      // set buffer addr to zero
++                      vin_set_dummy_buffer(line, STF_VIN_PAD_SINK);
++              } else
++                      vin_set_dummy_buffer(line,
++                                      stf_vin_map_isp_pad(line->id, STF_ISP_PAD_SINK));
++
++              dummy_buffer->stream_count--;
++      }
++      mutex_unlock(&dummy_buffer->stream_lock);
++
++      mutex_lock(&line->stream_lock);
++      if (enable) {
++              if (line->stream_count == 0) {
++                      if (line->id == VIN_LINE_WR) {
++                              vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1);
++                              vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 1);
++                      }
++              }
++              line->stream_count++;
++      } else {
++              if (line->stream_count == 1) {
++                      if (line->id == VIN_LINE_WR) {
++                              vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0);
++                              vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 0);
++                      }
++              }
++              line->stream_count--;
++      }
++      mutex_unlock(&line->stream_lock);
++
++      if (enable)
++              vin_enable_output(line);
++      else
++              vin_disable_output(line);
++
++      return 0;
++}
++
++static struct v4l2_mbus_framefmt *
++__vin_get_format(struct vin_line *line,
++              struct v4l2_subdev_state *state,
++              unsigned int pad,
++              enum v4l2_subdev_format_whence which)
++{
++      if (which == V4L2_SUBDEV_FORMAT_TRY)
++              return v4l2_subdev_get_try_format(&line->subdev, state, pad);
++      return &line->fmt[pad];
++}
++
++static void vin_try_format(struct vin_line *line,
++                              struct v4l2_subdev_state *state,
++                              unsigned int pad,
++                              struct v4l2_mbus_framefmt *fmt,
++                              enum v4l2_subdev_format_whence which)
++{
++      unsigned int i;
++
++      switch (pad) {
++      case STF_VIN_PAD_SINK:
++              /* Set format on sink pad */
++
++              for (i = 0; i < line->nformats; i++)
++                      if (fmt->code == line->formats[i].code)
++                              break;
++
++              /* If not found, use UYVY as default */
++              if (i >= line->nformats)
++                      fmt->code = line->formats[0].code;
++
++              fmt->width = clamp_t(u32,
++                              fmt->width, STFCAMSS_FRAME_MIN_WIDTH, STFCAMSS_FRAME_MAX_WIDTH);
++              fmt->height = clamp_t(u32,
++                              fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, STFCAMSS_FRAME_MAX_HEIGHT);
++
++              fmt->field = V4L2_FIELD_NONE;
++              fmt->colorspace = V4L2_COLORSPACE_SRGB;
++              fmt->flags = 0;
++
++              break;
++
++      case STF_VIN_PAD_SRC:
++              /* Set and return a format same as sink pad */
++              *fmt = *__vin_get_format(line, state, STF_VIN_PAD_SINK, which);
++              break;
++      }
++
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++}
++
++static int vin_enum_mbus_code(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct vin_line *line = v4l2_get_subdevdata(sd);
++
++      if (code->index >= line->nformats)
++              return -EINVAL;
++      if (code->pad == STF_VIN_PAD_SINK) {
++              code->code = line->formats[code->index].code;
++      } else {
++              struct v4l2_mbus_framefmt *sink_fmt;
++
++              sink_fmt = __vin_get_format(line, state, STF_VIN_PAD_SINK,
++                                      code->which);
++
++              code->code = sink_fmt->code;
++              if (!code->code)
++                      return -EINVAL;
++      }
++      code->flags = 0;
++
++      return 0;
++}
++
++static int vin_enum_frame_size(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *state,
++                              struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct vin_line *line = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt format;
++
++      if (fse->index != 0)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = 1;
++      format.height = 1;
++      vin_try_format(line, state, fse->pad, &format, fse->which);
++      fse->min_width = format.width;
++      fse->min_height = format.height;
++
++      if (format.code != fse->code)
++              return -EINVAL;
++
++      format.code = fse->code;
++      format.width = -1;
++      format.height = -1;
++      vin_try_format(line, state, fse->pad, &format, fse->which);
++      fse->max_width = format.width;
++      fse->max_height = format.height;
++
++      return 0;
++}
++
++static int vin_get_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct vin_line *line = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      format = __vin_get_format(line, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      fmt->format = *format;
++
++      return 0;
++}
++
++static int vin_set_format(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_state *state,
++                      struct v4l2_subdev_format *fmt)
++{
++      struct vin_line *line = v4l2_get_subdevdata(sd);
++      struct v4l2_mbus_framefmt *format;
++
++      st_debug(ST_VIDEO, "%s, pad %d, fmt code  %x\n",
++                      __func__, fmt->pad, fmt->format.code);
++
++      format = __vin_get_format(line, state, fmt->pad, fmt->which);
++      if (format == NULL)
++              return -EINVAL;
++
++      mutex_lock(&line->stream_lock);
++      if (line->stream_count) {
++              fmt->format = *format;
++              mutex_unlock(&line->stream_lock);
++              goto out;
++      } else {
++              vin_try_format(line, state, fmt->pad, &fmt->format, fmt->which);
++              *format = fmt->format;
++      }
++      mutex_unlock(&line->stream_lock);
++
++      if (fmt->pad == STF_VIN_PAD_SINK) {
++              /* Propagate the format from sink to source */
++              format = __vin_get_format(line, state, STF_VIN_PAD_SRC,
++                                      fmt->which);
++
++              *format = fmt->format;
++              vin_try_format(line, state, STF_VIN_PAD_SRC, format,
++                                      fmt->which);
++      }
++
++out:
++      return 0;
++}
++
++static int vin_init_formats(struct v4l2_subdev *sd,
++                      struct v4l2_subdev_fh *fh)
++{
++      struct v4l2_subdev_format format = {
++              .pad = STF_VIN_PAD_SINK,
++              .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
++                              V4L2_SUBDEV_FORMAT_ACTIVE,
++              .format = {
++                      .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++                      .width = 1920,
++                      .height = 1080
++              }
++      };
++
++      return vin_set_format(sd, fh ? fh->state : NULL, &format);
++}
++
++static void vin_output_init_addrs(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++      dma_addr_t ping_addr;
++      dma_addr_t pong_addr;
++      dma_addr_t y_addr, uv_addr;
++
++      output->active_buf = 0;
++
++      if (output->buf[0]) {
++              ping_addr = output->buf[0]->addr[0];
++              y_addr = output->buf[0]->addr[0];
++              uv_addr = output->buf[0]->addr[1];
++      } else
++              return;
++
++      if (output->buf[1])
++              pong_addr = output->buf[1]->addr[0];
++      else
++              pong_addr = ping_addr;
++
++      switch (stf_vin_map_isp_line(line->id)) {
++      case STF_ISP_LINE_SRC:
++              vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++                      y_addr, uv_addr);
++              break;
++      case STF_ISP_LINE_SRC_SS0:
++              vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++                      y_addr, uv_addr);
++              break;
++      case STF_ISP_LINE_SRC_SS1:
++              vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++                      y_addr, uv_addr);
++              break;
++      case STF_ISP_LINE_SRC_ITIW:
++              vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++                      y_addr, uv_addr);
++              break;
++      case STF_ISP_LINE_SRC_ITIR:
++              vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++                      y_addr, uv_addr);
++              break;
++      case STF_ISP_LINE_SRC_RAW:
++              vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, y_addr);
++              break;
++      case STF_ISP_LINE_SRC_SCD_Y:
++              output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
++              vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++                      y_addr, uv_addr, AWB_TYPE);
++              break;
++      default:
++              if (line->id == VIN_LINE_WR) {
++                      vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, ping_addr);
++#ifdef VIN_TWO_BUFFER
++                      vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, pong_addr);
++#else
++                      vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, ping_addr);
++#endif
++              }
++              break;
++      }
++}
++
++static void vin_init_outputs(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++
++      output->state = VIN_OUTPUT_OFF;
++      output->buf[0] = NULL;
++      output->buf[1] = NULL;
++      output->active_buf = 0;
++      INIT_LIST_HEAD(&output->pending_bufs);
++      INIT_LIST_HEAD(&output->ready_bufs);
++}
++
++static void vin_buf_add_ready(struct vin_output *output,
++                              struct stfcamss_buffer *buffer)
++{
++      INIT_LIST_HEAD(&buffer->queue);
++      list_add_tail(&buffer->queue, &output->ready_bufs);
++}
++
++static struct stfcamss_buffer *vin_buf_get_ready(struct vin_output *output)
++{
++      struct stfcamss_buffer *buffer = NULL;
++
++      if (!list_empty(&output->ready_bufs)) {
++              buffer = list_first_entry(&output->ready_bufs,
++                                      struct stfcamss_buffer,
++                                      queue);
++              list_del(&buffer->queue);
++      }
++
++      return buffer;
++}
++
++static void vin_buf_add_pending(struct vin_output *output,
++                              struct stfcamss_buffer *buffer)
++{
++      INIT_LIST_HEAD(&buffer->queue);
++      list_add_tail(&buffer->queue, &output->pending_bufs);
++}
++
++static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output)
++{
++      struct stfcamss_buffer *buffer = NULL;
++
++      if (!list_empty(&output->pending_bufs)) {
++              buffer = list_first_entry(&output->pending_bufs,
++                                      struct stfcamss_buffer,
++                                      queue);
++              list_del(&buffer->queue);
++      }
++
++      return buffer;
++}
++
++#ifdef UNUSED_CODE
++static void vin_output_checkpending(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++
++      if (output->state == VIN_OUTPUT_STOPPING) {
++              /* Release last buffer when hw is idle */
++              if (output->last_buffer) {
++                      // vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
++                      //              VB2_BUF_STATE_DONE);
++                      vin_buf_add_pending(output, output->last_buffer);
++                      output->last_buffer = NULL;
++              }
++              output->state = VIN_OUTPUT_IDLE;
++
++              /* Buffers received in stopping state are queued in */
++              /* dma pending queue, start next capture here */
++              output->buf[0] = vin_buf_get_pending(output);
++#ifdef VIN_TWO_BUFFER
++              if (line->id == VIN_LINE_WR)
++                      output->buf[1] = vin_buf_get_pending(output);
++#endif
++
++              if (!output->buf[0] && output->buf[1]) {
++                      output->buf[0] = output->buf[1];
++                      output->buf[1] = NULL;
++              }
++
++              if (output->buf[0])
++                      output->state = VIN_OUTPUT_SINGLE;
++
++#ifdef VIN_TWO_BUFFER
++              if (output->buf[1] && line->id == VIN_LINE_WR)
++                      output->state = VIN_OUTPUT_CONTINUOUS;
++#endif
++              vin_output_init_addrs(line);
++      }
++}
++#endif
++
++static void vin_buf_update_on_last(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++
++      switch (output->state) {
++      case VIN_OUTPUT_CONTINUOUS:
++              output->state = VIN_OUTPUT_SINGLE;
++              output->active_buf = !output->active_buf;
++              break;
++      case VIN_OUTPUT_SINGLE:
++              output->state = VIN_OUTPUT_STOPPING;
++              break;
++      default:
++              st_err_ratelimited(ST_VIN,
++                              "Last buff in wrong state! %d\n",
++                              output->state);
++              break;
++      }
++}
++
++static void vin_buf_update_on_next(struct vin_line *line)
++{
++      struct vin_output *output = &line->output;
++
++      switch (output->state) {
++      case VIN_OUTPUT_CONTINUOUS:
++              output->active_buf = !output->active_buf;
++              break;
++      case VIN_OUTPUT_SINGLE:
++      default:
++#ifdef VIN_TWO_BUFFER
++              if (line->id == VIN_LINE_WR)
++                      st_err_ratelimited(ST_VIN,
++                              "Next buf in wrong state! %d\n",
++                              output->state);
++#endif
++              break;
++      }
++}
++
++static void vin_buf_update_on_new(struct vin_line *line,
++                              struct vin_output *output,
++                              struct stfcamss_buffer *new_buf)
++{
++#ifdef VIN_TWO_BUFFER
++      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++      int inactive_idx;
++#endif
++
++      switch (output->state) {
++      case VIN_OUTPUT_SINGLE:
++#ifdef VIN_TWO_BUFFER
++              int inactive_idx = !output->active_buf;
++
++              if (!output->buf[inactive_idx] && line->id == VIN_LINE_WR) {
++                      output->buf[inactive_idx] = new_buf;
++                      if (inactive_idx)
++                              vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev,
++                                              output->buf[1]->addr[0]);
++                      else
++                              vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev,
++                                              output->buf[0]->addr[0]);
++                      output->state = VIN_OUTPUT_CONTINUOUS;
++
++              } else {
++                      vin_buf_add_pending(output, new_buf);
++                      if (line->id == VIN_LINE_WR)
++                              st_warn(ST_VIN, "Inactive buffer is busy\n");
++              }
++#else
++              vin_buf_add_pending(output, new_buf);
++#endif
++              break;
++      case VIN_OUTPUT_IDLE:
++              st_warn(ST_VIN, "Output idle buffer set!\n");
++              if (!output->buf[0]) {
++                      output->buf[0] = new_buf;
++                      vin_output_init_addrs(line);
++                      output->state = VIN_OUTPUT_SINGLE;
++              } else {
++                      vin_buf_add_pending(output, new_buf);
++                      st_warn(ST_VIN, "Output idle with buffer set!\n");
++              }
++              break;
++      case VIN_OUTPUT_STOPPING:
++              if (output->last_buffer) {
++                      output->buf[output->active_buf] = output->last_buffer;
++                      output->last_buffer = NULL;
++              } else
++                      st_err(ST_VIN,  "stop state lost lastbuffer!\n");
++              output->state = VIN_OUTPUT_SINGLE;
++              // vin_output_checkpending(line);
++              vin_buf_add_pending(output, new_buf);
++              break;
++      case VIN_OUTPUT_CONTINUOUS:
++      default:
++              vin_buf_add_pending(output, new_buf);
++              break;
++      }
++}
++
++static void vin_buf_flush(struct vin_output *output,
++                              enum vb2_buffer_state state)
++{
++      struct stfcamss_buffer *buf;
++      struct stfcamss_buffer *t;
++
++      list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
++              vb2_buffer_done(&buf->vb.vb2_buf, state);
++              list_del(&buf->queue);
++      }
++      list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) {
++              vb2_buffer_done(&buf->vb.vb2_buf, state);
++              list_del(&buf->queue);
++      }
++}
++
++static void vin_buffer_done(struct vin_line *line, struct vin_params *params)
++{
++      struct stfcamss_buffer *ready_buf;
++      struct vin_output *output = &line->output;
++      unsigned long flags;
++      u64 ts = ktime_get_ns();
++      struct v4l2_event event = {
++              .type = V4L2_EVENT_FRAME_SYNC,
++      };
++
++      if (output->state == VIN_OUTPUT_OFF
++              || output->state == VIN_OUTPUT_RESERVED)
++              return;
++
++      spin_lock_irqsave(&line->output_lock, flags);
++
++      while ((ready_buf = vin_buf_get_ready(output))) {
++              if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
++                      event.u.frame_sync.frame_sequence = output->sequence;
++                      v4l2_event_queue(line->subdev.devnode, &event);
++              }
++
++              ready_buf->vb.vb2_buf.timestamp = ts;
++              ready_buf->vb.sequence = output->sequence++;
++
++              /* The stf_isp_ctrl currently buffered with mmap,
++               * which will not update cache by default.
++               * Flush L2 cache to make sure data is updated.
++               */
++              if (ready_buf->vb.vb2_buf.memory == VB2_MEMORY_MMAP)
++                      sifive_l2_flush64_range(ready_buf->addr[0], ready_buf->sizeimage);
++
++              vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
++      }
++
++      spin_unlock_irqrestore(&line->output_lock, flags);
++}
++
++static void vin_change_buffer(struct vin_line *line)
++{
++      struct stfcamss_buffer *ready_buf;
++      struct vin_output *output = &line->output;
++      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++      dma_addr_t *new_addr;
++      unsigned long flags;
++      u32 active_index;
++      int scd_type;
++
++      if (output->state == VIN_OUTPUT_OFF
++              || output->state == VIN_OUTPUT_STOPPING
++              || output->state == VIN_OUTPUT_RESERVED
++              || output->state == VIN_OUTPUT_IDLE)
++              return;
++
++      spin_lock_irqsave(&line->output_lock, flags);
++
++      active_index = output->active_buf;
++
++      ready_buf = output->buf[active_index];
++      if (!ready_buf) {
++              st_err_ratelimited(ST_VIN,
++                                      "Missing ready buf %d %d!\n",
++                                      active_index, output->state);
++              active_index = !active_index;
++              ready_buf = output->buf[active_index];
++              if (!ready_buf) {
++                      st_err_ratelimited(ST_VIN,
++                                      "Missing ready buf 2 %d %d!\n",
++                                      active_index, output->state);
++                      goto out_unlock;
++              }
++      }
++
++      /* Get next buffer */
++      output->buf[active_index] = vin_buf_get_pending(output);
++      if (!output->buf[active_index]) {
++              /* No next buffer - set same address */
++              new_addr = ready_buf->addr;
++              vin_buf_update_on_last(line);
++      } else {
++              new_addr = output->buf[active_index]->addr;
++              vin_buf_update_on_next(line);
++      }
++
++      if (output->state == VIN_OUTPUT_STOPPING)
++              output->last_buffer = ready_buf;
++      else {
++              switch (stf_vin_map_isp_line(line->id)) {
++              case STF_ISP_LINE_SRC:
++                      vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev,
++                              new_addr[0], new_addr[1]);
++                      break;
++              case STF_ISP_LINE_SRC_SS0:
++                      vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev,
++                              new_addr[0], new_addr[1]);
++                      break;
++              case STF_ISP_LINE_SRC_SS1:
++                      vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev,
++                              new_addr[0], new_addr[1]);
++                      break;
++              case STF_ISP_LINE_SRC_ITIW:
++                      vin_dev->hw_ops->vin_isp_set_itiw_addr(vin_dev,
++                              new_addr[0], new_addr[1]);
++                      break;
++              case STF_ISP_LINE_SRC_ITIR:
++                      vin_dev->hw_ops->vin_isp_set_itir_addr(vin_dev,
++                              new_addr[0], new_addr[1]);
++                      break;
++              case STF_ISP_LINE_SRC_RAW:
++                      vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, new_addr[0]);
++                      break;
++              case STF_ISP_LINE_SRC_SCD_Y:
++                      scd_type = vin_dev->hw_ops->vin_isp_get_scd_type(vin_dev);
++                      ready_buf->vb.flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
++                      if (scd_type == AWB_TYPE)
++                              ready_buf->vb.flags |= V4L2_BUF_FLAG_PFRAME;
++                      else
++                              ready_buf->vb.flags |= V4L2_BUF_FLAG_BFRAME;
++                      if (!output->frame_skip) {
++                              output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
++                              scd_type = scd_type == AWB_TYPE ? OECF_TYPE : AWB_TYPE;
++                      } else {
++                              output->frame_skip--;
++                              scd_type = scd_type == AWB_TYPE ? AWB_TYPE : OECF_TYPE;
++                      }
++                      vin_dev->hw_ops->vin_isp_set_scd_addr(vin_dev,
++                              new_addr[0], new_addr[1], scd_type);
++                      break;
++              default:
++                      if (line->id == VIN_LINE_WR) {
++#ifdef VIN_TWO_BUFFER
++                              if (active_index)
++                                      vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev,
++                                                      new_addr[0]);
++                              else
++                                      vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev,
++                                                      new_addr[0]);
++#else
++                              vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev,
++                                                      new_addr[0]);
++                              vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev,
++                                                      new_addr[0]);
++#endif
++                      }
++                      break;
++              }
++
++              vin_buf_add_ready(output, ready_buf);
++      }
++
++      spin_unlock_irqrestore(&line->output_lock, flags);
++      return;
++
++out_unlock:
++      spin_unlock_irqrestore(&line->output_lock, flags);
++}
++
++static int vin_queue_buffer(struct stfcamss_video *vid,
++                              struct stfcamss_buffer *buf)
++{
++      struct vin_line *line = container_of(vid, struct vin_line, video_out);
++      struct vin_output *output;
++      unsigned long flags;
++
++
++      output = &line->output;
++
++      spin_lock_irqsave(&line->output_lock, flags);
++
++      vin_buf_update_on_new(line, output, buf);
++
++      spin_unlock_irqrestore(&line->output_lock, flags);
++
++      return 0;
++}
++
++static int vin_flush_buffers(struct stfcamss_video *vid,
++                              enum vb2_buffer_state state)
++{
++      struct vin_line *line = container_of(vid, struct vin_line, video_out);
++      struct vin_output *output = &line->output;
++      unsigned long flags;
++
++      spin_lock_irqsave(&line->output_lock, flags);
++
++      vin_buf_flush(output, state);
++      if (output->buf[0])
++              vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
++
++      if (output->buf[1])
++              vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
++
++      if (output->last_buffer) {
++              vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
++              output->last_buffer = NULL;
++      }
++      output->buf[0] = output->buf[1] = NULL;
++
++      spin_unlock_irqrestore(&line->output_lock, flags);
++      return 0;
++}
++
++static int vin_link_setup(struct media_entity *entity,
++                      const struct media_pad *local,
++                      const struct media_pad *remote, u32 flags)
++{
++      if (flags & MEDIA_LNK_FL_ENABLED)
++              if (media_pad_remote_pad_first(local))
++                      return -EBUSY;
++      return 0;
++}
++
++static int stf_vin_subscribe_event(struct v4l2_subdev *sd,
++                                 struct v4l2_fh *fh,
++                                 struct v4l2_event_subscription *sub)
++{
++      switch (sub->type) {
++      case V4L2_EVENT_FRAME_SYNC:
++              return v4l2_event_subscribe(fh, sub, 0, NULL);
++      default:
++              st_debug(ST_VIN, "unsupport subscribe_event\n");
++              return -EINVAL;
++      }
++}
++
++static const struct v4l2_subdev_core_ops vin_core_ops = {
++      .s_power = vin_set_power,
++      .subscribe_event = stf_vin_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops vin_video_ops = {
++      .s_stream = vin_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops vin_pad_ops = {
++      .enum_mbus_code   = vin_enum_mbus_code,
++      .enum_frame_size  = vin_enum_frame_size,
++      .get_fmt          = vin_get_format,
++      .set_fmt          = vin_set_format,
++};
++
++static const struct v4l2_subdev_ops vin_v4l2_ops = {
++      .core = &vin_core_ops,
++      .video = &vin_video_ops,
++      .pad = &vin_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops vin_v4l2_internal_ops = {
++      .open = vin_init_formats,
++};
++
++static const struct stfcamss_video_ops stfcamss_vin_video_ops = {
++      .queue_buffer = vin_queue_buffer,
++      .flush_buffers = vin_flush_buffers,
++};
++
++static const struct media_entity_operations vin_media_ops = {
++      .link_setup = vin_link_setup,
++      .link_validate = v4l2_subdev_link_validate,
++};
++
++int stf_vin_register(struct stf_vin2_dev *vin_dev, struct v4l2_device *v4l2_dev)
++{
++      struct v4l2_subdev *sd;
++      struct stfcamss_video *video_out;
++      struct media_pad *pads;
++      int ret;
++      int i;
++
++      for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) {
++              char name[32];
++              char *sub_name = get_line_subdevname(i);
++              int is_mp;
++
++#ifdef        STF_CAMSS_SKIP_ITI
++              if ((stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC_ITIW) ||
++                      (stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC_ITIR))
++                      continue;
++#endif
++              is_mp = (stf_vin_map_isp_line(i) == STF_ISP_LINE_SRC) ? true : false;
++              is_mp = false;
++              sd = &vin_dev->line[i].subdev;
++              pads = vin_dev->line[i].pads;
++              video_out = &vin_dev->line[i].video_out;
++              video_out->id = i;
++
++              v4l2_subdev_init(sd, &vin_v4l2_ops);
++              sd->internal_ops = &vin_v4l2_internal_ops;
++              sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
++              snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
++                      STF_VIN_NAME, 0, sub_name);
++              v4l2_set_subdevdata(sd, &vin_dev->line[i]);
++
++              ret = vin_init_formats(sd, NULL);
++              if (ret < 0) {
++                      st_err(ST_VIN, "Failed to init format: %d\n", ret);
++                      goto err_init;
++              }
++
++              pads[STF_VIN_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
++              pads[STF_VIN_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
++
++              sd->entity.function =
++                      MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++              sd->entity.ops = &vin_media_ops;
++              ret = media_entity_pads_init(&sd->entity,
++                              STF_VIN_PADS_NUM, pads);
++              if (ret < 0) {
++                      st_err(ST_VIN, "Failed to init media entity: %d\n", ret);
++                      goto err_init;
++              }
++
++              ret = v4l2_device_register_subdev(v4l2_dev, sd);
++              if (ret < 0) {
++                      st_err(ST_VIN, "Failed to register subdev: %d\n", ret);
++                      goto err_reg_subdev;
++              }
++
++              video_out->ops = &stfcamss_vin_video_ops;
++              video_out->bpl_alignment = 16 * 8;
++
++              snprintf(name, ARRAY_SIZE(name), "%s_%s%d",
++                      sd->name, "video", i);
++              ret = stf_video_register(video_out, v4l2_dev, name, is_mp);
++              if (ret < 0) {
++                      st_err(ST_VIN, "Failed to register video node: %d\n",
++                                      ret);
++                      goto err_vid_reg;
++              }
++
++              ret = media_create_pad_link(
++                      &sd->entity, STF_VIN_PAD_SRC,
++                      &video_out->vdev.entity, 0,
++                      MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++              if (ret < 0) {
++                      st_err(ST_VIN, "Failed to link %s->%s entities: %d\n",
++                              sd->entity.name, video_out->vdev.entity.name,
++                              ret);
++                      goto err_create_link;
++              }
++      }
++
++      return 0;
++
++err_create_link:
++      stf_video_unregister(video_out);
++err_vid_reg:
++      v4l2_device_unregister_subdev(sd);
++err_reg_subdev:
++      media_entity_cleanup(&sd->entity);
++err_init:
++      for (i--; i >= 0; i--) {
++              sd = &vin_dev->line[i].subdev;
++              video_out = &vin_dev->line[i].video_out;
++
++              stf_video_unregister(video_out);
++              v4l2_device_unregister_subdev(sd);
++              media_entity_cleanup(&sd->entity);
++      }
++      return ret;
++}
++
++int stf_vin_unregister(struct stf_vin2_dev *vin_dev)
++{
++      struct v4l2_subdev *sd;
++      struct stfcamss_video *video_out;
++      int i;
++
++      mutex_destroy(&vin_dev->power_lock);
++      for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++)
++              mutex_destroy(&vin_dev->dummy_buffer[i].stream_lock);
++
++      for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) {
++              sd = &vin_dev->line[i].subdev;
++              video_out = &vin_dev->line[i].video_out;
++
++              stf_video_unregister(video_out);
++              v4l2_device_unregister_subdev(sd);
++              media_entity_cleanup(&sd->entity);
++              mutex_destroy(&vin_dev->line[i].stream_lock);
++              mutex_destroy(&vin_dev->line[i].power_lock);
++      }
++      return 0;
++}
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.h
+@@ -0,0 +1,182 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STF_VIN_H
++#define STF_VIN_H
++
++#include <media/v4l2-device.h>
++#include <media/v4l2-subdev.h>
++#include <linux/spinlock_types.h>
++#include <video/stf-vin.h>
++#include <linux/platform_device.h>
++
++#include "stf_video.h"
++
++#define STF_VIN_NAME "stf_vin"
++
++#define STF_VIN_PAD_SINK   0
++#define STF_VIN_PAD_SRC    1
++#define STF_VIN_PADS_NUM   2
++
++struct vin2_format {
++      u32 code;
++      u8 bpp;
++};
++
++struct vin2_format_table {
++      const struct vin2_format *fmts;
++      int nfmts;
++};
++
++enum vin_output_state {
++      VIN_OUTPUT_OFF,
++      VIN_OUTPUT_RESERVED,
++      VIN_OUTPUT_SINGLE,
++      VIN_OUTPUT_CONTINUOUS,
++      VIN_OUTPUT_IDLE,
++      VIN_OUTPUT_STOPPING
++};
++
++struct vin_output {
++      int active_buf;
++      struct stfcamss_buffer *buf[2];
++      struct stfcamss_buffer *last_buffer;
++      struct list_head pending_bufs;
++      struct list_head ready_bufs;
++      enum vin_output_state state;
++      unsigned int sequence;
++      unsigned int frame_skip;
++};
++
++/* The vin output lines include all isp controller lines,
++ * and one vin_wr output line.
++ */
++enum vin_line_id {
++      VIN_LINE_NONE = -1,
++      VIN_LINE_WR = 0,
++      VIN_LINE_ISP = 1,
++      VIN_LINE_ISP_SS0 = 2,
++      VIN_LINE_ISP_SS1 = 3,
++      VIN_LINE_ISP_ITIW = 4,
++      VIN_LINE_ISP_ITIR = 5,
++      VIN_LINE_ISP_RAW = 6,
++      VIN_LINE_ISP_SCD_Y = 7,
++      VIN_LINE_MAX = 8,
++};
++
++enum subdev_type;
++
++struct vin_line {
++      enum subdev_type sdev_type;  // must be frist
++      enum vin_line_id id;
++      struct v4l2_subdev subdev;
++      struct media_pad pads[STF_VIN_PADS_NUM];
++      struct v4l2_mbus_framefmt fmt[STF_VIN_PADS_NUM];
++      struct stfcamss_video video_out;
++      struct mutex stream_lock;
++      int stream_count;
++      struct mutex power_lock;
++      int power_count;
++      struct vin_output output;
++      spinlock_t output_lock;
++      const struct vin2_format *formats;
++      unsigned int nformats;
++#ifdef CONFIG_PM_SLEEP
++      int pm_stream_count;
++      int pm_power_count;
++#endif
++};
++
++struct stf_vin2_dev;
++
++struct vin_hw_ops {
++      int (*vin_clk_enable)(struct stf_vin2_dev *vin_dev);
++      int (*vin_clk_disable)(struct stf_vin2_dev *vin_dev);
++      int (*vin_config_set)(struct stf_vin2_dev *vin_dev);
++      int (*vin_wr_stream_set)(struct stf_vin2_dev *vin_dev, int on);
++      void (*vin_wr_irq_enable)(struct stf_vin2_dev *vin_dev, int enable);
++      void (*vin_power_on)(struct stf_vin2_dev *vin_dev, int on);
++      void (*wr_rd_set_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t wr_addr, dma_addr_t rd_addr);
++      void (*vin_wr_set_ping_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t addr);
++      void (*vin_wr_set_pong_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t addr);
++      void (*vin_wr_get_ping_pong_status)(struct stf_vin2_dev *vin_dev);
++      void (*vin_isp_set_yuv_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t y_addr, dma_addr_t uv_addr);
++      void (*vin_isp_set_raw_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t raw_addr);
++      void (*vin_isp_set_ss0_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t y_addr, dma_addr_t uv_addr);
++      void (*vin_isp_set_ss1_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t y_addr, dma_addr_t uv_addr);
++      void (*vin_isp_set_itiw_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t y_addr, dma_addr_t uv_addr);
++      void (*vin_isp_set_itir_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t y_addr, dma_addr_t uv_addr);
++      void (*vin_isp_set_scd_addr)(struct stf_vin2_dev *vin_dev,
++                      dma_addr_t yhist_addr,
++                      dma_addr_t scd_addr, int scd_type);
++      int (*vin_isp_get_scd_type)(struct stf_vin2_dev *vin_dev);
++      irqreturn_t (*vin_wr_irq_handler)(int irq, void *priv);
++      irqreturn_t (*vin_isp_irq_handler)(int irq, void *priv);
++      irqreturn_t (*vin_isp_csi_irq_handler)(int irq, void *priv);
++      irqreturn_t (*vin_isp_scd_irq_handler)(int irq, void *priv);
++      irqreturn_t (*vin_isp_irq_csiline_handler)(int irq, void *priv);
++      void (*isr_buffer_done)(struct vin_line *line,
++                      struct vin_params *params);
++      void (*isr_change_buffer)(struct vin_line *line);
++};
++
++#define ISP_DUMMY_BUFFER_NUMS  STF_ISP_PAD_MAX
++#define VIN_DUMMY_BUFFER_NUMS  1
++
++enum {
++      STF_DUMMY_VIN,
++      STF_DUMMY_ISP,
++      STF_DUMMY_MODULE_NUMS,
++};
++
++struct vin_dummy_buffer {
++      dma_addr_t paddr[3];
++      void *vaddr;
++      u32 buffer_size;
++      u32 width;
++      u32 height;
++      u32 mcode;
++};
++
++struct dummy_buffer {
++      struct vin_dummy_buffer *buffer;
++      u32 nums;
++      struct mutex stream_lock;
++      int stream_count;
++      atomic_t frame_skip;
++};
++
++struct stf_vin2_dev {
++      struct stfcamss *stfcamss;
++      struct vin_line line[VIN_LINE_MAX];
++      struct dummy_buffer dummy_buffer[STF_DUMMY_MODULE_NUMS];
++      struct vin_hw_ops *hw_ops;
++      atomic_t ref_count;
++      struct mutex power_lock;
++      int power_count;
++};
++
++extern void sifive_l2_flush64_range(unsigned long start, unsigned long len);
++extern int stf_vin_subdev_init(struct stfcamss *stfcamss);
++extern int stf_vin_register(struct stf_vin2_dev *vin_dev,
++              struct v4l2_device *v4l2_dev);
++extern int stf_vin_unregister(struct stf_vin2_dev *vin_dev);
++
++extern struct vin_hw_ops vin_ops;
++extern void dump_vin_reg(void *__iomem regbase);
++extern enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line,
++              enum isp_pad_id def);
++
++#endif /* STF_VIN_H */
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
+@@ -0,0 +1,433 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include "stfcamss.h"
++#include <linux/of_graph.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-subdev.h>
++
++static void vin_intr_clear(void __iomem *sysctrl_base)
++{
++      reg_set_bit(sysctrl_base, SYSCONSAIF_SYSCFG_28,
++              U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++              0x1);
++      reg_set_bit(sysctrl_base, SYSCONSAIF_SYSCFG_28,
++              U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++              0x0);
++}
++
++static irqreturn_t stf_vin_wr_irq_handler(int irq, void *priv)
++{
++      static struct vin_params params;
++      struct stf_vin2_dev *vin_dev = priv;
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      struct dummy_buffer *dummy_buffer =
++                      &vin_dev->dummy_buffer[STF_DUMMY_VIN];
++
++      if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) {
++              vin_dev->hw_ops->isr_change_buffer(&vin_dev->line[VIN_LINE_WR]);
++              vin_dev->hw_ops->isr_buffer_done(&vin_dev->line[VIN_LINE_WR], &params);
++      }
++
++      vin_intr_clear(vin->sysctrl_base);
++
++      return IRQ_HANDLED;
++}
++
++static  void __iomem *stf_vin_get_ispbase(struct stf_vin_dev *vin)
++{
++      void __iomem *base = vin->isp_base;
++
++      return base;
++}
++
++static irqreturn_t stf_vin_isp_irq_handler(int irq, void *priv)
++{
++      static struct vin_params params;
++      struct stf_vin2_dev *vin_dev = priv;
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase;
++      u32 int_status, value;
++
++      ispbase = stf_vin_get_ispbase(vin);
++
++      int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++
++      if (int_status & BIT(24)) {
++              if ((int_status & BIT(11)))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP_SS0], &params);
++
++              if ((int_status & BIT(12)))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP_SS1], &params);
++
++              if ((int_status & BIT(20)))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP], &params);
++
++              value = reg_read(ispbase, ISP_REG_ITIDPSR);
++              if ((value & BIT(17)))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP_ITIW], &params);
++              if ((value & BIT(16)))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP_ITIR], &params);
++
++#ifndef ISP_USE_CSI_AND_SC_DONE_INTERRUPT
++              if (int_status & BIT(25))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP_RAW], &params);
++
++              if (int_status & BIT(26))
++                      vin_dev->hw_ops->isr_buffer_done(
++                              &vin_dev->line[VIN_LINE_ISP_SCD_Y], &params);
++
++              /* clear interrupt */
++              reg_write(ispbase, ISP_REG_ISP_CTRL_0, (int_status & ~EN_INT_ALL)
++                              | EN_INT_ISP_DONE | EN_INT_CSI_DONE | EN_INT_SC_DONE);
++#else
++              /* clear interrupt */
++              reg_write(ispbase, ISP_REG_ISP_CTRL_0,
++                      (int_status & ~EN_INT_ALL) | EN_INT_ISP_DONE);
++#endif
++      } else
++              st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t stf_vin_isp_csi_irq_handler(int irq, void *priv)
++{
++      static struct vin_params params;
++      struct stf_vin2_dev *vin_dev = priv;
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase;
++      u32 int_status;
++
++      ispbase = stf_vin_get_ispbase(vin);
++
++      int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++
++      if (int_status & BIT(25)) {
++              vin_dev->hw_ops->isr_buffer_done(
++                      &vin_dev->line[VIN_LINE_ISP_RAW], &params);
++
++              /* clear interrupt */
++              reg_write(ispbase, ISP_REG_ISP_CTRL_0,
++                      (int_status & ~EN_INT_ALL) | EN_INT_CSI_DONE);
++      } else
++              st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t stf_vin_isp_scd_irq_handler(int irq, void *priv)
++{
++      static struct vin_params params;
++      struct stf_vin2_dev *vin_dev = priv;
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase;
++      u32 int_status;
++
++      ispbase = stf_vin_get_ispbase(vin);
++
++      int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++
++      if (int_status & BIT(26)) {
++              vin_dev->hw_ops->isr_buffer_done(
++                      &vin_dev->line[VIN_LINE_ISP_SCD_Y], &params);
++
++              /* clear interrupt */
++              reg_write(ispbase, ISP_REG_ISP_CTRL_0, (int_status & ~EN_INT_ALL) | EN_INT_SC_DONE);
++      } else
++              st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t stf_vin_isp_irq_csiline_handler(int irq, void *priv)
++{
++      struct stf_vin2_dev *vin_dev = priv;
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      struct stf_isp_dev *isp_dev;
++      void __iomem *ispbase;
++      u32 int_status, value;
++
++      ispbase = stf_vin_get_ispbase(vin);
++
++      isp_dev = vin_dev->stfcamss->isp_dev;
++
++      int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0);
++      if (int_status & BIT(27)) {
++              struct dummy_buffer *dummy_buffer =
++                      &vin_dev->dummy_buffer[STF_DUMMY_ISP];
++
++              if (!atomic_read(&isp_dev->shadow_count)) {
++                      if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) {
++                              if ((int_status & BIT(11)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP_SS0]);
++                              if ((int_status & BIT(12)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP_SS1]);
++                              if ((int_status & BIT(20)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP]);
++
++                              value = reg_read(ispbase, ISP_REG_ITIDPSR);
++                              if ((value & BIT(17)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP_ITIW]);
++                              if ((value & BIT(16)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP_ITIR]);
++
++                              value = reg_read(ispbase, ISP_REG_CSI_MODULE_CFG);
++                              if ((value & BIT(19)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP_RAW]);
++                              if ((value & BIT(17)))
++                                      vin_dev->hw_ops->isr_change_buffer(
++                                              &vin_dev->line[VIN_LINE_ISP_SCD_Y]);
++                      }
++
++                      // shadow update
++                      reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, 0x30000, 0x30000);
++                      reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, BIT(1) | BIT(0), 0x3);
++              } else {
++                      st_warn(ST_VIN, "isp shadow_lock locked. skip this frame\n");
++              }
++
++              /* clear interrupt */
++              reg_write(ispbase, ISP_REG_ISP_CTRL_0,
++                      (int_status & ~EN_INT_ALL) | EN_INT_LINE_INT);
++      } else
++              st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__);
++
++      return IRQ_HANDLED;
++}
++
++static int stf_vin_clk_enable(struct stf_vin2_dev *vin_dev)
++{
++      struct stfcamss *stfcamss = vin_dev->stfcamss;
++
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_PCLK].clk);
++      clk_set_rate(stfcamss->sys_clk[STFCLK_APB_FUNC].clk, 49500000);
++      clk_set_rate(stfcamss->sys_clk[STFCLK_SYS_CLK].clk, 297000000);
++
++      reset_control_deassert(stfcamss->sys_rst[STFRST_PCLK].rstc);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_SYS_CLK].rstc);
++
++      return 0;
++}
++
++
++static int stf_vin_clk_disable(struct stf_vin2_dev *vin_dev)
++{
++      struct stfcamss *stfcamss = vin_dev->stfcamss;
++
++      reset_control_assert(stfcamss->sys_rst[STFRST_PCLK].rstc);
++      reset_control_assert(stfcamss->sys_rst[STFRST_SYS_CLK].rstc);
++
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_PCLK].clk);
++
++      return 0;
++}
++
++static int stf_vin_config_set(struct stf_vin2_dev *vin_dev)
++{
++      return 0;
++}
++
++static int stf_vin_wr_stream_set(struct stf_vin2_dev *vin_dev, int on)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++      //make the axiwr alway on
++      if (on)
++              reg_set(vin->sysctrl_base, SYSCONSAIF_SYSCFG_20, U0_VIN_CNFG_AXIWR0_EN);
++
++      return 0;
++}
++
++static void stf_vin_wr_irq_enable(struct stf_vin2_dev *vin_dev,
++              int enable)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      unsigned int value = 0;
++
++      if (enable) {
++              value = ~(0x1 << 1);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++                      U0_VIN_CNFG_AXIWR0_MASK,
++                      value);
++      } else {
++              /* clear vin interrupt */
++              value = 0x1 << 1;
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++                      U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++                      0x1);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++                      U0_VIN_CNFG_AXIWR0_INTR_CLEAN,
++                      0x0);
++              reg_set_bit(vin->sysctrl_base, SYSCONSAIF_SYSCFG_28,
++                      U0_VIN_CNFG_AXIWR0_MASK,
++                      value);
++      }
++}
++
++static void stf_vin_wr_rd_set_addr(struct stf_vin2_dev *vin_dev,
++              dma_addr_t wr_addr, dma_addr_t rd_addr)
++{
++#ifdef UNUSED_CODE
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++      /* set the start address*/
++      reg_write(vin->sysctrl_base,
++                      SYSCTRL_VIN_WR_START_ADDR, (long)wr_addr);
++      reg_write(vin->sysctrl_base,
++                      SYSCTRL_VIN_RD_END_ADDR, (long)rd_addr);
++#endif
++}
++
++void stf_vin_wr_set_ping_addr(struct stf_vin2_dev *vin_dev,
++              dma_addr_t addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++      /* set the start address */
++      reg_write(vin->sysctrl_base,  SYSCONSAIF_SYSCFG_24, (long)addr);
++}
++
++void stf_vin_wr_set_pong_addr(struct stf_vin2_dev *vin_dev, dma_addr_t addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++
++      /* set the start address */
++      reg_write(vin->sysctrl_base, SYSCONSAIF_SYSCFG_32, (long)addr);
++}
++
++void stf_vin_isp_set_yuv_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_write(ispbase, ISP_REG_Y_PLANE_START_ADDR, y_addr);
++      reg_write(ispbase, ISP_REG_UV_PLANE_START_ADDR, uv_addr);
++      // reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(0), 1);
++}
++
++void stf_vin_isp_set_raw_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t raw_addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_write(ispbase, ISP_REG_DUMP_CFG_0, raw_addr);
++}
++
++void stf_vin_isp_set_ss0_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_write(ispbase, ISP_REG_SS0AY, y_addr);
++      reg_write(ispbase, ISP_REG_SS0AUV, uv_addr);
++}
++
++void stf_vin_isp_set_ss1_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_write(ispbase, ISP_REG_SS1AY, y_addr);
++      reg_write(ispbase, ISP_REG_SS1AUV, uv_addr);
++}
++
++void stf_vin_isp_set_itiw_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_write(ispbase, ISP_REG_ITIDWYSAR, y_addr);
++      reg_write(ispbase, ISP_REG_ITIDWUSAR, uv_addr);
++}
++
++void stf_vin_isp_set_itir_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t y_addr, dma_addr_t uv_addr)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_write(ispbase, ISP_REG_ITIDRYSAR, y_addr);
++      reg_write(ispbase, ISP_REG_ITIDRUSAR, uv_addr);
++}
++
++int stf_vin_isp_get_scd_type(struct stf_vin2_dev *vin_dev)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      return (reg_read(ispbase, ISP_REG_SC_CFG_1) & (0x3 << 30)) >> 30;
++}
++
++void stf_vin_isp_set_scd_addr(struct stf_vin2_dev *vin_dev,
++                              dma_addr_t yhist_addr, dma_addr_t scd_addr, int scd_type)
++{
++      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++      void __iomem *ispbase = stf_vin_get_ispbase(vin);
++
++      reg_set_bit(ispbase, ISP_REG_SC_CFG_1, 0x3 << 30, scd_type << 30);
++      reg_write(ispbase, ISP_REG_SCD_CFG_0, scd_addr);
++      reg_write(ispbase, ISP_REG_YHIST_CFG_4, yhist_addr);
++}
++
++void dump_vin_reg(void *__iomem regbase)
++{
++      st_debug(ST_VIN, "DUMP VIN register:\n");
++      print_reg(ST_VIN, regbase, 0x00);
++      print_reg(ST_VIN, regbase, 0x04);
++      print_reg(ST_VIN, regbase, 0x08);
++      print_reg(ST_VIN, regbase, 0x0c);
++      print_reg(ST_VIN, regbase, 0x10);
++      print_reg(ST_VIN, regbase, 0x14);
++      print_reg(ST_VIN, regbase, 0x18);
++      print_reg(ST_VIN, regbase, 0x1c);
++      print_reg(ST_VIN, regbase, 0x20);
++      print_reg(ST_VIN, regbase, 0x24);
++      print_reg(ST_VIN, regbase, 0x28);
++}
++
++struct vin_hw_ops vin_ops = {
++      .vin_clk_enable        = stf_vin_clk_enable,
++      .vin_clk_disable       = stf_vin_clk_disable,
++      .vin_config_set        = stf_vin_config_set,
++      .vin_wr_stream_set     = stf_vin_wr_stream_set,
++      .vin_wr_irq_enable     = stf_vin_wr_irq_enable,
++      .wr_rd_set_addr        = stf_vin_wr_rd_set_addr,
++      .vin_wr_set_ping_addr  = stf_vin_wr_set_ping_addr,
++      .vin_wr_set_pong_addr  = stf_vin_wr_set_pong_addr,
++      .vin_isp_set_yuv_addr  = stf_vin_isp_set_yuv_addr,
++      .vin_isp_set_raw_addr  = stf_vin_isp_set_raw_addr,
++      .vin_isp_set_ss0_addr  = stf_vin_isp_set_ss0_addr,
++      .vin_isp_set_ss1_addr  = stf_vin_isp_set_ss1_addr,
++      .vin_isp_set_itiw_addr  = stf_vin_isp_set_itiw_addr,
++      .vin_isp_set_itir_addr  = stf_vin_isp_set_itir_addr,
++      .vin_isp_set_scd_addr  = stf_vin_isp_set_scd_addr,
++      .vin_isp_get_scd_type  = stf_vin_isp_get_scd_type,
++      .vin_wr_irq_handler    = stf_vin_wr_irq_handler,
++      .vin_isp_irq_handler   = stf_vin_isp_irq_handler,
++      .vin_isp_csi_irq_handler   = stf_vin_isp_csi_irq_handler,
++      .vin_isp_scd_irq_handler   = stf_vin_isp_scd_irq_handler,
++      .vin_isp_irq_csiline_handler   = stf_vin_isp_irq_csiline_handler,
++};
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stfcamss.c
+@@ -0,0 +1,1369 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/dmaengine.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_reserved_mem.h>
++#include <linux/of_graph.h>
++#include <linux/of_address.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/io.h>
++#include <linux/dma-mapping.h>
++#include <linux/uaccess.h>
++#include <linux/mfd/syscon.h>
++
++#include <linux/videodev2.h>
++
++#include <media/media-device.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-mc.h>
++#include <media/v4l2-fwnode.h>
++#include <linux/debugfs.h>
++
++#include "stfcamss.h"
++
++#ifdef STF_DEBUG
++unsigned int stdbg_level = ST_DEBUG;
++unsigned int stdbg_mask = 0x7F;
++#else
++unsigned int stdbg_level = ST_ERR;
++unsigned int stdbg_mask = 0x7F;
++#endif
++EXPORT_SYMBOL_GPL(stdbg_level);
++EXPORT_SYMBOL_GPL(stdbg_mask);
++
++static const struct reg_name mem_reg_name[] = {
++      {"csi2rx"},
++      {"vclk"},
++      {"vrst"},
++      {"sctrl"},
++      {"isp"},
++      {"trst"},
++      {"pmu"},
++      {"syscrg"},
++};
++
++static struct clk_bulk_data stfcamss_clocks[] = {
++      { .id = "clk_apb_func" },
++      { .id = "clk_pclk" },
++      { .id = "clk_sys_clk" },
++      { .id = "clk_wrapper_clk_c" },
++      { .id = "clk_dvp_inv" },
++      { .id = "clk_axiwr" },
++      { .id = "clk_mipi_rx0_pxl" },
++      { .id = "clk_pixel_clk_if0" },
++      { .id = "clk_pixel_clk_if1" },
++      { .id = "clk_pixel_clk_if2" },
++      { .id = "clk_pixel_clk_if3" },
++      { .id = "clk_m31dphy_cfgclk_in" },
++      { .id = "clk_m31dphy_refclk_in" },
++      { .id = "clk_m31dphy_txclkesc_lan0" },
++      { .id = "clk_ispcore_2x" },
++      { .id = "clk_isp_axi" },
++};
++
++static struct reset_control_bulk_data stfcamss_resets[] = {
++      { .id = "rst_wrapper_p" },
++      { .id = "rst_wrapper_c" },
++      { .id = "rst_pclk" },
++      { .id = "rst_sys_clk" },
++      { .id = "rst_axird" },
++      { .id = "rst_axiwr" },
++      { .id = "rst_pixel_clk_if0" },
++      { .id = "rst_pixel_clk_if1" },
++      { .id = "rst_pixel_clk_if2" },
++      { .id = "rst_pixel_clk_if3" },
++      { .id = "rst_m31dphy_hw" },
++      { .id = "rst_m31dphy_b09_always_on" },
++      { .id = "rst_isp_top_n" },
++      { .id = "rst_isp_top_axi" },
++};
++
++int stfcamss_get_mem_res(struct platform_device *pdev, struct stf_vin_dev *vin)
++{
++      struct device *dev = &pdev->dev;
++      struct resource *res;
++      char *name;
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(mem_reg_name); i++) {
++              name = (char *)(&mem_reg_name[i]);
++              res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
++
++              if (!res)
++                      return -EINVAL;
++
++              if (!strcmp(name, "csi2rx")) {
++                      vin->csi2rx_base = devm_ioremap_resource(dev, res);
++                      if (IS_ERR(vin->csi2rx_base))
++                              return PTR_ERR(vin->csi2rx_base);
++              } else if (!strcmp(name, "vclk")) {
++                      vin->clkgen_base = ioremap(res->start, resource_size(res));
++                      if (!vin->clkgen_base)
++                              return -ENOMEM;
++              } else if (!strcmp(name, "vrst")) {
++                      vin->rstgen_base = devm_ioremap_resource(dev, res);
++                      if (IS_ERR(vin->rstgen_base))
++                              return PTR_ERR(vin->rstgen_base);
++              } else if (!strcmp(name, "sctrl")) {
++                      vin->sysctrl_base = devm_ioremap_resource(dev, res);
++                      if (IS_ERR(vin->sysctrl_base))
++                              return PTR_ERR(vin->sysctrl_base);
++              } else if (!strcmp(name, "isp")) {
++                      vin->isp_base = devm_ioremap_resource(dev, res);
++                      if (IS_ERR(vin->isp_base))
++                              return PTR_ERR(vin->isp_base);
++              } else if (!strcmp(name, "trst")) {
++                      vin->vin_top_rstgen_base = devm_ioremap_resource(dev, res);
++                      if (IS_ERR(vin->vin_top_rstgen_base))
++                              return PTR_ERR(vin->vin_top_rstgen_base);
++              } else if (!strcmp(name, "pmu")) {
++                      vin->pmu_test = ioremap(res->start, resource_size(res));
++                      if (!vin->pmu_test)
++                              return -ENOMEM;
++              } else if (!strcmp(name, "syscrg")) {
++                      vin->sys_crg = ioremap(res->start, resource_size(res));
++                      if (!vin->sys_crg)
++                              return -ENOMEM;
++              } else {
++                      st_err(ST_CAMSS, "Could not match resource name\n");
++              }
++      }
++
++      return 0;
++}
++
++int vin_parse_dt(struct device *dev, struct stf_vin_dev *vin)
++{
++      int ret = 0;
++      struct device_node *np = dev->of_node;
++
++      if (!np)
++              return -EINVAL;
++
++      return ret;
++}
++
++struct media_entity *stfcamss_find_sensor(struct media_entity *entity)
++{
++      struct media_pad *pad;
++
++      while (1) {
++              if (!entity->pads)
++                      return NULL;
++
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      return NULL;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      return NULL;
++
++              entity = pad->entity;
++
++              if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
++                      return entity;
++      }
++}
++
++static int stfcamss_of_parse_endpoint_node(struct device *dev,
++                              struct device_node *node,
++                              struct stfcamss_async_subdev *csd)
++{
++      struct v4l2_fwnode_endpoint vep = { { 0 } };
++      struct v4l2_mbus_config_parallel *parallel_bus = &vep.bus.parallel;
++      struct v4l2_mbus_config_mipi_csi2 *csi2_bus = &vep.bus.mipi_csi2;
++      struct dvp_cfg *dvp = &csd->interface.dvp;
++      struct csi2phy_cfg *csiphy = &csd->interface.csiphy;
++
++      v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
++      st_debug(ST_CAMSS, "%s: vep.base.port = 0x%x, id = 0x%x\n",
++                      __func__, vep.base.port, vep.base.id);
++
++      csd->port = vep.base.port;
++      switch (csd->port) {
++      case DVP_SENSOR_PORT_NUMBER:
++              st_debug(ST_CAMSS, "%s, flags = 0x%x\n", __func__,
++                              parallel_bus->flags);
++              dvp->flags = parallel_bus->flags;
++              dvp->bus_width = parallel_bus->bus_width;
++              dvp->data_shift = parallel_bus->data_shift;
++              break;
++      case CSI2RX_SENSOR_PORT_NUMBER:
++              st_debug(ST_CAMSS, "%s, CSI2 flags = 0x%x\n",
++                              __func__, parallel_bus->flags);
++              csiphy->flags = csi2_bus->flags;
++              memcpy(csiphy->data_lanes,
++                              csi2_bus->data_lanes, csi2_bus->num_data_lanes);
++              csiphy->clock_lane = csi2_bus->clock_lane;
++              csiphy->num_data_lanes = csi2_bus->num_data_lanes;
++              memcpy(csiphy->lane_polarities,
++                              csi2_bus->lane_polarities,
++                              csi2_bus->num_data_lanes + 1);
++              break;
++      default:
++              break;
++      };
++
++      return 0;
++}
++
++static int stfcamss_of_parse_ports(struct stfcamss *stfcamss)
++{
++      struct device *dev = stfcamss->dev;
++      struct device_node *node = NULL;
++      struct device_node *remote = NULL;
++      int ret, num_subdevs = 0;
++
++      for_each_endpoint_of_node(dev->of_node, node) {
++              struct stfcamss_async_subdev *csd;
++
++              if (!of_device_is_available(node))
++                      continue;
++
++              remote = of_graph_get_remote_port_parent(node);
++              if (!remote) {
++                      st_err(ST_CAMSS, "Cannot get remote parent\n");
++                      ret = -EINVAL;
++                      goto err_cleanup;
++              }
++
++              csd = v4l2_async_nf_add_fwnode(&stfcamss->notifier,
++                                             of_fwnode_handle(remote),
++                                             struct stfcamss_async_subdev);
++              of_node_put(remote);
++              if (IS_ERR(csd)) {
++                      ret = PTR_ERR(csd);
++                      goto err_cleanup;
++              }
++
++              ret = stfcamss_of_parse_endpoint_node(dev, node, csd);
++              if (ret < 0)
++                      goto err_cleanup;
++
++              num_subdevs++;
++      }
++
++      return num_subdevs;
++
++err_cleanup:
++      of_node_put(node);
++      return ret;
++}
++
++static int stfcamss_init_subdevices(struct stfcamss *stfcamss)
++{
++      int ret;
++
++      ret = stf_dvp_subdev_init(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to init stf_dvp sub-device: %d\n",
++                      ret);
++              return ret;
++      }
++
++      ret = stf_csiphy_subdev_init(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to init stf_csiphy sub-device: %d\n",
++                      ret);
++              return ret;
++      }
++
++      ret = stf_csi_subdev_init(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to init stf_csi sub-device: %d\n",
++                      ret);
++              return ret;
++      }
++
++      ret = stf_isp_subdev_init(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to init stf_isp sub-device: %d\n",
++                      ret);
++              return ret;
++      }
++
++      ret = stf_vin_subdev_init(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to init stf_vin sub-device: %d\n",
++                      ret);
++              return ret;
++      }
++      return ret;
++}
++
++static int stfcamss_register_subdevices(struct stfcamss *stfcamss)
++{
++      int ret;
++      struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++      struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
++      struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
++      struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
++      struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++
++      ret = stf_dvp_register(dvp_dev, &stfcamss->v4l2_dev);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to register stf dvp%d entity: %d\n",
++                      0, ret);
++              goto err_reg_dvp;
++      }
++
++      ret = stf_csiphy_register(csiphy_dev, &stfcamss->v4l2_dev);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to register stf csiphy%d entity: %d\n",
++                      0, ret);
++              goto err_reg_csiphy;
++      }
++
++      ret = stf_csi_register(csi_dev, &stfcamss->v4l2_dev);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to register stf csi%d entity: %d\n",
++                      0, ret);
++              goto err_reg_csi;
++      }
++
++      ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to register stf isp%d entity: %d\n",
++                      0, ret);
++              goto err_reg_isp;
++      }
++
++      ret = stf_vin_register(vin_dev, &stfcamss->v4l2_dev);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to register vin entity: %d\n",
++                       ret);
++              goto err_reg_vin;
++      }
++
++      ret = media_create_pad_link(
++              &dvp_dev->subdev.entity,
++              STF_DVP_PAD_SRC,
++              &vin_dev->line[VIN_LINE_WR].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->vin entities: %d\n",
++                      dvp_dev->subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &csi_dev->subdev.entity,
++              STF_CSI_PAD_SRC,
++              &vin_dev->line[VIN_LINE_WR].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->vin entities: %d\n",
++                      csi_dev->subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &csiphy_dev->subdev.entity,
++              STF_CSIPHY_PAD_SRC,
++              &csi_dev->subdev.entity,
++              STF_CSI_PAD_SINK,
++              MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      csiphy_dev->subdev.entity.name,
++                      csi_dev->subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC,
++              &vin_dev->line[VIN_LINE_ISP].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC_SS0,
++              &vin_dev->line[VIN_LINE_ISP_SS0].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP_SS0]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC_SS1,
++              &vin_dev->line[VIN_LINE_ISP_SS1].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP_SS1]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++#ifndef       STF_CAMSS_SKIP_ITI
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC_ITIW,
++              &vin_dev->line[VIN_LINE_ISP_ITIW].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP_ITIW]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC_ITIR,
++              &vin_dev->line[VIN_LINE_ISP_ITIR].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP_ITIR]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++#endif
++
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC_RAW,
++              &vin_dev->line[VIN_LINE_ISP_RAW].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP_RAW]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SRC_SCD_Y,
++              &vin_dev->line[VIN_LINE_ISP_SCD_Y].subdev.entity,
++              STF_VIN_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      isp_dev->subdev.entity.name,
++                      vin_dev->line[VIN_LINE_ISP_SCD_Y]
++                      .subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &dvp_dev->subdev.entity,
++              STF_DVP_PAD_SRC,
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      dvp_dev->subdev.entity.name,
++                      isp_dev->subdev.entity.name,
++              ret);
++              goto err_link;
++      }
++
++      ret = media_create_pad_link(
++              &csi_dev->subdev.entity,
++              STF_CSI_PAD_SRC,
++              &isp_dev->subdev.entity,
++              STF_ISP_PAD_SINK,
++              0);
++      if (ret < 0) {
++              st_err(ST_CAMSS,
++                      "Failed to link %s->%s entities: %d\n",
++                      csi_dev->subdev.entity.name,
++                      isp_dev->subdev.entity.name,
++                      ret);
++              goto err_link;
++      }
++
++      return ret;
++
++err_link:
++      stf_vin_unregister(stfcamss->vin_dev);
++err_reg_vin:
++      stf_isp_unregister(stfcamss->isp_dev);
++err_reg_isp:
++      stf_csi_unregister(stfcamss->csi_dev);
++err_reg_csi:
++      stf_csiphy_unregister(stfcamss->csiphy_dev);
++err_reg_csiphy:
++      stf_dvp_unregister(stfcamss->dvp_dev);
++err_reg_dvp:
++      return ret;
++}
++
++static void stfcamss_unregister_subdevices(struct stfcamss *stfcamss)
++{
++      stf_dvp_unregister(stfcamss->dvp_dev);
++      stf_csiphy_unregister(stfcamss->csiphy_dev);
++      stf_csi_unregister(stfcamss->csi_dev);
++      stf_isp_unregister(stfcamss->isp_dev);
++      stf_vin_unregister(stfcamss->vin_dev);
++}
++
++static int stfcamss_register_mediadevice_subdevnodes(
++              struct v4l2_async_notifier *async,
++              struct v4l2_subdev *sd)
++{
++      struct stfcamss *stfcamss =
++              container_of(async, struct stfcamss, notifier);
++      int ret;
++
++      if (sd->host_priv) {
++              struct media_entity *sensor = &sd->entity;
++              struct media_entity *input = sd->host_priv;
++              unsigned int i;
++
++              for (i = 0; i < sensor->num_pads; i++) {
++                      if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
++                              break;
++              }
++              if (i == sensor->num_pads) {
++                      st_err(ST_CAMSS,
++                              "No source pad in external entity\n");
++                      return -EINVAL;
++              }
++
++              ret = media_create_pad_link(sensor, i,
++                      input, STF_PAD_SINK,
++                      MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++              if (ret < 0) {
++                      st_err(ST_CAMSS,
++                              "Failed to link %s->%s entities: %d\n",
++                              sensor->name, input->name, ret);
++                      return ret;
++              }
++      }
++
++      ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
++      if (ret < 0)
++              return ret;
++
++      if (stfcamss->media_dev.devnode)
++              return ret;
++
++      st_debug(ST_CAMSS, "stfcamss register media device\n");
++      return media_device_register(&stfcamss->media_dev);
++}
++
++static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async,
++                                      struct v4l2_subdev *subdev,
++                                      struct v4l2_async_connection *asd)
++{
++      struct stfcamss *stfcamss =
++              container_of(async, struct stfcamss, notifier);
++      struct stfcamss_async_subdev *csd =
++              container_of(asd, struct stfcamss_async_subdev, asd);
++      enum port_num port = csd->port;
++      struct stf_dvp_dev *dvp_dev = stfcamss->dvp_dev;
++      struct stf_csiphy_dev *csiphy_dev = stfcamss->csiphy_dev;
++
++      switch (port) {
++      case DVP_SENSOR_PORT_NUMBER:
++              dvp_dev->dvp = &csd->interface.dvp;
++              subdev->host_priv = &dvp_dev->subdev.entity;
++              break;
++      case CSI2RX_SENSOR_PORT_NUMBER:
++              csiphy_dev->csiphy = &csd->interface.csiphy;
++              subdev->host_priv = &csiphy_dev->subdev.entity;
++              break;
++      default:
++              break;
++      };
++
++      stfcamss_register_mediadevice_subdevnodes(async, subdev);
++
++      return 0;
++}
++
++#ifdef UNUSED_CODE
++static int stfcamss_subdev_notifier_complete(
++              struct v4l2_async_notifier *async)
++{
++      struct stfcamss *stfcamss =
++              container_of(async, struct stfcamss, notifier);
++      struct v4l2_device *v4l2_dev = &stfcamss->v4l2_dev;
++      struct v4l2_subdev *sd;
++      int ret;
++
++      list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
++              if (sd->host_priv) {
++                      struct media_entity *sensor = &sd->entity;
++                      struct media_entity *input = sd->host_priv;
++                      unsigned int i;
++
++                      for (i = 0; i < sensor->num_pads; i++) {
++                              if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
++                                      break;
++                      }
++                      if (i == sensor->num_pads) {
++                              st_err(ST_CAMSS,
++                                      "No source pad in external entity\n");
++                              return -EINVAL;
++                      }
++
++                      ret = media_create_pad_link(sensor, i,
++                              input, STF_PAD_SINK,
++                              MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
++                      if (ret < 0) {
++                              st_err(ST_CAMSS,
++                                      "Failed to link %s->%s entities: %d\n",
++                                      sensor->name, input->name, ret);
++                              return ret;
++                      }
++              }
++      }
++
++      ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
++      if (ret < 0)
++              return ret;
++
++      return media_device_register(&stfcamss->media_dev);
++}
++#endif
++
++static const struct v4l2_async_notifier_operations
++stfcamss_subdev_notifier_ops = {
++      .bound = stfcamss_subdev_notifier_bound,
++};
++
++static const struct media_device_ops stfcamss_media_ops = {
++      .link_notify = v4l2_pipeline_link_notify,
++};
++
++#ifdef CONFIG_DEBUG_FS
++enum module_id {
++      VIN_MODULE = 0,
++      ISP_MODULE,
++      CSI_MODULE,
++      CSIPHY_MODULE,
++      DVP_MODULE,
++      CLK_MODULE,
++};
++
++static enum module_id id_num = ISP_MODULE;
++
++void dump_clk_reg(void __iomem *reg_base)
++{
++      int i;
++
++      st_info(ST_CAMSS, "DUMP Clk register:\n");
++      for (i = 0; i <= CLK_C_ISP_CTRL; i += 4)
++              print_reg(ST_CAMSS, reg_base, i);
++}
++
++static ssize_t vin_debug_read(struct file *file, char __user *user_buf,
++                      size_t count, loff_t *ppos)
++{
++      struct device *dev = file->private_data;
++      void __iomem *reg_base;
++      struct stfcamss *stfcamss = dev_get_drvdata(dev);
++      struct stf_vin_dev *vin = stfcamss->vin;
++      struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++      struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++      struct stf_csi_dev *csi0_dev = stfcamss->csi_dev;
++
++      switch (id_num) {
++      case VIN_MODULE:
++      case CSIPHY_MODULE:
++      case DVP_MODULE:
++              mutex_lock(&vin_dev->power_lock);
++              if (vin_dev->power_count > 0) {
++                      reg_base = vin->sysctrl_base;
++                      dump_vin_reg(reg_base);
++              }
++              mutex_unlock(&vin_dev->power_lock);
++              break;
++      case ISP_MODULE:
++              mutex_lock(&isp_dev->stream_lock);
++              if (isp_dev->stream_count > 0) {
++                      reg_base = vin->isp_base;
++                      dump_isp_reg(reg_base);
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++              break;
++      case CSI_MODULE:
++              mutex_lock(&csi0_dev->stream_lock);
++              if (csi0_dev->stream_count > 0) {
++                      reg_base = vin->csi2rx_base;
++                      dump_csi_reg(reg_base);
++              }
++              mutex_unlock(&csi0_dev->stream_lock);
++              break;
++      case CLK_MODULE:
++              mutex_lock(&vin_dev->power_lock);
++              if (vin_dev->power_count > 0) {
++                      reg_base = vin->clkgen_base;
++                      dump_clk_reg(reg_base);
++              }
++              mutex_unlock(&vin_dev->power_lock);
++              break;
++      default:
++              break;
++      }
++
++      return 0;
++}
++
++static void set_reg_val(struct stfcamss *stfcamss, int id, u32 offset, u32 val)
++{
++      struct stf_vin_dev *vin = stfcamss->vin;
++      struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++      struct stf_isp_dev *isp_dev = stfcamss->isp_dev;
++      struct stf_csi_dev *csi_dev = stfcamss->csi_dev;
++      void __iomem *reg_base;
++
++      switch (id) {
++      case VIN_MODULE:
++      case CSIPHY_MODULE:
++      case DVP_MODULE:
++              mutex_lock(&vin_dev->power_lock);
++              if (vin_dev->power_count > 0) {
++                      reg_base = vin->sysctrl_base;
++                      print_reg(ST_VIN, reg_base, offset);
++                      reg_write(reg_base, offset, val);
++                      print_reg(ST_VIN, reg_base, offset);
++              }
++              mutex_unlock(&vin_dev->power_lock);
++              break;
++      case ISP_MODULE:
++              mutex_lock(&isp_dev->stream_lock);
++              if (isp_dev->stream_count > 0) {
++                      reg_base = vin->isp_base;
++                      print_reg(ST_ISP, reg_base, offset);
++                      reg_write(reg_base, offset, val);
++                      print_reg(ST_ISP, reg_base, offset);
++              }
++              mutex_unlock(&isp_dev->stream_lock);
++              break;
++      case CSI_MODULE:
++              mutex_lock(&csi_dev->stream_lock);
++              if (csi_dev->stream_count > 0) {
++                      reg_base = vin->csi2rx_base;
++                      print_reg(ST_CSI, reg_base, offset);
++                      reg_write(reg_base, offset, val);
++                      print_reg(ST_CSI, reg_base, offset);
++              }
++              mutex_unlock(&csi_dev->stream_lock);
++              break;
++      case CLK_MODULE:
++              mutex_lock(&vin_dev->power_lock);
++              if (vin_dev->power_count > 0) {
++                      reg_base = vin->clkgen_base;
++                      print_reg(ST_CAMSS, reg_base, offset);
++                      reg_write(reg_base, offset, val);
++                      print_reg(ST_CAMSS, reg_base, offset);
++              }
++              mutex_unlock(&vin_dev->power_lock);
++              break;
++      default:
++              break;
++
++      }
++}
++
++static u32 atoi(const char *s)
++{
++      u32 ret = 0, d = 0;
++      char ch;
++      int hex = 0;
++
++      if ((*s == '0') && (*(s+1) == 'x')) {
++              hex = 1;
++              s += 2;
++      }
++
++      while (1) {
++              if (!hex) {
++                      d = (*s++) - '0';
++                      if (d > 9)
++                              break;
++                      ret *= 10;
++                      ret += d;
++              } else {
++                      ch = tolower(*s++);
++                      if (isdigit(ch))
++                              d = ch - '0';
++                      else if (islower(ch))
++                              d = ch - 'a' + 10;
++                      else
++                              break;
++                      if (d > 15)
++                              break;
++                      ret *= 16;
++                      ret += d;
++              }
++      }
++
++      return ret;
++}
++
++static ssize_t vin_debug_write(struct file *file, const char __user *user_buf,
++                              size_t count, loff_t *ppos)
++{
++      struct device *dev = file->private_data;
++      struct stfcamss *stfcamss = dev_get_drvdata(dev);
++      char *buf;
++      char *line;
++      char *p;
++      static const char *delims = " \t\r";
++      char *token;
++      u32 offset, val;
++
++      buf = memdup_user_nul(user_buf, min_t(size_t, PAGE_SIZE, count));
++      if (IS_ERR(buf))
++              return PTR_ERR(buf);
++      p = buf;
++      st_debug(ST_CAMSS, "dup buf: %s, len: %lu, count: %lu\n", p, strlen(p), count);
++      while (p && *p) {
++              p = skip_spaces(p);
++              line = strsep(&p, "\n");
++              if (!*line || *line == '#')
++                      break;
++              token = strsep(&line, delims);
++              if (!token)
++                      goto out;
++              id_num = atoi(token);
++              token = strsep(&line, delims);
++              if (!token)
++                      goto out;
++              offset = atoi(token);
++              token = strsep(&line, delims);
++              if (!token)
++                      goto out;
++              val = atoi(token);
++      }
++      set_reg_val(stfcamss, id_num, offset, val);
++out:
++      kfree(buf);
++      st_info(ST_CAMSS, "id_num = %d, offset = 0x%x, 0x%x\n", id_num, offset, val);
++      return count;
++}
++
++static const struct file_operations vin_debug_fops = {
++      .open = simple_open,
++      .read = vin_debug_read,
++      .write = vin_debug_write,
++};
++#endif /* CONFIG_DEBUG_FS */
++
++
++static int stfcamss_probe(struct platform_device *pdev)
++{
++      struct stfcamss *stfcamss;
++      struct stf_vin_dev *vin;
++      struct device *dev = &pdev->dev;
++      struct of_phandle_args args;
++      int ret = 0, num_subdevs;
++
++      dev_info(dev, "stfcamss probe enter!\n");
++
++      stfcamss = devm_kzalloc(dev, sizeof(struct stfcamss), GFP_KERNEL);
++      if (!stfcamss)
++              return -ENOMEM;
++
++      stfcamss->dvp_dev = devm_kzalloc(dev,
++              sizeof(*stfcamss->dvp_dev), GFP_KERNEL);
++      if (!stfcamss->dvp_dev) {
++              ret = -ENOMEM;
++              goto err_cam;
++      }
++
++      stfcamss->csiphy_dev = devm_kzalloc(dev,
++              sizeof(*stfcamss->csiphy_dev),
++              GFP_KERNEL);
++      if (!stfcamss->csiphy_dev) {
++              ret = -ENOMEM;
++              goto err_cam;
++      }
++
++      stfcamss->csi_dev = devm_kzalloc(dev,
++              sizeof(*stfcamss->csi_dev),
++              GFP_KERNEL);
++      if (!stfcamss->csi_dev) {
++              ret = -ENOMEM;
++              goto err_cam;
++      }
++
++      stfcamss->isp_dev = devm_kzalloc(dev,
++              sizeof(*stfcamss->isp_dev),
++              GFP_KERNEL);
++      if (!stfcamss->isp_dev) {
++              ret = -ENOMEM;
++              goto err_cam;
++      }
++
++      stfcamss->vin_dev = devm_kzalloc(dev,
++              sizeof(*stfcamss->vin_dev),
++              GFP_KERNEL);
++      if (!stfcamss->vin_dev) {
++              ret = -ENOMEM;
++              goto err_cam;
++      }
++
++      stfcamss->vin = devm_kzalloc(dev,
++              sizeof(struct stf_vin_dev),
++              GFP_KERNEL);
++      if (!stfcamss->vin) {
++              ret = -ENOMEM;
++              goto err_cam;
++      }
++
++      vin = stfcamss->vin;
++
++      vin->irq = platform_get_irq(pdev, 0);
++      if (vin->irq <= 0) {
++              st_err(ST_CAMSS, "Could not get irq\n");
++              goto err_cam;
++      }
++
++      vin->isp_irq = platform_get_irq(pdev, 1);
++      if (vin->isp_irq <= 0) {
++              st_err(ST_CAMSS, "Could not get isp irq\n");
++              goto err_cam;
++      }
++
++      vin->isp_csi_irq = platform_get_irq(pdev, 2);
++      if (vin->isp_csi_irq <= 0) {
++              st_err(ST_CAMSS, "Could not get isp csi irq\n");
++              goto err_cam;
++      }
++
++      vin->isp_scd_irq = platform_get_irq(pdev, 3);
++      if (vin->isp_scd_irq <= 0) {
++              st_err(ST_CAMSS, "Could not get isp scd irq\n");
++              goto err_cam;
++      }
++
++      vin->isp_irq_csiline = platform_get_irq(pdev, 4);
++      if (vin->isp_irq_csiline <= 0) {
++              st_err(ST_CAMSS, "Could not get isp irq csiline\n");
++              goto err_cam;
++      }
++
++      pm_runtime_enable(dev);
++
++      stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks);
++      stfcamss->sys_clk = stfcamss_clocks;
++
++      ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk);
++      if (ret) {
++              st_err(ST_CAMSS, "Failed to get clk controls\n");
++              return ret;
++      }
++
++      stfcamss->nrsts = ARRAY_SIZE(stfcamss_resets);
++      stfcamss->sys_rst = stfcamss_resets;
++
++      ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts,
++              stfcamss->sys_rst);
++      if (ret) {
++              st_err(ST_CAMSS, "Failed to get reset controls\n");
++              return ret;
++      }
++
++      ret = of_parse_phandle_with_fixed_args(dev->of_node,
++                      "starfive,aon-syscon", 1, 0, &args);
++      if (ret < 0) {
++              st_err(ST_CAMSS, "Failed to parse starfive,aon-syscon\n");
++              return -EINVAL;
++      }
++
++      stfcamss->stf_aon_syscon = syscon_node_to_regmap(args.np);
++      of_node_put(args.np);
++      if (IS_ERR(stfcamss->stf_aon_syscon))
++              return PTR_ERR(stfcamss->stf_aon_syscon);
++
++      stfcamss->aon_gp_reg = args.args[0];
++
++      ret = stfcamss_get_mem_res(pdev, vin);
++      if (ret) {
++              st_err(ST_CAMSS, "Could not map registers\n");
++              goto err_cam;
++      }
++
++      ret = vin_parse_dt(dev, vin);
++      if (ret)
++              goto err_cam;
++
++      vin->dev = dev;
++      stfcamss->dev = dev;
++      platform_set_drvdata(pdev, stfcamss);
++
++      v4l2_async_nf_init(&stfcamss->notifier, &stfcamss->v4l2_dev);
++
++      num_subdevs = stfcamss_of_parse_ports(stfcamss);
++      if (num_subdevs < 0) {
++              ret = num_subdevs;
++              goto err_cam_noti;
++      }
++
++      ret = stfcamss_init_subdevices(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS, "Failed to init subdevice: %d\n", ret);
++              goto err_cam_noti;
++      }
++
++      stfcamss->media_dev.dev = stfcamss->dev;
++      strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem",
++              sizeof(stfcamss->media_dev.model));
++      strscpy(stfcamss->media_dev.serial, "0123456789ABCDEF",
++              sizeof(stfcamss->media_dev.serial));
++      snprintf(stfcamss->media_dev.bus_info, sizeof(stfcamss->media_dev.bus_info),
++                      "%s:%s", dev_bus_name(dev), pdev->name);
++      stfcamss->media_dev.hw_revision = 0x01;
++      stfcamss->media_dev.ops = &stfcamss_media_ops;
++      media_device_init(&stfcamss->media_dev);
++
++      stfcamss->v4l2_dev.mdev = &stfcamss->media_dev;
++
++      ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev);
++      if (ret < 0) {
++              st_err(ST_CAMSS, "Failed to register V4L2 device: %d\n", ret);
++              goto err_cam_noti_med;
++      }
++
++      ret = stfcamss_register_subdevices(stfcamss);
++      if (ret < 0) {
++              st_err(ST_CAMSS, "Failed to register subdevice: %d\n", ret);
++              goto err_cam_noti_med_vreg;
++      }
++
++      if (num_subdevs) {
++              stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops;
++              ret = v4l2_async_nf_register(&stfcamss->notifier);
++              if (ret) {
++                      st_err(ST_CAMSS,
++                              "Failed to register async subdev nodes: %d\n",
++                              ret);
++                      goto err_cam_noti_med_vreg_sub;
++              }
++      } else {
++              ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
++              if (ret < 0) {
++                      st_err(ST_CAMSS,
++                              "Failed to register subdev nodes: %d\n",
++                              ret);
++                      goto err_cam_noti_med_vreg_sub;
++              }
++
++              ret = media_device_register(&stfcamss->media_dev);
++              if (ret < 0) {
++                      st_err(ST_CAMSS, "Failed to register media device: %d\n",
++                                      ret);
++                      goto err_cam_noti_med_vreg_sub_medreg;
++              }
++      }
++
++#ifdef CONFIG_DEBUG_FS
++      stfcamss->debugfs_entry = debugfs_create_dir("stfcamss", NULL);
++      stfcamss->vin_debugfs = debugfs_create_file("stf_vin",
++                      0644, stfcamss->debugfs_entry,
++                      (void *)dev, &vin_debug_fops);
++      debugfs_create_u32("dbg_level",
++                      0644, stfcamss->debugfs_entry,
++                      &stdbg_level);
++      debugfs_create_u32("dbg_mask",
++                      0644, stfcamss->debugfs_entry,
++                      &stdbg_mask);
++#endif
++      dev_info(dev, "stfcamss probe success!\n");
++
++      return 0;
++
++#ifdef CONFIG_DEBUG_FS
++      debugfs_remove(stfcamss->vin_debugfs);
++      debugfs_remove_recursive(stfcamss->debugfs_entry);
++      stfcamss->debugfs_entry = NULL;
++#endif
++
++err_cam_noti_med_vreg_sub_medreg:
++err_cam_noti_med_vreg_sub:
++      stfcamss_unregister_subdevices(stfcamss);
++err_cam_noti_med_vreg:
++      v4l2_device_unregister(&stfcamss->v4l2_dev);
++err_cam_noti_med:
++      media_device_cleanup(&stfcamss->media_dev);
++err_cam_noti:
++      v4l2_async_nf_cleanup(&stfcamss->notifier);
++err_cam:
++      // kfree(stfcamss);
++      return ret;
++}
++
++static int stfcamss_remove(struct platform_device *pdev)
++{
++      struct stfcamss *stfcamss = platform_get_drvdata(pdev);
++
++      dev_info(&pdev->dev, "remove done\n");
++
++#ifdef CONFIG_DEBUG_FS
++      debugfs_remove(stfcamss->vin_debugfs);
++      debugfs_remove_recursive(stfcamss->debugfs_entry);
++      stfcamss->debugfs_entry = NULL;
++#endif
++
++      stfcamss_unregister_subdevices(stfcamss);
++      v4l2_device_unregister(&stfcamss->v4l2_dev);
++      media_device_cleanup(&stfcamss->media_dev);
++      pm_runtime_disable(&pdev->dev);
++
++      kfree(stfcamss);
++
++      return 0;
++}
++
++static const struct of_device_id stfcamss_of_match[] = {
++      { .compatible = "starfive,jh7110-vin" },
++      { /* end node */ },
++};
++
++MODULE_DEVICE_TABLE(of, stfcamss_of_match);
++
++#ifdef CONFIG_PM_SLEEP
++static int stfcamss_suspend(struct device *dev)
++{
++      struct stfcamss *stfcamss = dev_get_drvdata(dev);
++      struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++      struct media_entity *entity;
++      struct media_pad *pad;
++      struct v4l2_subdev *subdev;
++      struct stfcamss_video *video;
++      struct video_device *vdev;
++      int i = 0;
++      int pm_power_count;
++      int pm_stream_count;
++
++      for (i = 0; i < VIN_LINE_MAX; i++) {
++              video = &vin_dev->line[i].video_out;
++              vdev = &vin_dev->line[i].video_out.vdev;
++              vin_dev->line[i].pm_power_count = vin_dev->line[i].power_count;
++              vin_dev->line[i].pm_stream_count = vin_dev->line[i].stream_count;
++              pm_power_count = vin_dev->line[i].pm_power_count;
++              pm_stream_count = vin_dev->line[i].pm_stream_count;
++
++              if (pm_stream_count) {
++                      while (pm_stream_count--) {
++                              entity = &vdev->entity;
++                              while (1) {
++                                      pad = &entity->pads[0];
++                                      if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                                              break;
++
++                                      pad = media_pad_remote_pad_first(pad);
++                                      if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                                              break;
++
++                                      entity = pad->entity;
++                                      subdev = media_entity_to_v4l2_subdev(entity);
++
++                                      v4l2_subdev_call(subdev, video, s_stream, 0);
++                              }
++                      }
++                      video_device_pipeline_stop(vdev);
++                      video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
++              }
++
++              if (!pm_power_count)
++                      continue;
++
++              v4l2_pipeline_pm_put(&vdev->entity);
++      }
++
++      return pm_runtime_force_suspend(dev);
++}
++
++static int stfcamss_resume(struct device *dev)
++{
++      struct stfcamss *stfcamss = dev_get_drvdata(dev);
++      struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
++      struct media_entity *entity;
++      struct media_pad *pad;
++      struct v4l2_subdev *subdev;
++      struct stfcamss_video *video;
++      struct video_device *vdev;
++      int i = 0;
++      int pm_power_count;
++      int pm_stream_count;
++      int ret = 0;
++
++      pm_runtime_force_resume(dev);
++
++      for (i = 0; i < VIN_LINE_MAX; i++) {
++              video = &vin_dev->line[i].video_out;
++              vdev = &vin_dev->line[i].video_out.vdev;
++              pm_power_count = vin_dev->line[i].pm_power_count;
++              pm_stream_count = vin_dev->line[i].pm_stream_count;
++
++              if (!pm_power_count)
++                      continue;
++
++              ret = v4l2_pipeline_pm_get(&vdev->entity);
++              if (ret < 0)
++                      goto err;
++
++              if (pm_stream_count) {
++                      ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe);
++                      if (ret < 0)
++                              goto err_pm_put;
++
++                      while (pm_stream_count--) {
++                              entity = &vdev->entity;
++                              while (1) {
++                                      pad = &entity->pads[0];
++                                      if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                                              break;
++
++                                      pad = media_pad_remote_pad_first(pad);
++                                      if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                                              break;
++
++                                      entity = pad->entity;
++                                      subdev = media_entity_to_v4l2_subdev(entity);
++
++                                      ret = v4l2_subdev_call(subdev, video, s_stream, 1);
++                                      if (ret < 0 && ret != -ENOIOCTLCMD)
++                                              goto err_pipeline_stop;
++                              }
++                      }
++              }
++      }
++
++      return 0;
++
++err_pipeline_stop:
++      video_device_pipeline_stop(vdev);
++err_pm_put:
++      v4l2_pipeline_pm_put(&vdev->entity);
++err:
++      return ret;
++}
++#endif /* CONFIG_PM_SLEEP */
++
++#ifdef CONFIG_PM
++static int stfcamss_runtime_suspend(struct device *dev)
++{
++      struct stfcamss *stfcamss = dev_get_drvdata(dev);
++
++      reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
++      reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
++      clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
++
++      return 0;
++}
++
++static int stfcamss_runtime_resume(struct device *dev)
++{
++      struct stfcamss *stfcamss = dev_get_drvdata(dev);
++
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
++      clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
++      reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
++
++      return 0;
++}
++#endif /* CONFIG_PM */
++
++static const struct dev_pm_ops stfcamss_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(stfcamss_suspend, stfcamss_resume)
++      SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, stfcamss_runtime_resume, NULL)
++};
++
++static struct platform_driver stfcamss_driver = {
++      .probe = stfcamss_probe,
++      .remove = stfcamss_remove,
++      .driver = {
++              .name = DRV_NAME,
++              .pm = &stfcamss_pm_ops,
++              .of_match_table = of_match_ptr(stfcamss_of_match),
++      },
++};
++
++static int __init stfcamss_init(void)
++{
++      return platform_driver_register(&stfcamss_driver);
++}
++
++static void __exit stfcamss_cleanup(void)
++{
++      platform_driver_unregister(&stfcamss_driver);
++}
++
++module_init(stfcamss_init);
++//fs_initcall(stfcamss_init);
++module_exit(stfcamss_cleanup);
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/media/platform/starfive/v4l2_driver/stfcamss.h
+@@ -0,0 +1,117 @@
++/* SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
++ *
++ */
++
++#ifndef STFCAMSS_H
++#define STFCAMSS_H
++
++#include <linux/io.h>
++#include <linux/delay.h>
++#include <linux/reset.h>
++#include <linux/clk.h>
++
++enum sensor_type {
++      SENSOR_VIN,
++      /* need replace sensor */
++      SENSOR_ISP,
++};
++
++enum subdev_type {
++      VIN_DEV_TYPE,
++      ISP_DEV_TYPE,
++};
++
++#include "stf_common.h"
++#include "stf_dvp.h"
++#include "stf_csi.h"
++#include "stf_csiphy.h"
++#include "stf_isp.h"
++#include "stf_vin.h"
++
++#define STF_PAD_SINK   0
++#define STF_PAD_SRC    1
++#define STF_PADS_NUM   2
++
++#define STF_CAMSS_SKIP_ITI
++
++enum port_num {
++      DVP_SENSOR_PORT_NUMBER = 0,
++      CSI2RX_SENSOR_PORT_NUMBER
++};
++
++enum stf_clk_num {
++      STFCLK_APB_FUNC = 0,
++      STFCLK_PCLK,
++      STFCLK_SYS_CLK,
++      STFCLK_WRAPPER_CLK_C,
++      STFCLK_DVP_INV,
++      STFCLK_AXIWR,
++      STFCLK_MIPI_RX0_PXL,
++      STFCLK_PIXEL_CLK_IF0,
++      STFCLK_PIXEL_CLK_IF1,
++      STFCLK_PIXEL_CLK_IF2,
++      STFCLK_PIXEL_CLK_IF3,
++      STFCLK_M31DPHY_CFGCLK_IN,
++      STFCLK_M31DPHY_REFCLK_IN,
++      STFCLK_M31DPHY_TXCLKESC_LAN0,
++      STFCLK_ISPCORE_2X,
++      STFCLK_ISP_AXI,
++      STFCLK_NUM
++};
++
++enum stf_rst_num {
++      STFRST_WRAPPER_P = 0,
++      STFRST_WRAPPER_C,
++      STFRST_PCLK,
++      STFRST_SYS_CLK,
++      STFRST_AXIRD,
++      STFRST_AXIWR,
++      STFRST_PIXEL_CLK_IF0,
++      STFRST_PIXEL_CLK_IF1,
++      STFRST_PIXEL_CLK_IF2,
++      STFRST_PIXEL_CLK_IF3,
++      STFRST_M31DPHY_HW,
++      STFRST_M31DPHY_B09_ALWAYS_ON,
++      STFRST_ISP_TOP_N,
++      STFRST_ISP_TOP_AXI,
++      STFRST_NUM
++};
++
++struct stfcamss {
++      struct stf_vin_dev *vin;  // stfcamss phy res
++      struct v4l2_device v4l2_dev;
++      struct media_device media_dev;
++      struct media_pipeline pipe;
++      struct device *dev;
++      struct stf_vin2_dev *vin_dev;  // subdev
++      struct stf_dvp_dev *dvp_dev;   // subdev
++      struct stf_csi_dev *csi_dev;   // subdev
++      struct stf_csiphy_dev *csiphy_dev;   // subdev
++      struct stf_isp_dev *isp_dev;   // subdev
++      struct v4l2_async_notifier notifier;
++      struct clk_bulk_data *sys_clk;
++      int nclks;
++      struct reset_control_bulk_data *sys_rst;
++      int nrsts;
++      struct regmap *stf_aon_syscon;
++      uint32_t aon_gp_reg;
++#ifdef CONFIG_DEBUG_FS
++      struct dentry *debugfs_entry;
++      struct dentry *vin_debugfs;
++#endif
++};
++
++struct stfcamss_async_subdev {
++      struct v4l2_async_connection asd;  // must be first
++      enum port_num port;
++      struct {
++              struct dvp_cfg dvp;
++              struct csi2phy_cfg csiphy;
++      } interface;
++};
++
++extern struct media_entity *stfcamss_find_sensor(struct media_entity *entity);
++
++#endif /* STFCAMSS_H */
+--- /dev/null
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -0,0 +1,253 @@
++/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
++/*
++ * jh7110-isp.h
++ *
++ * JH7110 ISP driver - user space header file.
++ *
++ * Copyright Â© 2023 Starfive Technology Co., Ltd.
++ *
++ * Author: Su Zejian (zejian.su@starfivetech.com)
++ *
++ */
++
++#ifndef __JH7110_ISP_H_
++#define __JH7110_ISP_H_
++
++#include <linux/v4l2-controls.h>
++
++#define V4L2_CID_USER_JH7110_ISP_WB_SETTING   \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0001)
++#define V4L2_CID_USER_JH7110_ISP_CAR_SETTING  \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0002)
++#define V4L2_CID_USER_JH7110_ISP_CCM_SETTING  \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0003)
++#define V4L2_CID_USER_JH7110_ISP_CFA_SETTING          \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0004)
++#define V4L2_CID_USER_JH7110_ISP_CTC_SETTING          \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0005)
++#define V4L2_CID_USER_JH7110_ISP_DBC_SETTING  \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0006)
++#define V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING        \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0007)
++#define V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING               \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0008)
++#define V4L2_CID_USER_JH7110_ISP_LCCF_SETTING \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0009)
++#define V4L2_CID_USER_JH7110_ISP_OBC_SETTING  \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x000a)
++#define V4L2_CID_USER_JH7110_ISP_OECF_SETTING \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x000b)
++#define V4L2_CID_USER_JH7110_ISP_R2Y_SETTING  \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x000c)
++#define V4L2_CID_USER_JH7110_ISP_SAT_SETTING          \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x000d)
++#define V4L2_CID_USER_JH7110_ISP_SHRP_SETTING         \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x000e)
++#define V4L2_CID_USER_JH7110_ISP_YCRV_SETTING \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
++
++struct jh7110_isp_wb_gain {
++      __u16 gain_r;
++      __u16 gain_g;
++      __u16 gain_b;
++};
++
++struct jh7110_isp_wb_setting {
++      __u32 enabled;
++      struct jh7110_isp_wb_gain gains;
++};
++
++struct jh7110_isp_car_setting {
++      __u32 enabled;
++};
++
++struct jh7110_isp_ccm_smlow {
++      __s32 ccm[3][3];
++      __s32 offsets[3];
++};
++
++struct jh7110_isp_ccm_setting {
++      __u32 enabled;
++      struct jh7110_isp_ccm_smlow ccm_smlow;
++};
++
++struct jh7110_isp_cfa_params {
++      __s32 hv_width;
++      __s32 cross_cov;
++};
++
++struct jh7110_isp_cfa_setting {
++      __u32 enabled;
++      struct jh7110_isp_cfa_params settings;
++};
++
++struct jh7110_isp_ctc_params {
++      __u8 saf_mode;
++      __u8 daf_mode;
++      __s32 max_gt;
++      __s32 min_gt;
++};
++
++struct jh7110_isp_ctc_setting {
++      __u32 enabled;
++      struct jh7110_isp_ctc_params settings;
++};
++
++struct jh7110_isp_dbc_params {
++      __s32 bad_gt;
++      __s32 bad_xt;
++};
++
++struct jh7110_isp_dbc_setting {
++      __u32 enabled;
++      struct jh7110_isp_dbc_params settings;
++};
++
++struct jh7110_isp_dnyuv_params {
++      __u8 y_sweight[10];
++      __u16 y_curve[7];
++      __u8 uv_sweight[10];
++      __u16 uv_curve[7];
++};
++
++struct jh7110_isp_dnyuv_setting {
++      __u32 enabled;
++      struct jh7110_isp_dnyuv_params settings;
++};
++
++struct jh7110_isp_gmargb_point {
++      __u16 g_val;
++      __u16 sg_val;
++};
++
++struct jh7110_isp_gmargb_setting {
++      __u32 enabled;
++      struct jh7110_isp_gmargb_point curve[15];
++};
++
++struct jh7110_isp_lccf_circle {
++      __s16 center_x;
++      __s16 center_y;
++      __u8 radius;
++};
++
++struct jh7110_isp_lccf_curve_param {
++      __s16 f1;
++      __s16 f2;
++};
++
++struct jh7110_isp_lccf_setting {
++      __u32 enabled;
++      struct jh7110_isp_lccf_circle circle;
++      struct jh7110_isp_lccf_curve_param r_param;
++      struct jh7110_isp_lccf_curve_param gr_param;
++      struct jh7110_isp_lccf_curve_param gb_param;
++      struct jh7110_isp_lccf_curve_param b_param;
++};
++
++struct jh7110_isp_blacklevel_win_size {
++      __u32 width;
++      __u32 height;
++};
++
++struct jh7110_isp_blacklevel_gain {
++      __u8 tl_gain;
++      __u8 tr_gain;
++      __u8 bl_gain;
++      __u8 br_gain;
++};
++
++struct jh7110_isp_blacklevel_offset {
++      __u8 tl_offset;
++      __u8 tr_offset;
++      __u8 bl_offset;
++      __u8 br_offset;
++};
++
++struct jh7110_isp_blacklevel_setting {
++      __u32 enabled;
++      struct jh7110_isp_blacklevel_win_size win_size;
++      struct jh7110_isp_blacklevel_gain gain[4];
++      struct jh7110_isp_blacklevel_offset offset[4];
++};
++
++struct jh7110_isp_oecf_point {
++      __u16 x;
++      __u16 y;
++      __s16 slope;
++};
++
++struct jh7110_isp_oecf_setting {
++      __u32 enabled;
++      struct jh7110_isp_oecf_point r_curve[16];
++      struct jh7110_isp_oecf_point gr_curve[16];
++      struct jh7110_isp_oecf_point gb_curve[16];
++      struct jh7110_isp_oecf_point b_curve[16];
++};
++
++struct jh7110_isp_r2y_matrix {
++      __s16 m[9];
++};
++
++struct jh7110_isp_r2y_setting {
++      __u32 enabled;
++      struct jh7110_isp_r2y_matrix matrix;
++};
++
++struct jh7110_isp_sat_curve {
++      __s16 yi_min;
++      __s16 yo_ir;
++      __s16 yo_min;
++      __s16 yo_max;
++};
++
++struct jh7110_isp_sat_hue_info {
++      __s16 sin;
++      __s16 cos;
++};
++
++struct jh7110_isp_sat_info {
++      __s16 gain_cmab;
++      __s16 gain_cmad;
++      __s16 threshold_cmb;
++      __s16 threshold_cmd;
++      __s16 offset_u;
++      __s16 offset_v;
++      __s16 cmsf;
++};
++
++struct jh7110_isp_sat_setting {
++      __u32 enabled;
++      struct jh7110_isp_sat_curve curve;
++      struct jh7110_isp_sat_hue_info hue_info;
++      struct jh7110_isp_sat_info sat_info;
++};
++
++struct jh7110_isp_sharp_weight {
++      __u8 weight[15];
++      __u32 recip_wei_sum;
++};
++
++struct jh7110_isp_sharp_strength {
++      __s16 diff[4];
++      __s16 f[4];
++};
++
++struct jh7110_isp_sharp_setting {
++      __u32 enabled;
++      struct jh7110_isp_sharp_weight weight;
++      struct jh7110_isp_sharp_strength strength;
++      __s8 pdirf;
++      __s8 ndirf;
++};
++
++struct jh7110_isp_ycrv_curve {
++      __s16 y[64];
++};
++
++struct jh7110_isp_ycrv_setting {
++      __u32 enabled;
++      struct jh7110_isp_ycrv_curve curve;
++};
++
++#endif
+--- a/include/uapi/linux/v4l2-controls.h
++++ b/include/uapi/linux/v4l2-controls.h
+@@ -203,6 +203,12 @@ enum v4l2_colorfx {
+  */
+ #define V4L2_CID_USER_ASPEED_BASE             (V4L2_CID_USER_BASE + 0x11a0)
++/*
++ * The base for the jh7110-isp driver controls.
++ * We reserve 16 controls for this driver.
++ */
++#define V4L2_CID_USER_JH7110_ISP_BASE         (V4L2_CID_USER_BASE + 0x1170)
++
+ /* MPEG-class control IDs */
+ /* The MPEG controls are applicable to all codec controls
+  * and the 'MPEG' part of the define is historical */
+--- /dev/null
++++ b/include/video/stf-vin.h
+@@ -0,0 +1,443 @@
++/* include/video/stf-vin.h
++ *
++ * Copyright 2020 starfive tech.
++ *    Eric Tang <eric.tang@starfivetech.com>
++ *
++ * Generic vin notifier interface
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++*/
++#ifndef _VIDEO_VIN_H
++#define _VIDEO_VIN_H
++
++#include <linux/cdev.h>
++
++#define DRV_NAME "jh7110-vin"
++#define FB_FIRST_ADDR      0xf9000000
++#define FB_SECOND_ADDR     0xf97e9000
++
++#define RESERVED_MEM_SIZE  0x1000000
++
++#define VIN_MIPI_CONTROLLER0_OFFSET 0x00000
++#define VIN_CLKGEN_OFFSET           0x10000
++#define VIN_RSTGEN_OFFSET           0x20000
++#define VIN_MIPI_CONTROLLER1_OFFSET 0x30000
++#define VIN_SYSCONTROLLER_OFFSET    0x40000
++
++#define VD_1080P    1080
++#define VD_720P     720
++#define VD_PAL      480
++
++#define VD_HEIGHT_1080P     VD_1080P
++#define VD_WIDTH_1080P      1920
++
++#define VD_HEIGHT_720P      VD_720P
++#define VD_WIDTH_720P       1080
++
++#define VD_HEIGHT_480       480
++#define VD_WIDTH_640        640
++
++#define SEEED_WIDTH_800       800
++#define SEEED_HIGH_480        480
++
++#define VIN_TOP_CLKGEN_BASE_ADDR          0x11800000
++#define VIN_TOP_RSTGEN_BASE_ADDR          0x11840000
++#define VIN_TOP_IOPAD_BASE_ADDR               0x11858000
++
++#define ISP_BASE_MIPI0_ADDR             0x19800000
++#define ISP_BASE_CLKGEN_ADDR            0x19810000
++#define ISP_BASE_RSTGEN_ADDR            0x19820000
++#define ISP_BASE_MIPI1_ADDR             0x19830000
++#define ISP_BASE_SYSCTRL_ADDR         0x19840000
++#define ISP_BASE_ISP0_ADDR                0x19870000
++#define ISP_BASE_ISP1_ADDR                0x198a0000
++
++
++//vin clk registers
++#define CLK_VIN_SRC_CTRL                  0x188
++#define CLK_ISP0_AXI_CTRL                 0x190
++#define CLK_ISP0NOC_AXI_CTRL      0x194
++#define CLK_ISPSLV_AXI_CTRL               0x198
++#define CLK_ISP1_AXI_CTRL                 0x1A0
++#define CLK_ISP1NOC_AXI_CTRL      0x1A4
++#define CLK_VIN_AXI                       0x1AC
++#define CLK_VINNOC_AXI                        0x1B0
++
++
++#define CLK_DOM4_APB_FUNC                     0x0
++#define CLK_MUX_SEL                                   0xffffff
++
++#define CLK_MIPI_RX0_PXL            0x4
++
++#define CLK_DVP_INV                                   0x8
++#define CLK_U0_VIN_PCLK                               0x18
++#define CLK_U0_VIN_PCLK_ICG                                           (0x1<<31)
++
++#define CLK_U0_VIN_SYS_CLK                    0x1c
++#define CLK_U0_VIN_CLK_P_AXIWR                0x30
++#define CLK_U0_VIN_MUX_SEL                    (BIT(24) | BIT(25) | BIT(26) | BIT(27) | BIT(28) | BIT(29))
++
++#define CLK_U0_VIN_PIXEL_CLK_IF0    0x20
++#define CLK_U0_VIN_PIXEL_CLK_IF1    0x24
++#define CLK_U0_VIN_PIXEL_CLK_IF2    0x28
++#define CLK_U0_VIN_PIXEL_CLK_IF3    0x2c
++
++#define CLK_U0_VIN_CLK_P_AXIWR      0x30
++
++#define CLK_U0_ISPV2_TOP_WRAPPER_CLK_C        0x34u
++#define CLK_U0_ISPV2_MUX_SEL          (0x1<<24 | 0x1<<25 | 0x1<<26 | 0x1<<27 | 0x1<<28 | 0x1<< 29)
++
++#define CLK_U0_ISPV2_CLK_ICG                                          (0x1<<31)
++
++#define SOFTWARE_RESET_ASSERT0_ASSERT_SET 0x38U
++#define SOFTWARE_RESET_ASSERT0_ASSERT_SET_STATE 0x3CU
++#define RST_U0_ISPV2_TOP_WRAPPER_RST_P        BIT(0)
++#define RST_U0_ISPV2_TOP_WRAPPER_RST_C        BIT(1)
++#define RSTN_U0_VIN_RST_N_PCLK        BIT(4)
++#define RSTN_U0_VIN_RST_N_SYS_CLK     BIT(9)
++#define RSTN_U0_VIN_RST_P_AXIRD       BIT(10)
++#define RSTN_U0_VIN_RST_P_AXIWR       BIT(11)
++
++
++#define CLK_POLARITY                          (0x1<<30)
++
++#define M31DPHY_APBCFGSAIF__SYSCFG_0  0x0
++#define M31DPHY_APBCFGSAIF__SYSCFG_4  0x4
++#define M31DPHY_APBCFGSAIF__SYSCFG_8  0x8
++#define M31DPHY_APBCFGSAIF__SYSCFG_12 0xc
++#define M31DPHY_APBCFGSAIF__SYSCFG_16 0x10
++#define M31DPHY_APBCFGSAIF__SYSCFG_20 0x14
++#define M31DPHY_APBCFGSAIF__SYSCFG_24 0x18
++#define M31DPHY_APBCFGSAIF__SYSCFG_28 0x1c
++#define M31DPHY_APBCFGSAIF__SYSCFG_32 0x20
++#define M31DPHY_APBCFGSAIF__SYSCFG_36 0x24
++#define M31DPHY_APBCFGSAIF__SYSCFG_40 0x28
++#define M31DPHY_APBCFGSAIF__SYSCFG_44 0x2c
++#define M31DPHY_APBCFGSAIF__SYSCFG_48 0x30
++#define M31DPHY_APBCFGSAIF__SYSCFG_52 0x34
++#define M31DPHY_APBCFGSAIF__SYSCFG_56 0x38
++#define M31DPHY_APBCFGSAIF__SYSCFG_60 0x3c
++#define M31DPHY_APBCFGSAIF__SYSCFG_64 0x40
++#define M31DPHY_APBCFGSAIF__SYSCFG_68 0x44
++#define M31DPHY_APBCFGSAIF__SYSCFG_72 0x48
++#define M31DPHY_APBCFGSAIF__SYSCFG_76 0x4c
++#define M31DPHY_APBCFGSAIF__SYSCFG_80 0x50
++#define M31DPHY_APBCFGSAIF__SYSCFG_84 0x54
++#define M31DPHY_APBCFGSAIF__SYSCFG_88 0x58
++#define M31DPHY_APBCFGSAIF__SYSCFG_92 0x5c
++#define M31DPHY_APBCFGSAIF__SYSCFG_96 0x60
++#define M31DPHY_APBCFGSAIF__SYSCFG_100        0x64
++#define M31DPHY_APBCFGSAIF__SYSCFG_104        0x68
++#define M31DPHY_APBCFGSAIF__SYSCFG_108        0x6c
++#define M31DPHY_APBCFGSAIF__SYSCFG_112        0x70
++#define M31DPHY_APBCFGSAIF__SYSCFG_116        0x74
++#define M31DPHY_APBCFGSAIF__SYSCFG_120        0x78
++#define M31DPHY_APBCFGSAIF__SYSCFG_124        0x7c
++#define M31DPHY_APBCFGSAIF__SYSCFG_128        0x80
++#define M31DPHY_APBCFGSAIF__SYSCFG_132        0x84
++#define M31DPHY_APBCFGSAIF__SYSCFG_136        0x88
++#define M31DPHY_APBCFGSAIF__SYSCFG_140        0x8c
++#define M31DPHY_APBCFGSAIF__SYSCFG_144        0x90
++#define M31DPHY_APBCFGSAIF__SYSCFG_184        0xb8
++
++//pmu registers
++#define SW_DEST_POWER_ON                      0x0C
++#define SW_DEST_POWER_OFF                     0x10
++#define SW_ENCOURAGE                          0x44
++
++
++//isp clk registers
++#define CLK_DPHY_CFGCLK_ISPCORE_2X_CTRL    0x00
++#define CLK_DPHY_REFCLK_ISPCORE_2X_CTRL    0x04
++#define CLK_DPHY_TXCLKESC_IN_CTRL          0x08
++#define CLK_MIPI_RX0_PXL_CTRL       0x0c
++#define CLK_MIPI_RX1_PXL_CTRL       0x10
++#define CLK_MIPI_RX0_PXL_0_CTRL     0X14
++#define CLK_MIPI_RX0_PXL_1_CTRL     0X18
++#define CLK_MIPI_RX0_PXL_2_CTRL     0X1C
++#define CLK_MIPI_RX0_PXL_3_CTRL     0X20
++#define CLK_MIPI_RX0_SYS0_CTRL      0x24
++#define CLK_MIPI_RX1_PXL_0_CTRL     0X28
++#define CLK_MIPI_RX1_PXL_1_CTRL     0X2C
++#define CLK_MIPI_RX1_PXL_2_CTRL     0X30
++#define CLK_MIPI_RX1_PXL_3_CTRL     0X34
++#define CLK_MIPI_RX1_SYS1_CTRL      0x38
++#define CLK_ISP_CTRL               0x3c
++#define CLK_ISP_2X_CTRL            0x40
++#define CLK_ISP_MIPI_CTRL          0x44
++#define CLK_C_ISP_CTRL             0x64
++#define CLK_CSI2RX0_APB_CTRL        0x58
++
++
++#define CLK_VIN_AXI_WR_CTRL         0x5C
++
++#define SOFTWARE_RESET_ASSERT0                0x0
++#define SOFTWARE_RESET_ASSERT1                0x4
++#define SOFTWARE_RESET_STATUS         0x4
++
++#define IOPAD_REG81                   0x144
++#define IOPAD_REG82                   0x148
++#define IOPAD_REG83                   0x14C
++#define IOPAD_REG84                   0x150
++#define IOPAD_REG85                   0x154
++#define IOPAD_REG86                   0x158
++#define IOPAD_REG87               0x15C
++#define IOPAD_REG88               0x160
++#define IOPAD_REG89               0x164
++
++//sys control REG DEFINE
++#define SYSCONSAIF_SYSCFG_0                                           0X0
++#define U0_VIN_SCFG_SRAM_CONFIG     (BIT(0) | BIT(1))
++
++#define SYSCONSAIF_SYSCFG_4                   0x4
++#define U0_VIN_CNFG_AXIRD_END_ADDR 0xffffffff
++#define SYSCONSAIF_SYSCFG_8                   0x8
++#define U0_VIN_CNFG_AXIRD_LINE_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13))
++#define U0_VIN_CNFG_AXIRD_LINE_CNT_START (BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
++#define SYSCONSAIF_SYSCFG_12          0xc
++#define U0_VIN_CNFG_AXIRD_PIX_CNT_END  (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
++#define U0_VIN_CNFG_AXIRD_PIX_CNT_START (BIT(13) | BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
++#define U0_VIN_CNFG_AXIRD_PIX_CT      (BIT(26) | BIT(27))
++#define SYSCONSAIF_SYSCFG_16          0x10
++#define U0_VIN_CNFG_AXIRD_START_ADDR 0xFFFFFFFF
++#define SYSCONSAIF_SYSCFG_20          0x14
++#define U0_VIN_CNFG_AXIWR0_EN         BIT(4)
++#define U0_VIN_CNFG_AXIWR0_CHANNEL_SEL (BIT(0) | BIT(1) | BIT(2) | BIT(3))
++#define SYSCONSAIF_SYSCFG_24          0x18
++#define U0_VIN_CNFG_AXIWR0_END_ADDR 0xFFFFFFFF
++
++#define SYSCONSAIF_SYSCFG_28          0x1c
++#define U0_VIN_CNFG_AXIWR0_INTR_CLEAN BIT(0)
++#define U0_VIN_CNFG_AXIWR0_MASK               BIT(1)
++#define U0_VIN_CNFG_AXIWR0_PIX_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
++#define U0_VIN_CNFG_AXIWR0_PIX_CT (BIT(13) | BIT(14))
++#define UO_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL (BIT(15) | BIT(16))
++#define SYSCONSAIF_SYSCFG_32          0x20
++
++#define SYSCONSAIF_SYSCFG_36                  0x24
++#define UO_VIN_CNFG_COLOR_BAR_EN      BIT(0)
++#define U0_VIN_CNFG_DVP_HS_POS                (0x1<<1)
++#define U0_VIN_CNFG_DVP_SWAP_EN               BIT(2)
++#define U0_VIN_CNFG_DVP_VS_POS                (0x1<<3)
++#define U0_VIN_CNFG_GEN_EN_AXIRD      BIT(4)
++#define U0_VIN_CNFG_ISP_DVP_EN0               BIT(5)
++#define U0_VIN_CNFG_MIPI_BYTE_EN_ISP0 (BIT(6) |BIT(7))
++#define U0_VIN_CNFG_P_I_MIPI_CHANNEL_SEL0     (BIT(8) |BIT(9) | BIT(10) | BIT(11))
++#define U0_VIN_CNFG_P_I_MIPI_HEADER_EN0 BIT(12)
++
++#define U0_VIN_CNFG_PIX_NUM                   (0x1<<13 | 0x1<<14 | 0x1<<15 | 0x1<<16)
++#define U0_VIN_CNFG_AXIRD_AXI_CNT_END (BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13))
++
++#define U0_VIN_CNFG_AXI_DVP_EN                BIT(2)
++#define U0_VIN_CNFG_AXIRD_INTR_MASK   BIT(1)
++#define U0_VIN_CNFG_AXIWRD_INTR_MASK  BIT(1)
++#define U0_VIN_CNFG_AXIWR0_START_ADDR 0xffffffff
++#define U0_VIN_CNFG_COLOR_BAR_EN      0X0
++#define U0_VIN_CNFG_AXIWR0_PIX_CNT_CT (BIT(13) | BIT(14))
++#define U0_VIN_CNFG_AXIWR0_PIX_CNT_CNT_END (BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
++#define U0_VIN_CNFG_AXIWR0_PIXEL_HITH_BIT_SEL (BIT(15) | BIT(16))
++
++#define SYSCTRL_REG4              0x10
++#define SYSCTRL_DPHY_CTRL             0x14
++#define SYSCTRL_VIN_AXI_CTRL      0x18
++#define SYSCTRL_VIN_WR_START_ADDR     0x28
++#define SYSCTRL_VIN_RD_END_ADDR           0x2C
++#define SYSCTRL_VIN_WR_PIX_TOTAL      0x30
++#define SYSCTRL_VIN_RD_PIX_TOTAL      0x34
++#define SYSCTRL_VIN_RW_CTRL           0x38
++#define SYSCTRL_VIN_SRC_CHAN_SEL      0x24
++#define SYSCTRL_VIN_SRC_DW_SEL            0x40
++#define SYSCTRL_VIN_RD_VBLANK     0x44
++#define SYSCTRL_VIN_RD_VEND           0x48
++#define SYSCTRL_VIN_RD_HBLANK     0x4C
++#define SYSCTRL_VIN_RD_HEND           0x50
++#define SYSCTRL_VIN_INTP_CTRL     0x54
++
++#define ISP_NO_SCALE_ENABLE     (0x1<<20)
++#define ISP_MULTI_FRAME_ENABLE  (0x1<<17)
++#define ISP_SS0_ENABLE          (0x1<<11)
++#define ISP_SS1_ENABLE          (0x1<<12)
++#define ISP_RESET               (0x1<<1)
++#define ISP_ENBALE              (0x1)
++
++
++
++ //ISP REG DEFINE
++#define ISP_REG_DVP_POLARITY_CFG            0x00000014
++#define ISP_REG_RAW_FORMAT_CFG              0x00000018
++#define ISP_REG_CFA_MODE                    0x00000A1C
++#define ISP_REG_PIC_CAPTURE_START_CFG       0x0000001C
++#define ISP_REG_PIC_CAPTURE_END_CFG         0x00000020
++#define ISP_REG_PIPELINE_XY_SIZE            0x00000A0C
++#define ISP_REG_Y_PLANE_START_ADDR          0x00000A80
++#define ISP_REG_UV_PLANE_START_ADDR         0x00000A84
++#define ISP_REG_STRIDE                      0x00000A88
++#define ISP_REG_PIXEL_COORDINATE_GEN        0x00000A8C
++#define ISP_REG_PIXEL_AXI_CONTROL           0x00000A90
++#define ISP_REG_SS_AXI_CONTROL              0x00000AC4
++#define ISP_REG_RGB_TO_YUV_COVERSION0       0x00000E40
++#define ISP_REG_RGB_TO_YUV_COVERSION1       0x00000E44
++#define ISP_REG_RGB_TO_YUV_COVERSION2       0x00000E48
++#define ISP_REG_RGB_TO_YUV_COVERSION3       0x00000E4C
++#define ISP_REG_RGB_TO_YUV_COVERSION4       0x00000E50
++#define ISP_REG_RGB_TO_YUV_COVERSION5       0x00000E54
++#define ISP_REG_RGB_TO_YUV_COVERSION6       0x00000E58
++#define ISP_REG_RGB_TO_YUV_COVERSION7       0x00000E5C
++#define ISP_REG_RGB_TO_YUV_COVERSION8       0x00000E60
++#define ISP_REG_CSI_MODULE_CFG              0x00000010
++#define ISP_REG_ISP_CTRL_1                  0x00000A08
++#define ISP_REG_ISP_CTRL_0                  0x00000A00
++#define ISP_REG_DC_AXI_ID                   0x00000044
++#define ISP_REG_CSI_INPUT_EN_AND_STATUS     0x00000000
++
++//CSI registers
++#define DEVICE_CONFIG           0x00
++#define SOFT_RESET              0x04
++#define STATIC_CFG              0x08
++#define ERROR_BYPASS_CFG        0x10
++#define MONITOR_IRQS            0x18
++#define MONITOR_IRQS_MASK_CFG   0x1c
++#define INFO_IRQS               0x20
++#define INFO_IRQS_MASK_CFG      0x24
++#define ERROR_IRQS              0x28
++#define ERROR_IRQS_MASK_CFG     0x2c
++#define DPHY_LANE_CONTROL       0x40
++#define DPHY_STATUS             0x48
++#define DPHY_ERR_STATUS_IRQ     0x4C
++#define DPHY_ERR_IRQ_MASK_CFG   0x50
++#define INTEGRATION_DEBUG       0x60
++#define ERROR_DEBUG             0x74
++
++#define STREAM0_CTRL            0x100
++#define STREAM0_STATUS          0x104
++#define STREAM0_DATA_CFG        0x108
++#define STREAM0_CFG             0x10c
++#define STREAM0_MONITOR_CTRL    0x110
++#define STREAM0_MONITOR_FRAME   0x114
++#define STREAM0_MONITOR_LB      0x118
++#define STREAM0_TIMER           0x11c
++#define STREAM0_FCC_CFG         0x120
++#define STREAM0_FCC_CTRL        0x124
++#define STREAM0_FIFO_FILL_LVL   0x128
++
++//m31_dphy registers
++#define M31DPHY_APBCFGSAIF__SYSCFG_188      0xbc
++#define M31DPHY_APBCFGSAIF__SYSCFG_192      0xc0
++#define M31DPHY_APBCFGSAIF__SYSCFG_196      0xc4
++#define M31DPHY_APBCFGSAIF__SYSCFG_200      0xc8
++
++typedef enum
++{
++    DT_RAW6  = 0x28,
++    DT_RAW7  = 0x29,
++    DT_RAW8  = 0x2a,
++    DT_RAW10 = 0x2b,
++    DT_RAW12 = 0x2c,
++    DT_RAW14 = 0x2d,
++} mipicam_data_type_t;
++
++
++enum VIN_SOURCE_FORMAT {
++      SRC_COLORBAR_VIN_ISP = 0,
++      SRC_DVP_SENSOR_VIN,
++      SRC_DVP_SENSOR_VIN_ISP,//need replace sensor
++      SRC_CSI2RX_VIN_ISP,
++      SRC_DVP_SENSOR_VIN_OV5640,
++};
++
++struct reg_name {
++      char name[10];
++};
++
++typedef struct
++{
++    int dlane_nb;
++    int dlane_map[4];
++    int dlane_en[4];
++    int dlane_pn_swap[4];
++    int clane_nb;
++    int clane_map[2];
++    int clane_pn_swap[2];
++} csi2rx_dphy_cfg_t;
++
++typedef struct
++{
++    int lane_nb;
++    int dlane_map[4];
++    int dt;
++    int hsize;
++    int vsize;
++} csi2rx_cfg_t;
++
++
++typedef struct
++{
++    int mipi_id, w, h, dt, bpp, fps,lane;
++      u8  clane_swap;
++    u8  clane_pn_swap;
++    u8  dlane_swap[4];
++    u8  dlane_pn_swap[4];
++} csi_format;
++
++struct vin_params {
++      void *paddr;
++      unsigned long size;
++};
++
++struct vin_buf {
++      void *vaddr;
++      dma_addr_t paddr;
++      u32 size;
++};
++
++struct vin_framesize {
++      u32 width;
++      u32 height;
++};
++
++struct vin_format {
++      enum VIN_SOURCE_FORMAT format;
++      u8 fps;
++};
++
++struct stf_vin_dev {
++      /* Protects the access of variables shared within the interrupt */
++      spinlock_t irqlock;
++      int irq;
++      struct device *dev;
++      struct cdev vin_cdev;
++      void __iomem *base;
++      void __iomem *csi2rx_base;
++      void __iomem *clkgen_base;
++      void __iomem *rstgen_base;
++      void __iomem *sysctrl_base;
++      void __iomem *isp_base;
++      void __iomem *vin_top_clkgen_base;
++      void __iomem *vin_top_rstgen_base;
++      void __iomem *vin_top_iopad_base;
++      void __iomem *pmu_test;
++      void __iomem *sys_crg;
++      struct vin_framesize frame;
++      struct vin_format format;
++      bool isp;
++      int isp_irq;
++      int isp_csi_irq;
++      int isp_scd_irq;
++      int isp_irq_csiline;
++      u32 major;
++      struct vin_buf buf;
++
++      wait_queue_head_t wq;
++      bool condition;
++      int odd;
++
++      csi_format csi_fmt;
++};
++
++extern int vin_notifier_register(struct notifier_block *nb);
++extern void vin_notifier_unregister(struct notifier_block *nb);
++extern int vin_notifier_call(unsigned long e, void *v);
++#endif
diff --git a/target/linux/starfive/patches-6.6/0086-dt-bindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch b/target/linux/starfive/patches-6.6/0086-dt-bindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch
new file mode 100644 (file)
index 0000000..c2a97a1
--- /dev/null
@@ -0,0 +1,135 @@
+From baec350a0994467584e7e390746e0bb365957a89 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Tue, 13 Jun 2023 16:55:23 +0800
+Subject: [PATCH 086/116] dt-bindings: media: i2c: Add IMX708 CMOS sensor
+ binding
+
+Add YAML devicetree binding for IMX708 CMOS image sensor.
+Let's also add a MAINTAINERS entry for the binding and driver.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ .../devicetree/bindings/media/i2c/imx708.yaml | 117 ++++++++++++++++++
+ 1 file changed, 117 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/imx708.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/imx708.yaml
+@@ -0,0 +1,117 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/imx708.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor
++
++maintainers:
++  - Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.om>
++
++description: |-
++  The Sony IMX708 is a 1/2.3-inch CMOS active pixel digital mage sensor
++  with an active array size of 4608H x 2592V. It is rogrammable through
++  I2C interface. The I2C address is fixed to 0x1A as per ensor data sheet.
++  Image data is sent through MIPI CSI-2, which is configured s either 2 or
++  4 data lanes.
++
++properties:
++  compatible:
++    const: sony,imx708
++
++  reg:
++    description: I2C device address
++    maxItems: 1
++
++  clocks:
++    maxItems: 1
++
++  VDIG-supply:
++    description:
++      Digital I/O voltage supply, 1.1 volts
++
++  VANA1-supply:
++    description:
++      Analog1 voltage supply, 2.8 volts
++
++  VANA2-supply:
++    description:
++      Analog2 voltage supply, 1.8 volts
++
++  VDDL-supply:
++    description:
++      Digital core voltage supply, 1.8 volts
++
++  reset-gpios:
++    description: |-
++      Reference to the GPIO connected to the xclr pin, if any.
++      Must be released (set high) after all supplies and INCK re applied.
++
++  # See ../video-interfaces.txt for more details
++  port:
++    type: object
++    properties:
++      endpoint:
++        type: object
++        properties:
++          data-lanes:
++            description: |-
++              The sensor supports either two-lane, or our-lane operation.
++              For two-lane operation the property must be set o <1 2>.
++            items:
++              - const: 1
++              - const: 2
++
++          clock-noncontinuous:
++            type: boolean
++            description: |-
++              MIPI CSI-2 clock is non-continuous if this roperty is present,
++              otherwise it's continuous.
++
++          link-frequencies:
++            allOf:
++              - $ref: /schemas/types.yaml#/definitions/int64-array
++            description:
++              Allowed data bus frequencies.
++
++        required:
++          - link-frequencies
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - VANA1-supply
++  - VANA2-supply
++  - VDIG-supply
++  - VDDL-supply
++  - port
++
++additionalProperties: false
++
++examples:
++  - |
++    i2c0 {
++        #address-cells = <1>;
++        #size-cells = <0>;
++
++        imx708: sensor@1a {
++            compatible = "sony,imx708";
++            reg = <0x1a>;
++            clocks = <&imx708_clk>;
++            VANA1-supply = <&imx708_vana1>; /* 1.8v */
++            VANA2-supply = <&imx708_vana2>; /* 2.8v */
++            VDIG-supply = <&imx708_vdig>;   /* 1.1v */
++            VDDL-supply = <&imx708_vddl>;   /* 1.8v */
++
++            port {
++                imx708_0: endpoint {
++                    remote-endpoint = <&csi1_ep>;
++                    data-lanes = <1 2>;
++                    clock-noncontinuous;
++                    link-frequencies = /bits/ 64 <450000000>;
++                };
++            };
++        };
++    };
diff --git a/target/linux/starfive/patches-6.6/0087-media-i2c-Add-imx708-support.patch b/target/linux/starfive/patches-6.6/0087-media-i2c-Add-imx708-support.patch
new file mode 100644 (file)
index 0000000..51e6181
--- /dev/null
@@ -0,0 +1,1971 @@
+From e89556802c5d29a80f5887995ab257d4e826a90f Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 3 Apr 2023 13:52:17 +0800
+Subject: [PATCH 087/116] media: i2c: Add imx708 support
+
+Add imx708 support.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/media/i2c/Kconfig  |   13 +
+ drivers/media/i2c/Makefile |    1 +
+ drivers/media/i2c/imx708.c | 1921 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1935 insertions(+)
+ create mode 100644 drivers/media/i2c/imx708.c
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -201,6 +201,19 @@ config VIDEO_IMX415
+         To compile this driver as a module, choose M here: the
+         module will be called imx415.
++config VIDEO_IMX708
++      tristate "Sony IMX708 sensor support"
++      depends on I2C && VIDEO_DEV
++      select MEDIA_CONTROLLER
++      select VIDEO_V4L2_SUBDEV_API
++      select V4L2_FWNODE
++      help
++        This is a Video4Linux2 sensor driver for the Sony
++        IMX708 camera.
++
++        To compile this driver as a module, choose M here: the
++        module will be called imx708.
++
+ config VIDEO_MAX9271_LIB
+       tristate
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -53,6 +53,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
+ obj-$(CONFIG_VIDEO_IMX355) += imx355.o
+ obj-$(CONFIG_VIDEO_IMX412) += imx412.o
+ obj-$(CONFIG_VIDEO_IMX415) += imx415.o
++obj-$(CONFIG_VIDEO_IMX708) += imx708.o
+ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+ obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
+ obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
+--- /dev/null
++++ b/drivers/media/i2c/imx708.c
+@@ -0,0 +1,1921 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX708 cameras.
++ * Copyright (C) 2022-2023, Raspberry Pi Ltd
++ *
++ * Based on Sony imx477 camera driver
++ * Copyright (C) 2020 Raspberry Pi Ltd
++ */
++#include <asm/unaligned.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++
++#define IMX708_REG_VALUE_08BIT                1
++#define IMX708_REG_VALUE_16BIT                2
++
++/* Chip ID */
++#define IMX708_REG_CHIP_ID            0x0016
++#define IMX708_CHIP_ID                        0x0708
++
++#define IMX708_REG_MODE_SELECT                0x0100
++#define IMX708_MODE_STANDBY           0x00
++#define IMX708_MODE_STREAMING         0x01
++
++#define IMX708_REG_ORIENTATION                0x101
++
++#define IMX708_XCLK_FREQ              24000000
++
++#define IMX708_DEFAULT_LINK_FREQ      450000000
++
++/* V_TIMING internal */
++#define IMX708_REG_FRAME_LENGTH               0x0340
++#define IMX708_FRAME_LENGTH_MAX               0xffff
++
++/* Long exposure multiplier */
++#define IMX708_LONG_EXP_SHIFT_MAX     7
++#define IMX708_LONG_EXP_SHIFT_REG     0x3100
++
++/* Exposure control */
++#define IMX708_REG_EXPOSURE           0x0202
++#define IMX708_EXPOSURE_OFFSET                48
++#define IMX708_EXPOSURE_DEFAULT               0x640
++#define IMX708_EXPOSURE_STEP          1
++#define IMX708_EXPOSURE_MIN           1
++#define IMX708_EXPOSURE_MAX           (IMX708_FRAME_LENGTH_MAX - \
++                                       IMX708_EXPOSURE_OFFSET)
++
++/* Analog gain control */
++#define IMX708_REG_ANALOG_GAIN                0x0204
++#define IMX708_ANA_GAIN_MIN           112
++#define IMX708_ANA_GAIN_MAX           960
++#define IMX708_ANA_GAIN_STEP          1
++#define IMX708_ANA_GAIN_DEFAULT               IMX708_ANA_GAIN_MIN
++
++/* Digital gain control */
++#define IMX708_REG_DIGITAL_GAIN               0x020e
++#define IMX708_DGTL_GAIN_MIN          0x0100
++#define IMX708_DGTL_GAIN_MAX          0xffff
++#define IMX708_DGTL_GAIN_DEFAULT      0x0100
++#define IMX708_DGTL_GAIN_STEP         1
++
++/* Colour balance controls */
++#define IMX708_REG_COLOUR_BALANCE_RED   0x0b90
++#define IMX708_REG_COLOUR_BALANCE_BLUE        0x0b92
++#define IMX708_COLOUR_BALANCE_MIN     0x01
++#define IMX708_COLOUR_BALANCE_MAX     0xffff
++#define IMX708_COLOUR_BALANCE_STEP    0x01
++#define IMX708_COLOUR_BALANCE_DEFAULT 0x100
++
++/* Test Pattern Control */
++#define IMX708_REG_TEST_PATTERN               0x0600
++#define IMX708_TEST_PATTERN_DISABLE   0
++#define IMX708_TEST_PATTERN_SOLID_COLOR       1
++#define IMX708_TEST_PATTERN_COLOR_BARS        2
++#define IMX708_TEST_PATTERN_GREY_COLOR        3
++#define IMX708_TEST_PATTERN_PN9               4
++
++/* Test pattern colour components */
++#define IMX708_REG_TEST_PATTERN_R     0x0602
++#define IMX708_REG_TEST_PATTERN_GR    0x0604
++#define IMX708_REG_TEST_PATTERN_B     0x0606
++#define IMX708_REG_TEST_PATTERN_GB    0x0608
++#define IMX708_TEST_PATTERN_COLOUR_MIN        0
++#define IMX708_TEST_PATTERN_COLOUR_MAX        0x0fff
++#define IMX708_TEST_PATTERN_COLOUR_STEP       1
++
++#define IMX708_REG_BASE_SPC_GAINS_L   0x7b10
++#define IMX708_REG_BASE_SPC_GAINS_R   0x7c00
++
++/* HDR exposure ratio (long:med == med:short) */
++#define IMX708_HDR_EXPOSURE_RATIO       4
++#define IMX708_REG_MID_EXPOSURE               0x3116
++#define IMX708_REG_SHT_EXPOSURE               0x0224
++#define IMX708_REG_MID_ANALOG_GAIN    0x3118
++#define IMX708_REG_SHT_ANALOG_GAIN    0x0216
++
++/* IMX708 native and active pixel array size. */
++#define IMX708_NATIVE_WIDTH           4640U
++#define IMX708_NATIVE_HEIGHT          2658U
++#define IMX708_PIXEL_ARRAY_LEFT               16U
++#define IMX708_PIXEL_ARRAY_TOP                24U
++#define IMX708_PIXEL_ARRAY_WIDTH      4608U
++#define IMX708_PIXEL_ARRAY_HEIGHT     2592U
++
++struct imx708_reg {
++      u16 address;
++      u8 val;
++};
++
++struct imx708_reg_list {
++      unsigned int num_of_regs;
++      const struct imx708_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx708_mode {
++      /* Frame width */
++      unsigned int width;
++
++      /* Frame height */
++      unsigned int height;
++
++      /* H-timing in pixels */
++      unsigned int line_length_pix;
++
++      /* Analog crop rectangle. */
++      struct v4l2_rect crop;
++
++      /* Highest possible framerate. */
++      unsigned int vblank_min;
++
++      /* Default framerate. */
++      unsigned int vblank_default;
++
++      /* Default register values */
++      struct imx708_reg_list reg_list;
++
++      /* Not all modes have the same pixel rate. */
++      u64 pixel_rate;
++
++      /* Not all modes have the same minimum exposure. */
++      u32 exposure_lines_min;
++
++      /* Not all modes have the same exposure lines step. */
++      u32 exposure_lines_step;
++
++      /* HDR flag, currently not used at runtime */
++      bool hdr;
++};
++
++/* Default PDAF pixel correction gains */
++static const u8 pdaf_gains[2][9] = {
++      { 0x4c, 0x4c, 0x4c, 0x46, 0x3e, 0x38, 0x35, 0x35, 0x35 },
++      { 0x35, 0x35, 0x35, 0x38, 0x3e, 0x46, 0x4c, 0x4c, 0x4c }
++};
++
++static const struct imx708_reg mode_common_regs[] = {
++      {0x0100, 0x00},
++      {0x0136, 0x18},
++      {0x0137, 0x00},
++      {0x33f0, 0x02},
++      {0x33f1, 0x05},
++      {0x3062, 0x00},
++      {0x3063, 0x12},
++      {0x3068, 0x00},
++      {0x3069, 0x12},
++      {0x306a, 0x00},
++      {0x306b, 0x30},
++      {0x3076, 0x00},
++      {0x3077, 0x30},
++      {0x3078, 0x00},
++      {0x3079, 0x30},
++      {0x5e54, 0x0c},
++      {0x6e44, 0x00},
++      {0xb0b6, 0x01},
++      {0xe829, 0x00},
++      {0xf001, 0x08},
++      {0xf003, 0x08},
++      {0xf00d, 0x10},
++      {0xf00f, 0x10},
++      {0xf031, 0x08},
++      {0xf033, 0x08},
++      {0xf03d, 0x10},
++      {0xf03f, 0x10},
++      {0x0112, 0x0a},
++      {0x0113, 0x0a},
++      {0x0114, 0x01},
++      {0x0b8e, 0x01},
++      {0x0b8f, 0x00},
++      {0x0b94, 0x01},
++      {0x0b95, 0x00},
++      {0x3400, 0x01},
++      {0x3478, 0x01},
++      {0x3479, 0x1c},
++      {0x3091, 0x01},
++      {0x3092, 0x00},
++      {0x3419, 0x00},
++      {0xbcf1, 0x02},
++      {0x3094, 0x01},
++      {0x3095, 0x01},
++      {0x3362, 0x00},
++      {0x3363, 0x00},
++      {0x3364, 0x00},
++      {0x3365, 0x00},
++      {0x0138, 0x01},
++};
++
++/* 10-bit. */
++static const struct imx708_reg mode_4608x2592_regs[] = {
++      {0x0342, 0x3d},
++      {0x0343, 0x20},
++      {0x0340, 0x0a},
++      {0x0341, 0x59},
++      {0x0344, 0x00},
++      {0x0345, 0x00},
++      {0x0346, 0x00},
++      {0x0347, 0x00},
++      {0x0348, 0x11},
++      {0x0349, 0xff},
++      {0x034a, 0x0a},
++      {0x034b, 0x1f},
++      {0x0220, 0x62},
++      {0x0222, 0x01},
++      {0x0900, 0x00},
++      {0x0901, 0x11},
++      {0x0902, 0x0a},
++      {0x3200, 0x01},
++      {0x3201, 0x01},
++      {0x32d5, 0x01},
++      {0x32d6, 0x00},
++      {0x32db, 0x01},
++      {0x32df, 0x00},
++      {0x350c, 0x00},
++      {0x350d, 0x00},
++      {0x0408, 0x00},
++      {0x0409, 0x00},
++      {0x040a, 0x00},
++      {0x040b, 0x00},
++      {0x040c, 0x12},
++      {0x040d, 0x00},
++      {0x040e, 0x0a},
++      {0x040f, 0x20},
++      {0x034c, 0x12},
++      {0x034d, 0x00},
++      {0x034e, 0x0a},
++      {0x034f, 0x20},
++      {0x0301, 0x05},
++      {0x0303, 0x02},
++      {0x0305, 0x02},
++      {0x0306, 0x00},
++      {0x0307, 0x7c},
++      {0x030b, 0x02},
++      {0x030d, 0x04},
++      {0x030e, 0x01},
++      {0x030f, 0x2c},
++      {0x0310, 0x01},
++      {0x3ca0, 0x00},
++      {0x3ca1, 0x64},
++      {0x3ca4, 0x00},
++      {0x3ca5, 0x00},
++      {0x3ca6, 0x00},
++      {0x3ca7, 0x00},
++      {0x3caa, 0x00},
++      {0x3cab, 0x00},
++      {0x3cb8, 0x00},
++      {0x3cb9, 0x08},
++      {0x3cba, 0x00},
++      {0x3cbb, 0x00},
++      {0x3cbc, 0x00},
++      {0x3cbd, 0x3c},
++      {0x3cbe, 0x00},
++      {0x3cbf, 0x00},
++      {0x0202, 0x0a},
++      {0x0203, 0x29},
++      {0x0224, 0x01},
++      {0x0225, 0xf4},
++      {0x3116, 0x01},
++      {0x3117, 0xf4},
++      {0x0204, 0x00},
++      {0x0205, 0x00},
++      {0x0216, 0x00},
++      {0x0217, 0x00},
++      {0x0218, 0x01},
++      {0x0219, 0x00},
++      {0x020e, 0x01},
++      {0x020f, 0x00},
++      {0x3118, 0x00},
++      {0x3119, 0x00},
++      {0x311a, 0x01},
++      {0x311b, 0x00},
++      {0x341a, 0x00},
++      {0x341b, 0x00},
++      {0x341c, 0x00},
++      {0x341d, 0x00},
++      {0x341e, 0x01},
++      {0x341f, 0x20},
++      {0x3420, 0x00},
++      {0x3421, 0xd8},
++      {0xc428, 0x00},
++      {0xc429, 0x04},
++      {0x3366, 0x00},
++      {0x3367, 0x00},
++      {0x3368, 0x00},
++      {0x3369, 0x00},
++};
++
++static const struct imx708_reg mode_2x2binned_regs[] = {
++      {0x0342, 0x1e},
++      {0x0343, 0x90},
++      {0x0340, 0x05},
++      {0x0341, 0x38},
++      {0x0344, 0x00},
++      {0x0345, 0x00},
++      {0x0346, 0x00},
++      {0x0347, 0x00},
++      {0x0348, 0x11},
++      {0x0349, 0xff},
++      {0x034a, 0x0a},
++      {0x034b, 0x1f},
++      {0x0220, 0x62},
++      {0x0222, 0x01},
++      {0x0900, 0x01},
++      {0x0901, 0x22},
++      {0x0902, 0x08},
++      {0x3200, 0x41},
++      {0x3201, 0x41},
++      {0x32d5, 0x00},
++      {0x32d6, 0x00},
++      {0x32db, 0x01},
++      {0x32df, 0x00},
++      {0x350c, 0x00},
++      {0x350d, 0x00},
++      {0x0408, 0x00},
++      {0x0409, 0x00},
++      {0x040a, 0x00},
++      {0x040b, 0x00},
++      {0x040c, 0x09},
++      {0x040d, 0x00},
++      {0x040e, 0x05},
++      {0x040f, 0x10},
++      {0x034c, 0x09},
++      {0x034d, 0x00},
++      {0x034e, 0x05},
++      {0x034f, 0x10},
++      {0x0301, 0x05},
++      {0x0303, 0x02},
++      {0x0305, 0x02},
++      {0x0306, 0x00},
++      {0x0307, 0x7a},
++      {0x030b, 0x02},
++      {0x030d, 0x04},
++      {0x030e, 0x01},
++      {0x030f, 0x2c},
++      {0x0310, 0x01},
++      {0x3ca0, 0x00},
++      {0x3ca1, 0x3c},
++      {0x3ca4, 0x00},
++      {0x3ca5, 0x3c},
++      {0x3ca6, 0x00},
++      {0x3ca7, 0x00},
++      {0x3caa, 0x00},
++      {0x3cab, 0x00},
++      {0x3cb8, 0x00},
++      {0x3cb9, 0x1c},
++      {0x3cba, 0x00},
++      {0x3cbb, 0x08},
++      {0x3cbc, 0x00},
++      {0x3cbd, 0x1e},
++      {0x3cbe, 0x00},
++      {0x3cbf, 0x0a},
++      {0x0202, 0x05},
++      {0x0203, 0x08},
++      {0x0224, 0x01},
++      {0x0225, 0xf4},
++      {0x3116, 0x01},
++      {0x3117, 0xf4},
++      {0x0204, 0x00},
++      {0x0205, 0x70},
++      {0x0216, 0x00},
++      {0x0217, 0x70},
++      {0x0218, 0x01},
++      {0x0219, 0x00},
++      {0x020e, 0x01},
++      {0x020f, 0x00},
++      {0x3118, 0x00},
++      {0x3119, 0x70},
++      {0x311a, 0x01},
++      {0x311b, 0x00},
++      {0x341a, 0x00},
++      {0x341b, 0x00},
++      {0x341c, 0x00},
++      {0x341d, 0x00},
++      {0x341e, 0x00},
++      {0x341f, 0x90},
++      {0x3420, 0x00},
++      {0x3421, 0x6c},
++      {0x3366, 0x00},
++      {0x3367, 0x00},
++      {0x3368, 0x00},
++      {0x3369, 0x00},
++};
++
++static const struct imx708_reg mode_2x2binned_720p_regs[] = {
++      {0x0342, 0x14},
++      {0x0343, 0x60},
++      {0x0340, 0x04},
++      {0x0341, 0xb6},
++      {0x0344, 0x03},
++      {0x0345, 0x00},
++      {0x0346, 0x01},
++      {0x0347, 0xb0},
++      {0x0348, 0x0e},
++      {0x0349, 0xff},
++      {0x034a, 0x08},
++      {0x034b, 0x6f},
++      {0x0220, 0x62},
++      {0x0222, 0x01},
++      {0x0900, 0x01},
++      {0x0901, 0x22},
++      {0x0902, 0x08},
++      {0x3200, 0x41},
++      {0x3201, 0x41},
++      {0x32d5, 0x00},
++      {0x32d6, 0x00},
++      {0x32db, 0x01},
++      {0x32df, 0x01},
++      {0x350c, 0x00},
++      {0x350d, 0x00},
++      {0x0408, 0x00},
++      {0x0409, 0x00},
++      {0x040a, 0x00},
++      {0x040b, 0x00},
++      {0x040c, 0x06},
++      {0x040d, 0x00},
++      {0x040e, 0x03},
++      {0x040f, 0x60},
++      {0x034c, 0x06},
++      {0x034d, 0x00},
++      {0x034e, 0x03},
++      {0x034f, 0x60},
++      {0x0301, 0x05},
++      {0x0303, 0x02},
++      {0x0305, 0x02},
++      {0x0306, 0x00},
++      {0x0307, 0x76},
++      {0x030b, 0x02},
++      {0x030d, 0x04},
++      {0x030e, 0x01},
++      {0x030f, 0x2c},
++      {0x0310, 0x01},
++      {0x3ca0, 0x00},
++      {0x3ca1, 0x3c},
++      {0x3ca4, 0x01},
++      {0x3ca5, 0x5e},
++      {0x3ca6, 0x00},
++      {0x3ca7, 0x00},
++      {0x3caa, 0x00},
++      {0x3cab, 0x00},
++      {0x3cb8, 0x00},
++      {0x3cb9, 0x0c},
++      {0x3cba, 0x00},
++      {0x3cbb, 0x04},
++      {0x3cbc, 0x00},
++      {0x3cbd, 0x1e},
++      {0x3cbe, 0x00},
++      {0x3cbf, 0x05},
++      {0x0202, 0x04},
++      {0x0203, 0x86},
++      {0x0224, 0x01},
++      {0x0225, 0xf4},
++      {0x3116, 0x01},
++      {0x3117, 0xf4},
++      {0x0204, 0x00},
++      {0x0205, 0x70},
++      {0x0216, 0x00},
++      {0x0217, 0x70},
++      {0x0218, 0x01},
++      {0x0219, 0x00},
++      {0x020e, 0x01},
++      {0x020f, 0x00},
++      {0x3118, 0x00},
++      {0x3119, 0x70},
++      {0x311a, 0x01},
++      {0x311b, 0x00},
++      {0x341a, 0x00},
++      {0x341b, 0x00},
++      {0x341c, 0x00},
++      {0x341d, 0x00},
++      {0x341e, 0x00},
++      {0x341f, 0x60},
++      {0x3420, 0x00},
++      {0x3421, 0x48},
++      {0x3366, 0x00},
++      {0x3367, 0x00},
++      {0x3368, 0x00},
++      {0x3369, 0x00},
++};
++
++static const struct imx708_reg mode_hdr_regs[] = {
++      {0x0342, 0x14},
++      {0x0343, 0x60},
++      {0x0340, 0x0a},
++      {0x0341, 0x5b},
++      {0x0344, 0x00},
++      {0x0345, 0x00},
++      {0x0346, 0x00},
++      {0x0347, 0x00},
++      {0x0348, 0x11},
++      {0x0349, 0xff},
++      {0x034a, 0x0a},
++      {0x034b, 0x1f},
++      {0x0220, 0x01},
++      {0x0222, IMX708_HDR_EXPOSURE_RATIO},
++      {0x0900, 0x00},
++      {0x0901, 0x11},
++      {0x0902, 0x0a},
++      {0x3200, 0x01},
++      {0x3201, 0x01},
++      {0x32d5, 0x00},
++      {0x32d6, 0x00},
++      {0x32db, 0x01},
++      {0x32df, 0x00},
++      {0x350c, 0x00},
++      {0x350d, 0x00},
++      {0x0408, 0x00},
++      {0x0409, 0x00},
++      {0x040a, 0x00},
++      {0x040b, 0x00},
++      {0x040c, 0x09},
++      {0x040d, 0x00},
++      {0x040e, 0x05},
++      {0x040f, 0x10},
++      {0x034c, 0x09},
++      {0x034d, 0x00},
++      {0x034e, 0x05},
++      {0x034f, 0x10},
++      {0x0301, 0x05},
++      {0x0303, 0x02},
++      {0x0305, 0x02},
++      {0x0306, 0x00},
++      {0x0307, 0xa2},
++      {0x030b, 0x02},
++      {0x030d, 0x04},
++      {0x030e, 0x01},
++      {0x030f, 0x2c},
++      {0x0310, 0x01},
++      {0x3ca0, 0x00},
++      {0x3ca1, 0x00},
++      {0x3ca4, 0x00},
++      {0x3ca5, 0x00},
++      {0x3ca6, 0x00},
++      {0x3ca7, 0x28},
++      {0x3caa, 0x00},
++      {0x3cab, 0x00},
++      {0x3cb8, 0x00},
++      {0x3cb9, 0x30},
++      {0x3cba, 0x00},
++      {0x3cbb, 0x00},
++      {0x3cbc, 0x00},
++      {0x3cbd, 0x32},
++      {0x3cbe, 0x00},
++      {0x3cbf, 0x00},
++      {0x0202, 0x0a},
++      {0x0203, 0x2b},
++      {0x0224, 0x0a},
++      {0x0225, 0x2b},
++      {0x3116, 0x0a},
++      {0x3117, 0x2b},
++      {0x0204, 0x00},
++      {0x0205, 0x00},
++      {0x0216, 0x00},
++      {0x0217, 0x00},
++      {0x0218, 0x01},
++      {0x0219, 0x00},
++      {0x020e, 0x01},
++      {0x020f, 0x00},
++      {0x3118, 0x00},
++      {0x3119, 0x00},
++      {0x311a, 0x01},
++      {0x311b, 0x00},
++      {0x341a, 0x00},
++      {0x341b, 0x00},
++      {0x341c, 0x00},
++      {0x341d, 0x00},
++      {0x341e, 0x00},
++      {0x341f, 0x90},
++      {0x3420, 0x00},
++      {0x3421, 0x6c},
++      {0x3360, 0x01},
++      {0x3361, 0x01},
++      {0x3366, 0x09},
++      {0x3367, 0x00},
++      {0x3368, 0x05},
++      {0x3369, 0x10},
++};
++
++/* Mode configs. Keep separate lists for when HDR is enabled or not. */
++static const struct imx708_mode supported_modes_10bit_no_hdr[] = {
++      {
++              /* Full resolution. */
++              .width = 4608,
++              .height = 2592,
++              .line_length_pix = 0x3d20,
++              .crop = {
++                      .left = IMX708_PIXEL_ARRAY_LEFT,
++                      .top = IMX708_PIXEL_ARRAY_TOP,
++                      .width = 4608,
++                      .height = 2592,
++              },
++              .vblank_min = 58,
++              .vblank_default = 58,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_4608x2592_regs),
++                      .regs = mode_4608x2592_regs,
++              },
++              .pixel_rate = 595200000,
++              .exposure_lines_min = 8,
++              .exposure_lines_step = 1,
++              .hdr = false
++      },
++      {
++              /* regular 2x2 binned. */
++              .width = 2304,
++              .height = 1296,
++              .line_length_pix = 0x1e90,
++              .crop = {
++                      .left = IMX708_PIXEL_ARRAY_LEFT,
++                      .top = IMX708_PIXEL_ARRAY_TOP,
++                      .width = 4608,
++                      .height = 2592,
++              },
++              .vblank_min = 40,
++              .vblank_default = 1198,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_2x2binned_regs),
++                      .regs = mode_2x2binned_regs,
++              },
++              .pixel_rate = 585600000,
++              .exposure_lines_min = 4,
++              .exposure_lines_step = 2,
++              .hdr = false
++      },
++      {
++              /* 2x2 binned and cropped for 720p. */
++              .width = 1536,
++              .height = 864,
++              .line_length_pix = 0x1460,
++              .crop = {
++                      .left = IMX708_PIXEL_ARRAY_LEFT + 768,
++                      .top = IMX708_PIXEL_ARRAY_TOP + 432,
++                      .width = 3072,
++                      .height = 1728,
++              },
++              .vblank_min = 40,
++              .vblank_default = 2755,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_2x2binned_720p_regs),
++                      .regs = mode_2x2binned_720p_regs,
++              },
++              .pixel_rate = 566400000,
++              .exposure_lines_min = 4,
++              .exposure_lines_step = 2,
++              .hdr = false
++      },
++};
++
++static const struct imx708_mode supported_modes_10bit_hdr[] = {
++      {
++              /* There's only one HDR mode, which is 2x2 downscaled */
++              .width = 2304,
++              .height = 1296,
++              .line_length_pix = 0x1460,
++              .crop = {
++                      .left = IMX708_PIXEL_ARRAY_LEFT,
++                      .top = IMX708_PIXEL_ARRAY_TOP,
++                      .width = 4608,
++                      .height = 2592,
++              },
++              .vblank_min = 3673,
++              .vblank_default = 3673,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_hdr_regs),
++                      .regs = mode_hdr_regs,
++              },
++              .pixel_rate = 777600000,
++              .exposure_lines_min = 8 * IMX708_HDR_EXPOSURE_RATIO *
++                                    IMX708_HDR_EXPOSURE_RATIO,
++              .exposure_lines_step = 2 * IMX708_HDR_EXPOSURE_RATIO *
++                                     IMX708_HDR_EXPOSURE_RATIO,
++              .hdr = true
++      }
++};
++
++/*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++      /* 10-bit modes. */
++      MEDIA_BUS_FMT_SRGGB10_1X10,
++      MEDIA_BUS_FMT_SGRBG10_1X10,
++      MEDIA_BUS_FMT_SGBRG10_1X10,
++      MEDIA_BUS_FMT_SBGGR10_1X10,
++};
++
++static const char * const imx708_test_pattern_menu[] = {
++      "Disabled",
++      "Color Bars",
++      "Solid Color",
++      "Grey Color Bars",
++      "PN9"
++};
++
++static const int imx708_test_pattern_val[] = {
++      IMX708_TEST_PATTERN_DISABLE,
++      IMX708_TEST_PATTERN_COLOR_BARS,
++      IMX708_TEST_PATTERN_SOLID_COLOR,
++      IMX708_TEST_PATTERN_GREY_COLOR,
++      IMX708_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx708_supply_name[] = {
++      /* Supplies can be enabled in any order */
++      "VANA1",  /* Analog1 (2.8V) supply */
++      "VANA2",  /* Analog2 (1.8V) supply */
++      "VDIG",  /* Digital Core (1.1V) supply */
++      "VDDL",  /* IF (1.8V) supply */
++};
++
++#define IMX708_NUM_SUPPLIES ARRAY_SIZE(imx708_supply_name)
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software standby), given by T7 in the
++ * datasheet is 8ms.  This does include I2C setup time as well.
++ *
++ * Note, that delay between XCLR low->high and reading the CCI ID register (T6
++ * in the datasheet) is much smaller - 600us.
++ */
++#define IMX708_XCLR_MIN_DELAY_US      8000
++#define IMX708_XCLR_DELAY_RANGE_US    1000
++
++struct imx708 {
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++
++      struct v4l2_mbus_framefmt fmt;
++
++      struct clk *xclk;
++      u32 xclk_freq;
++
++      struct gpio_desc *reset_gpio;
++      struct regulator_bulk_data supplies[IMX708_NUM_SUPPLIES];
++
++      struct v4l2_ctrl_handler ctrl_handler;
++      /* V4L2 Controls */
++      struct v4l2_ctrl *pixel_rate;
++      struct v4l2_ctrl *exposure;
++      struct v4l2_ctrl *vflip;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vblank;
++      struct v4l2_ctrl *hblank;
++      struct v4l2_ctrl *red_balance;
++      struct v4l2_ctrl *blue_balance;
++      struct v4l2_ctrl *notify_gains;
++      struct v4l2_ctrl *hdr_mode;
++
++      /* Current mode */
++      const struct imx708_mode *mode;
++
++      /* Mutex for serialized access */
++      struct mutex mutex;
++
++      /* Streaming on/off */
++      bool streaming;
++
++      /* Rewrite common registers on stream on? */
++      bool common_regs_written;
++
++      /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
++      unsigned int long_exp_shift;
++};
++
++static inline struct imx708 *to_imx708(struct v4l2_subdev *_sd)
++{
++      return container_of(_sd, struct imx708, sd);
++}
++
++static inline void get_mode_table(const struct imx708_mode **mode_list,
++                                unsigned int *num_modes,
++                                bool hdr_enable)
++{
++      if (hdr_enable) {
++              *mode_list = supported_modes_10bit_hdr;
++              *num_modes = ARRAY_SIZE(supported_modes_10bit_hdr);
++      } else {
++              *mode_list = supported_modes_10bit_no_hdr;
++              *num_modes = ARRAY_SIZE(supported_modes_10bit_no_hdr);
++      }
++}
++
++/* Read registers up to 2 at a time */
++static int imx708_read_reg(struct imx708 *imx708, u16 reg, u32 len, u32 *val)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      struct i2c_msg msgs[2];
++      u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++      u8 data_buf[4] = { 0, };
++      int ret;
++
++      if (len > 4)
++              return -EINVAL;
++
++      /* Write register address */
++      msgs[0].addr = client->addr;
++      msgs[0].flags = 0;
++      msgs[0].len = ARRAY_SIZE(addr_buf);
++      msgs[0].buf = addr_buf;
++
++      /* Read data from register */
++      msgs[1].addr = client->addr;
++      msgs[1].flags = I2C_M_RD;
++      msgs[1].len = len;
++      msgs[1].buf = &data_buf[4 - len];
++
++      ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++      if (ret != ARRAY_SIZE(msgs))
++              return -EIO;
++
++      *val = get_unaligned_be32(data_buf);
++
++      return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx708_write_reg(struct imx708 *imx708, u16 reg, u32 len, u32 val)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      u8 buf[6];
++
++      if (len > 4)
++              return -EINVAL;
++
++      put_unaligned_be16(reg, buf);
++      put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++      if (i2c_master_send(client, buf, len + 2) != len + 2)
++              return -EIO;
++
++      return 0;
++}
++
++/* Write a list of registers */
++static int imx708_write_regs(struct imx708 *imx708,
++                           const struct imx708_reg *regs, u32 len)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      unsigned int i;
++      int ret;
++
++      for (i = 0; i < len; i++) {
++              ret = imx708_write_reg(imx708, regs[i].address, 1, regs[i].val);
++              if (ret) {
++                      dev_err_ratelimited(&client->dev,
++                                          "Failed to write reg 0x%4.4x. error = %d\n",
++                                          regs[i].address, ret);
++
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx708_get_format_code(struct imx708 *imx708)
++{
++      unsigned int i;
++
++      lockdep_assert_held(&imx708->mutex);
++
++      i = (imx708->vflip->val ? 2 : 0) |
++          (imx708->hflip->val ? 1 : 0);
++
++      return codes[i];
++}
++
++static void imx708_set_default_format(struct imx708 *imx708)
++{
++      struct v4l2_mbus_framefmt *fmt = &imx708->fmt;
++
++      /* Set default mode to max resolution */
++      imx708->mode = &supported_modes_10bit_no_hdr[0];
++
++      /* fmt->code not set as it will always be computed based on flips */
++      fmt->colorspace = V4L2_COLORSPACE_RAW;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++                                                        fmt->colorspace,
++                                                        fmt->ycbcr_enc);
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++      fmt->width = imx708->mode->width;
++      fmt->height = imx708->mode->height;
++      fmt->field = V4L2_FIELD_NONE;
++}
++
++static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++      struct imx708 *imx708 = to_imx708(sd);
++      struct v4l2_mbus_framefmt *try_fmt_img =
++              v4l2_subdev_get_try_format(sd, fh->state, 0);
++      struct v4l2_rect *try_crop;
++
++      mutex_lock(&imx708->mutex);
++
++      /* Initialize try_fmt for the image pad */
++      if (imx708->hdr_mode->val) {
++              try_fmt_img->width = supported_modes_10bit_hdr[0].width;
++              try_fmt_img->height = supported_modes_10bit_hdr[0].height;
++      } else {
++              try_fmt_img->width = supported_modes_10bit_no_hdr[0].width;
++              try_fmt_img->height = supported_modes_10bit_no_hdr[0].height;
++      }
++      try_fmt_img->code = imx708_get_format_code(imx708);
++      try_fmt_img->field = V4L2_FIELD_NONE;
++
++      /* Initialize try_crop */
++      try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0);
++      try_crop->left = IMX708_PIXEL_ARRAY_LEFT;
++      try_crop->top = IMX708_PIXEL_ARRAY_TOP;
++      try_crop->width = IMX708_PIXEL_ARRAY_WIDTH;
++      try_crop->height = IMX708_PIXEL_ARRAY_HEIGHT;
++
++      mutex_unlock(&imx708->mutex);
++
++      return 0;
++}
++
++static int imx708_set_exposure(struct imx708 *imx708, unsigned int val)
++{
++      int ret;
++
++      val = max(val, imx708->mode->exposure_lines_min);
++      val -= val % imx708->mode->exposure_lines_step;
++
++      /*
++       * In HDR mode this will set the longest exposure. The sensor
++       * will automatically divide the medium and short ones by 4,16.
++       */
++      ret = imx708_write_reg(imx708, IMX708_REG_EXPOSURE,
++                             IMX708_REG_VALUE_16BIT,
++                             val >> imx708->long_exp_shift);
++
++      return ret;
++}
++
++static void imx708_adjust_exposure_range(struct imx708 *imx708,
++                                       struct v4l2_ctrl *ctrl)
++{
++      int exposure_max, exposure_def;
++
++      /* Honour the VBLANK limits when setting exposure. */
++      exposure_max = imx708->mode->height + imx708->vblank->val -
++              IMX708_EXPOSURE_OFFSET;
++      exposure_def = min(exposure_max, imx708->exposure->val);
++      __v4l2_ctrl_modify_range(imx708->exposure, imx708->exposure->minimum,
++                               exposure_max, imx708->exposure->step,
++                               exposure_def);
++}
++
++static int imx708_set_analogue_gain(struct imx708 *imx708, unsigned int val)
++{
++      int ret;
++
++      /*
++       * In HDR mode this will set the gain for the longest exposure,
++       * and by default the sensor uses the same gain for all of them.
++       */
++      ret = imx708_write_reg(imx708, IMX708_REG_ANALOG_GAIN,
++                             IMX708_REG_VALUE_16BIT, val);
++
++      return ret;
++}
++
++static int imx708_set_frame_length(struct imx708 *imx708, unsigned int val)
++{
++      int ret = 0;
++
++      imx708->long_exp_shift = 0;
++
++      while (val > IMX708_FRAME_LENGTH_MAX) {
++              imx708->long_exp_shift++;
++              val >>= 1;
++      }
++
++      ret = imx708_write_reg(imx708, IMX708_REG_FRAME_LENGTH,
++                             IMX708_REG_VALUE_16BIT, val);
++      if (ret)
++              return ret;
++
++      return imx708_write_reg(imx708, IMX708_LONG_EXP_SHIFT_REG,
++                              IMX708_REG_VALUE_08BIT, imx708->long_exp_shift);
++}
++
++static void imx708_set_framing_limits(struct imx708 *imx708)
++{
++      unsigned int hblank;
++      const struct imx708_mode *mode = imx708->mode;
++
++      /* Default to no long exposure multiplier */
++      imx708->long_exp_shift = 0;
++
++      __v4l2_ctrl_modify_range(imx708->pixel_rate,
++                               mode->pixel_rate, mode->pixel_rate,
++                               1, mode->pixel_rate);
++
++      /* Update limits and set FPS to default */
++      __v4l2_ctrl_modify_range(imx708->vblank, mode->vblank_min,
++                               ((1 << IMX708_LONG_EXP_SHIFT_MAX) *
++                                      IMX708_FRAME_LENGTH_MAX) - mode->height,
++                               1, mode->vblank_default);
++
++      /*
++       * Currently PPL is fixed to the mode specified value, so hblank
++       * depends on mode->width only, and is not changeable in any
++       * way other than changing the mode.
++       */
++      hblank = mode->line_length_pix - mode->width;
++      __v4l2_ctrl_modify_range(imx708->hblank, hblank, hblank, 1, hblank);
++}
++
++static int imx708_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct imx708 *imx708 =
++              container_of(ctrl->handler, struct imx708, ctrl_handler);
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      const struct imx708_mode *mode_list;
++      unsigned int num_modes;
++      int ret;
++
++      /*
++       * The VBLANK control may change the limits of usable exposure, so check
++       * and adjust if necessary.
++       */
++      if (ctrl->id == V4L2_CID_VBLANK)
++              imx708_adjust_exposure_range(imx708, ctrl);
++
++      /*
++       * Applying V4L2 control value only happens
++       * when power is up for streaming
++       */
++      if (!pm_runtime_get_if_in_use(&client->dev))
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = imx708_set_analogue_gain(imx708, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE:
++              ret = imx708_set_exposure(imx708, ctrl->val);
++              break;
++      case V4L2_CID_DIGITAL_GAIN:
++              ret = imx708_write_reg(imx708, IMX708_REG_DIGITAL_GAIN,
++                                     IMX708_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN,
++                                     IMX708_REG_VALUE_16BIT,
++                                     imx708_test_pattern_val[ctrl->val]);
++              break;
++      case V4L2_CID_TEST_PATTERN_RED:
++              ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_R,
++                                     IMX708_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_GREENR:
++              ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GR,
++                                     IMX708_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_BLUE:
++              ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_B,
++                                     IMX708_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_GREENB:
++              ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GB,
++                                     IMX708_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_HFLIP:
++      case V4L2_CID_VFLIP:
++              ret = imx708_write_reg(imx708, IMX708_REG_ORIENTATION, 1,
++                                     imx708->hflip->val |
++                                     imx708->vflip->val << 1);
++              break;
++      case V4L2_CID_VBLANK:
++              ret = imx708_set_frame_length(imx708,
++                                            imx708->mode->height + ctrl->val);
++              break;
++      case V4L2_CID_NOTIFY_GAINS:
++              ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_BLUE,
++                                     IMX708_REG_VALUE_16BIT,
++                                     imx708->notify_gains->p_new.p_u32[0]);
++              if (ret)
++                      break;
++              ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_RED,
++                                     IMX708_REG_VALUE_16BIT,
++                                     imx708->notify_gains->p_new.p_u32[3]);
++              break;
++      case V4L2_CID_WIDE_DYNAMIC_RANGE:
++              get_mode_table(&mode_list, &num_modes, ctrl->val);
++              imx708->mode = v4l2_find_nearest_size(mode_list,
++                                                    num_modes,
++                                                    width, height,
++                                                    imx708->mode->width,
++                                                    imx708->mode->height);
++              imx708_set_framing_limits(imx708);
++              ret = 0;
++              break;
++      default:
++              dev_info(&client->dev,
++                       "ctrl(id:0x%x,val:0x%x) is not handled\n",
++                       ctrl->id, ctrl->val);
++              ret = -EINVAL;
++              break;
++      }
++
++      pm_runtime_mark_last_busy(&client->dev);
++      pm_runtime_put_autosuspend(&client->dev);
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops imx708_ctrl_ops = {
++      .s_ctrl = imx708_set_ctrl,
++};
++
++static int imx708_enum_mbus_code(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *sd_state,
++                               struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct imx708 *imx708 = to_imx708(sd);
++
++      if (code->index >= 1)
++              return -EINVAL;
++
++      code->code = imx708_get_format_code(imx708);
++
++      return 0;
++}
++
++static int imx708_enum_frame_size(struct v4l2_subdev *sd,
++                                struct v4l2_subdev_state *sd_state,
++                                struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct imx708 *imx708 = to_imx708(sd);
++      const struct imx708_mode *mode_list;
++      unsigned int num_modes;
++
++      get_mode_table(&mode_list, &num_modes, imx708->hdr_mode->val);
++
++      if (fse->index >= num_modes)
++              return -EINVAL;
++
++      if (fse->code != imx708_get_format_code(imx708))
++              return -EINVAL;
++
++      fse->min_width = mode_list[fse->index].width;
++      fse->max_width = fse->min_width;
++      fse->min_height = mode_list[fse->index].height;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static void imx708_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++      fmt->colorspace = V4L2_COLORSPACE_RAW;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++                                                        fmt->colorspace,
++                                                        fmt->ycbcr_enc);
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx708_update_image_pad_format(struct imx708 *imx708,
++                                         const struct imx708_mode *mode,
++                                         struct v4l2_subdev_format *fmt)
++{
++      fmt->format.width = mode->width;
++      fmt->format.height = mode->height;
++      fmt->format.field = V4L2_FIELD_NONE;
++      imx708_reset_colorspace(&fmt->format);
++}
++
++static int imx708_get_pad_format(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *sd_state,
++                               struct v4l2_subdev_format *fmt)
++{
++      struct imx708 *imx708 = to_imx708(sd);
++
++      mutex_lock(&imx708->mutex);
++
++      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++              struct v4l2_mbus_framefmt *try_fmt =
++                      v4l2_subdev_get_try_format(&imx708->sd, sd_state,
++                                                 fmt->pad);
++              /* update the code which could change due to vflip or hflip */
++              try_fmt->code = imx708_get_format_code(imx708);
++              fmt->format = *try_fmt;
++      } else {
++              imx708_update_image_pad_format(imx708, imx708->mode, fmt);
++              fmt->format.code = imx708_get_format_code(imx708);
++      }
++
++      mutex_unlock(&imx708->mutex);
++      return 0;
++}
++
++static int imx708_set_pad_format(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *sd_state,
++                               struct v4l2_subdev_format *fmt)
++{
++      struct v4l2_mbus_framefmt *framefmt;
++      const struct imx708_mode *mode;
++      struct imx708 *imx708 = to_imx708(sd);
++      const struct imx708_mode *mode_list;
++      unsigned int num_modes;
++
++      mutex_lock(&imx708->mutex);
++
++      /* Bayer order varies with flips */
++      fmt->format.code = imx708_get_format_code(imx708);
++
++      get_mode_table(&mode_list, &num_modes, imx708->hdr_mode->val);
++
++      mode = v4l2_find_nearest_size(mode_list, num_modes, width, height,
++                                    fmt->format.width, fmt->format.height);
++      imx708_update_image_pad_format(imx708, mode, fmt);
++
++      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++              framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
++              *framefmt = fmt->format;
++      } else {
++              imx708->mode = mode;
++              imx708_set_framing_limits(imx708);
++      }
++
++      mutex_unlock(&imx708->mutex);
++
++      return 0;
++}
++
++static const struct v4l2_rect *
++__imx708_get_pad_crop(struct imx708 *imx708, struct v4l2_subdev_state *sd_state,
++                    unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++      switch (which) {
++      case V4L2_SUBDEV_FORMAT_TRY:
++              return v4l2_subdev_get_try_crop(&imx708->sd, sd_state, pad);
++      case V4L2_SUBDEV_FORMAT_ACTIVE:
++              return &imx708->mode->crop;
++      }
++
++      return NULL;
++}
++
++static int imx708_get_selection(struct v4l2_subdev *sd,
++                              struct v4l2_subdev_state *sd_state,
++                              struct v4l2_subdev_selection *sel)
++{
++      switch (sel->target) {
++      case V4L2_SEL_TGT_CROP: {
++              struct imx708 *imx708 = to_imx708(sd);
++
++              mutex_lock(&imx708->mutex);
++              sel->r = *__imx708_get_pad_crop(imx708, sd_state, sel->pad,
++                                              sel->which);
++              mutex_unlock(&imx708->mutex);
++
++              return 0;
++      }
++
++      case V4L2_SEL_TGT_NATIVE_SIZE:
++              sel->r.left = 0;
++              sel->r.top = 0;
++              sel->r.width = IMX708_NATIVE_WIDTH;
++              sel->r.height = IMX708_NATIVE_HEIGHT;
++
++              return 0;
++
++      case V4L2_SEL_TGT_CROP_DEFAULT:
++      case V4L2_SEL_TGT_CROP_BOUNDS:
++              sel->r.left = IMX708_PIXEL_ARRAY_LEFT;
++              sel->r.top = IMX708_PIXEL_ARRAY_TOP;
++              sel->r.width = IMX708_PIXEL_ARRAY_WIDTH;
++              sel->r.height = IMX708_PIXEL_ARRAY_HEIGHT;
++
++              return 0;
++      }
++
++      return -EINVAL;
++}
++
++/* Start streaming */
++static int imx708_start_streaming(struct imx708 *imx708)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      const struct imx708_reg_list *reg_list;
++      int i, ret;
++      u32 val;
++
++      if (!imx708->common_regs_written) {
++              ret = imx708_write_regs(imx708, mode_common_regs,
++                                      ARRAY_SIZE(mode_common_regs));
++              if (ret) {
++                      dev_err(&client->dev, "%s failed to set common settings\n",
++                              __func__);
++                      return ret;
++              }
++
++              ret = imx708_read_reg(imx708, IMX708_REG_BASE_SPC_GAINS_L,
++                                    IMX708_REG_VALUE_08BIT, &val);
++              if (ret == 0 && val == 0x40) {
++                      for (i = 0; i < 54 && ret == 0; i++) {
++                              u16 reg = IMX708_REG_BASE_SPC_GAINS_L + i;
++
++                              ret = imx708_write_reg(imx708, reg,
++                                                     IMX708_REG_VALUE_08BIT,
++                                                     pdaf_gains[0][i % 9]);
++                      }
++                      for (i = 0; i < 54 && ret == 0; i++) {
++                              u16 reg = IMX708_REG_BASE_SPC_GAINS_R + i;
++
++                              ret = imx708_write_reg(imx708, reg,
++                                                     IMX708_REG_VALUE_08BIT,
++                                                     pdaf_gains[1][i % 9]);
++                      }
++              }
++              if (ret) {
++                      dev_err(&client->dev, "%s failed to set PDAF gains\n",
++                              __func__);
++                      return ret;
++              }
++
++              imx708->common_regs_written = true;
++      }
++
++      /* Apply default values of current mode */
++      reg_list = &imx708->mode->reg_list;
++      ret = imx708_write_regs(imx708, reg_list->regs, reg_list->num_of_regs);
++      if (ret) {
++              dev_err(&client->dev, "%s failed to set mode\n", __func__);
++              return ret;
++      }
++
++      /* Apply customized values from user */
++      ret =  __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler);
++      if (ret)
++              return ret;
++
++      /* set stream on register */
++      return imx708_write_reg(imx708, IMX708_REG_MODE_SELECT,
++                              IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING);
++}
++
++/* Stop streaming */
++static void imx708_stop_streaming(struct imx708 *imx708)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      int ret;
++
++      /* set stream off register */
++      ret = imx708_write_reg(imx708, IMX708_REG_MODE_SELECT,
++                             IMX708_REG_VALUE_08BIT, IMX708_MODE_STANDBY);
++      if (ret)
++              dev_err(&client->dev, "%s failed to set stream\n", __func__);
++}
++
++static int imx708_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct imx708 *imx708 = to_imx708(sd);
++      struct i2c_client *client = v4l2_get_subdevdata(sd);
++      int ret = 0;
++
++      mutex_lock(&imx708->mutex);
++      if (imx708->streaming == enable) {
++              mutex_unlock(&imx708->mutex);
++              return 0;
++      }
++
++      if (enable) {
++              ret = pm_runtime_resume_and_get(&client->dev);
++              if (ret < 0)
++                      goto err_unlock;
++
++              /*
++               * Apply default & customized values
++               * and then start streaming.
++               */
++              ret = imx708_start_streaming(imx708);
++              if (ret)
++                      goto err_rpm_put;
++      } else {
++              imx708_stop_streaming(imx708);
++              pm_runtime_mark_last_busy(&client->dev);
++              pm_runtime_put_autosuspend(&client->dev);
++      }
++
++      imx708->streaming = enable;
++
++      /* vflip/hflip and hdr mode cannot change during streaming */
++      __v4l2_ctrl_grab(imx708->vflip, enable);
++      __v4l2_ctrl_grab(imx708->hflip, enable);
++      __v4l2_ctrl_grab(imx708->hdr_mode, enable);
++
++      mutex_unlock(&imx708->mutex);
++
++      return ret;
++
++err_rpm_put:
++      pm_runtime_put_sync(&client->dev);
++err_unlock:
++      mutex_unlock(&imx708->mutex);
++
++      return ret;
++}
++
++/* Power/clock management functions */
++static int imx708_power_on(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx708 *imx708 = to_imx708(sd);
++      int ret;
++
++      ret = regulator_bulk_enable(IMX708_NUM_SUPPLIES,
++                                  imx708->supplies);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable regulators\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = clk_prepare_enable(imx708->xclk);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable clock\n",
++                      __func__);
++              goto reg_off;
++      }
++
++      gpiod_set_value_cansleep(imx708->reset_gpio, 1);
++      usleep_range(IMX708_XCLR_MIN_DELAY_US,
++                   IMX708_XCLR_MIN_DELAY_US + IMX708_XCLR_DELAY_RANGE_US);
++
++      return 0;
++
++reg_off:
++      regulator_bulk_disable(IMX708_NUM_SUPPLIES, imx708->supplies);
++      return ret;
++}
++
++static int imx708_power_off(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx708 *imx708 = to_imx708(sd);
++
++      gpiod_set_value_cansleep(imx708->reset_gpio, 0);
++      clk_disable_unprepare(imx708->xclk);
++      regulator_bulk_disable(IMX708_NUM_SUPPLIES, imx708->supplies);
++
++      /* Force reprogramming of the common registers when powered up again. */
++      imx708->common_regs_written = false;
++
++      return 0;
++}
++
++static int __maybe_unused imx708_suspend(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx708 *imx708 = to_imx708(sd);
++
++      if (imx708->streaming)
++              imx708_stop_streaming(imx708);
++
++      return 0;
++}
++
++static int __maybe_unused imx708_resume(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx708 *imx708 = to_imx708(sd);
++      int ret;
++
++      if (imx708->streaming) {
++              ret = imx708_start_streaming(imx708);
++              if (ret)
++                      goto error;
++      }
++
++      return 0;
++
++error:
++      imx708_stop_streaming(imx708);
++      imx708->streaming = 0;
++      return ret;
++}
++
++static int imx708_get_regulators(struct imx708 *imx708)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      unsigned int i;
++
++      for (i = 0; i < IMX708_NUM_SUPPLIES; i++)
++              imx708->supplies[i].supply = imx708_supply_name[i];
++
++      return devm_regulator_bulk_get(&client->dev,
++                                     IMX708_NUM_SUPPLIES,
++                                     imx708->supplies);
++}
++
++/* Verify chip ID */
++static int imx708_identify_module(struct imx708 *imx708)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      int ret;
++      u32 val;
++
++      ret = imx708_read_reg(imx708, IMX708_REG_CHIP_ID,
++                            IMX708_REG_VALUE_16BIT, &val);
++      if (ret) {
++              dev_err(&client->dev, "failed to read chip id %x, with error %d\n",
++                      IMX708_CHIP_ID, ret);
++              return ret;
++      }
++
++      if (val != IMX708_CHIP_ID) {
++              dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++                      IMX708_CHIP_ID, val);
++              return -EIO;
++      }
++
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx708_core_ops = {
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx708_video_ops = {
++      .s_stream = imx708_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx708_pad_ops = {
++      .enum_mbus_code = imx708_enum_mbus_code,
++      .get_fmt = imx708_get_pad_format,
++      .set_fmt = imx708_set_pad_format,
++      .get_selection = imx708_get_selection,
++      .enum_frame_size = imx708_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops imx708_subdev_ops = {
++      .core = &imx708_core_ops,
++      .video = &imx708_video_ops,
++      .pad = &imx708_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx708_internal_ops = {
++      .open = imx708_open,
++};
++
++static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = {
++      .ops = &imx708_ctrl_ops,
++      .id = V4L2_CID_NOTIFY_GAINS,
++      .type = V4L2_CTRL_TYPE_U32,
++      .min = IMX708_COLOUR_BALANCE_MIN,
++      .max = IMX708_COLOUR_BALANCE_MAX,
++      .step = IMX708_COLOUR_BALANCE_STEP,
++      .def = IMX708_COLOUR_BALANCE_DEFAULT,
++      .dims = { 4 },
++      .elem_size = sizeof(u32),
++};
++
++/* Initialize control handlers */
++static int imx708_init_controls(struct imx708 *imx708)
++{
++      struct v4l2_ctrl_handler *ctrl_hdlr;
++      struct i2c_client *client = v4l2_get_subdevdata(&imx708->sd);
++      struct v4l2_fwnode_device_properties props;
++      unsigned int i;
++      int ret;
++
++      ctrl_hdlr = &imx708->ctrl_handler;
++      ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16);
++      if (ret)
++              return ret;
++
++      mutex_init(&imx708->mutex);
++      ctrl_hdlr->lock = &imx708->mutex;
++
++      /* By default, PIXEL_RATE is read only */
++      imx708->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                             V4L2_CID_PIXEL_RATE,
++                                             imx708->mode->pixel_rate,
++                                             imx708->mode->pixel_rate, 1,
++                                             imx708->mode->pixel_rate);
++
++      /*
++       * Create the controls here, but mode specific limits are setup
++       * in the imx708_set_framing_limits() call below.
++       */
++      imx708->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                         V4L2_CID_VBLANK, 0, 0xffff, 1, 0);
++      imx708->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                         V4L2_CID_HBLANK, 0, 0xffff, 1, 0);
++
++      imx708->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                           V4L2_CID_EXPOSURE,
++                                           IMX708_EXPOSURE_MIN,
++                                           IMX708_EXPOSURE_MAX,
++                                           IMX708_EXPOSURE_STEP,
++                                           IMX708_EXPOSURE_DEFAULT);
++
++      v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++                        IMX708_ANA_GAIN_MIN, IMX708_ANA_GAIN_MAX,
++                        IMX708_ANA_GAIN_STEP, IMX708_ANA_GAIN_DEFAULT);
++
++      v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++                        IMX708_DGTL_GAIN_MIN, IMX708_DGTL_GAIN_MAX,
++                        IMX708_DGTL_GAIN_STEP, IMX708_DGTL_GAIN_DEFAULT);
++
++      imx708->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                        V4L2_CID_HFLIP, 0, 1, 1, 0);
++
++      imx708->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                        V4L2_CID_VFLIP, 0, 1, 1, 0);
++
++      v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx708_ctrl_ops,
++                                   V4L2_CID_TEST_PATTERN,
++                                   ARRAY_SIZE(imx708_test_pattern_menu) - 1,
++                                   0, 0, imx708_test_pattern_menu);
++      for (i = 0; i < 4; i++) {
++              /*
++               * The assumption is that
++               * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++               * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
++               * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++               */
++              v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                V4L2_CID_TEST_PATTERN_RED + i,
++                                IMX708_TEST_PATTERN_COLOUR_MIN,
++                                IMX708_TEST_PATTERN_COLOUR_MAX,
++                                IMX708_TEST_PATTERN_COLOUR_STEP,
++                                IMX708_TEST_PATTERN_COLOUR_MAX);
++              /* The "Solid color" pattern is white by default */
++      }
++
++      imx708->notify_gains = v4l2_ctrl_new_custom(ctrl_hdlr,
++                                                  &imx708_notify_gains_ctrl,
++                                                  NULL);
++
++      imx708->hdr_mode = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
++                                           V4L2_CID_WIDE_DYNAMIC_RANGE,
++                                           0, 1, 1, 0);
++
++      ret = v4l2_fwnode_device_parse(&client->dev, &props);
++      if (ret)
++              goto error;
++
++      v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx708_ctrl_ops, &props);
++
++      if (ctrl_hdlr->error) {
++              ret = ctrl_hdlr->error;
++              dev_err(&client->dev, "%s control init failed (%d)\n",
++                      __func__, ret);
++              goto error;
++      }
++
++      imx708->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      imx708->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++      imx708->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++      imx708->hdr_mode->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      imx708->sd.ctrl_handler = ctrl_hdlr;
++
++      /* Setup exposure and frame/line length limits. */
++      imx708_set_framing_limits(imx708);
++
++      return 0;
++
++error:
++      v4l2_ctrl_handler_free(ctrl_hdlr);
++      mutex_destroy(&imx708->mutex);
++
++      return ret;
++}
++
++static void imx708_free_controls(struct imx708 *imx708)
++{
++      v4l2_ctrl_handler_free(imx708->sd.ctrl_handler);
++      mutex_destroy(&imx708->mutex);
++}
++
++static int imx708_check_hwcfg(struct device *dev)
++{
++      struct fwnode_handle *endpoint;
++      struct v4l2_fwnode_endpoint ep_cfg = {
++              .bus_type = V4L2_MBUS_CSI2_DPHY
++      };
++      int ret = -EINVAL;
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++              dev_err(dev, "could not parse endpoint\n");
++              goto error_out;
++      }
++
++      /* Check the number of MIPI CSI2 data lanes */
++      if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++              dev_err(dev, "only 2 data lanes are currently supported\n");
++              goto error_out;
++      }
++
++      /* Check the link frequency set in device tree */
++      if (!ep_cfg.nr_of_link_frequencies) {
++              dev_err(dev, "link-frequency property not found in DT\n");
++              goto error_out;
++      }
++
++      if (ep_cfg.nr_of_link_frequencies != 1 ||
++          ep_cfg.link_frequencies[0] != IMX708_DEFAULT_LINK_FREQ) {
++              dev_err(dev, "Link frequency not supported: %lld\n",
++                      ep_cfg.link_frequencies[0]);
++              goto error_out;
++      }
++
++      ret = 0;
++
++error_out:
++      v4l2_fwnode_endpoint_free(&ep_cfg);
++      fwnode_handle_put(endpoint);
++
++      return ret;
++}
++
++static int imx708_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct imx708 *imx708;
++      int ret;
++
++      imx708 = devm_kzalloc(&client->dev, sizeof(*imx708), GFP_KERNEL);
++      if (!imx708)
++              return -ENOMEM;
++
++      v4l2_i2c_subdev_init(&imx708->sd, client, &imx708_subdev_ops);
++
++      /* Check the hardware configuration in device tree */
++      if (imx708_check_hwcfg(dev))
++              return -EINVAL;
++
++      /* Get system clock (xclk) */
++      imx708->xclk = devm_clk_get(dev, NULL);
++      if (IS_ERR(imx708->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(imx708->xclk);
++      }
++
++      imx708->xclk_freq = clk_get_rate(imx708->xclk);
++      if (imx708->xclk_freq != IMX708_XCLK_FREQ) {
++              dev_err(dev, "xclk frequency not supported: %d Hz\n",
++                      imx708->xclk_freq);
++              return -EINVAL;
++      }
++
++      ret = imx708_get_regulators(imx708);
++      if (ret) {
++              dev_err(dev, "failed to get regulators\n");
++              return ret;
++      }
++
++      /* Request optional enable pin */
++      imx708->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++                                                   GPIOD_OUT_HIGH);
++
++      /*
++       * The sensor must be powered for imx708_identify_module()
++       * to be able to read the CHIP_ID register
++       */
++      ret = imx708_power_on(dev);
++      if (ret)
++              return ret;
++
++      ret = imx708_identify_module(imx708);
++      if (ret)
++              goto error_power_off;
++
++      /* Initialize default format */
++      imx708_set_default_format(imx708);
++
++      /*
++       * Enable runtime PM with autosuspend. As the device has been powered
++       * manually, mark it as active, and increase the usage count without
++       * resuming the device.
++       */
++      pm_runtime_set_active(dev);
++      pm_runtime_get_noresume(dev);
++      pm_runtime_enable(dev);
++      pm_runtime_set_autosuspend_delay(dev, 1000);
++      pm_runtime_use_autosuspend(dev);
++
++      /* This needs the pm runtime to be registered. */
++      ret = imx708_init_controls(imx708);
++      if (ret)
++              goto error_power_off;
++
++      /* Initialize subdev */
++      imx708->sd.internal_ops = &imx708_internal_ops;
++      imx708->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++                          V4L2_SUBDEV_FL_HAS_EVENTS;
++      imx708->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++      /* Initialize source pad */
++      imx708->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++      ret = media_entity_pads_init(&imx708->sd.entity, 1, &imx708->pad);
++      if (ret) {
++              dev_err(dev, "failed to init entity pads: %d\n", ret);
++              goto error_handler_free;
++      }
++
++      ret = v4l2_async_register_subdev_sensor(&imx708->sd);
++      if (ret < 0) {
++              dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++              goto error_media_entity;
++      }
++
++      /*
++       * Decrease the PM usage count. The device will get suspended after the
++       * autosuspend delay, turning the power off.
++       */
++      pm_runtime_mark_last_busy(dev);
++      pm_runtime_put_autosuspend(dev);
++
++      return 0;
++
++error_media_entity:
++      media_entity_cleanup(&imx708->sd.entity);
++
++error_handler_free:
++      imx708_free_controls(imx708);
++
++error_power_off:
++      pm_runtime_disable(&client->dev);
++      pm_runtime_put_noidle(&client->dev);
++      imx708_power_off(&client->dev);
++
++      return ret;
++}
++
++static void imx708_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx708 *imx708 = to_imx708(sd);
++
++      v4l2_async_unregister_subdev(sd);
++      media_entity_cleanup(&sd->entity);
++      imx708_free_controls(imx708);
++
++      pm_runtime_disable(&client->dev);
++      if (!pm_runtime_status_suspended(&client->dev))
++              imx708_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id imx708_dt_ids[] = {
++      { .compatible = "sony,imx708" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx708_dt_ids);
++
++static const struct dev_pm_ops imx708_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(imx708_suspend, imx708_resume)
++      SET_RUNTIME_PM_OPS(imx708_power_off, imx708_power_on, NULL)
++};
++
++static struct i2c_driver imx708_i2c_driver = {
++      .driver = {
++              .name = "imx708",
++              .of_match_table = imx708_dt_ids,
++              .pm = &imx708_pm_ops,
++      },
++      .probe = imx708_probe,
++      .remove = imx708_remove,
++};
++
++module_i2c_driver(imx708_i2c_driver);
++
++MODULE_AUTHOR("David Plowman <david.plowman@raspberrypi.com>");
++MODULE_DESCRIPTION("Sony IMX708 sensor driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0088-media-i2c-imx708-Delete-gain.patch b/target/linux/starfive/patches-6.6/0088-media-i2c-imx708-Delete-gain.patch
new file mode 100644 (file)
index 0000000..c3971ed
--- /dev/null
@@ -0,0 +1,69 @@
+From 280ab217867d0b934454a837540eab665e854b47 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 3 Apr 2023 15:28:11 +0800
+Subject: [PATCH 088/116] media: i2c: imx708: Delete gain
+
+Delete gain.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ drivers/media/i2c/imx708.c | 27 ---------------------------
+ 1 file changed, 27 deletions(-)
+
+--- a/drivers/media/i2c/imx708.c
++++ b/drivers/media/i2c/imx708.c
+@@ -777,7 +777,6 @@ struct imx708 {
+       struct v4l2_ctrl *hblank;
+       struct v4l2_ctrl *red_balance;
+       struct v4l2_ctrl *blue_balance;
+-      struct v4l2_ctrl *notify_gains;
+       struct v4l2_ctrl *hdr_mode;
+       /* Current mode */
+@@ -1108,16 +1107,6 @@ static int imx708_set_ctrl(struct v4l2_c
+               ret = imx708_set_frame_length(imx708,
+                                             imx708->mode->height + ctrl->val);
+               break;
+-      case V4L2_CID_NOTIFY_GAINS:
+-              ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_BLUE,
+-                                     IMX708_REG_VALUE_16BIT,
+-                                     imx708->notify_gains->p_new.p_u32[0]);
+-              if (ret)
+-                      break;
+-              ret = imx708_write_reg(imx708, IMX708_REG_COLOUR_BALANCE_RED,
+-                                     IMX708_REG_VALUE_16BIT,
+-                                     imx708->notify_gains->p_new.p_u32[3]);
+-              break;
+       case V4L2_CID_WIDE_DYNAMIC_RANGE:
+               get_mode_table(&mode_list, &num_modes, ctrl->val);
+               imx708->mode = v4l2_find_nearest_size(mode_list,
+@@ -1584,18 +1573,6 @@ static const struct v4l2_subdev_internal
+       .open = imx708_open,
+ };
+-static const struct v4l2_ctrl_config imx708_notify_gains_ctrl = {
+-      .ops = &imx708_ctrl_ops,
+-      .id = V4L2_CID_NOTIFY_GAINS,
+-      .type = V4L2_CTRL_TYPE_U32,
+-      .min = IMX708_COLOUR_BALANCE_MIN,
+-      .max = IMX708_COLOUR_BALANCE_MAX,
+-      .step = IMX708_COLOUR_BALANCE_STEP,
+-      .def = IMX708_COLOUR_BALANCE_DEFAULT,
+-      .dims = { 4 },
+-      .elem_size = sizeof(u32),
+-};
+-
+ /* Initialize control handlers */
+ static int imx708_init_controls(struct imx708 *imx708)
+ {
+@@ -1670,10 +1647,6 @@ static int imx708_init_controls(struct i
+               /* The "Solid color" pattern is white by default */
+       }
+-      imx708->notify_gains = v4l2_ctrl_new_custom(ctrl_hdlr,
+-                                                  &imx708_notify_gains_ctrl,
+-                                                  NULL);
+-
+       imx708->hdr_mode = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops,
+                                            V4L2_CID_WIDE_DYNAMIC_RANGE,
+                                            0, 1, 1, 0);
diff --git a/target/linux/starfive/patches-6.6/0089-dt-bindings-display-Add-yamls-for-JH7110-display-sys.patch b/target/linux/starfive/patches-6.6/0089-dt-bindings-display-Add-yamls-for-JH7110-display-sys.patch
new file mode 100644 (file)
index 0000000..e8baa80
--- /dev/null
@@ -0,0 +1,270 @@
+From aa4febf074cbaad81c981a5c6b55324a6f676fb7 Mon Sep 17 00:00:00 2001
+From: "shengyang.chen" <shengyang.chen@starfivetech.com>
+Date: Tue, 13 Jun 2023 14:22:29 +0800
+Subject: [PATCH 089/116] dt-bindings: display: Add yamls for JH7110 display
+ system and hdmi
+
+StarFive SoCs like the jh7110 use display system based on verisilicon IP, use hdmi
+base on innosilicon IP. Add bindings for them.
+
+Signed-off-by: Shengyang Chen <shengyang.chen@starfivetech.com>
+---
+ .../display/verisilicon/starfive-hdmi.yaml    |  92 +++++++++++++++
+ .../display/verisilicon/verisilicon-dc.yaml   | 109 ++++++++++++++++++
+ .../display/verisilicon/verisilicon-drm.yaml  |  41 +++++++
+ 3 files changed, 242 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/display/verisilicon/starfive-hdmi.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/verisilicon/verisilicon-dc.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/verisilicon/verisilicon-drm.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/verisilicon/starfive-hdmi.yaml
+@@ -0,0 +1,92 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/verisilicon/starfive-hdmi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive SoC HDMI transmiter
++
++description:
++  The StarFive SoC uses the HDMI signal transmiter based on innosilicon IP 
++  to generate HDMI signal from its input and transmit the signal to the screen.
++
++maintainers:
++  - Keith Zhao <keith.zhao@starfivetech.com>
++
++properties:
++  compatible:
++    const: starfive,hdmi
++
++  reg:
++    minItems: 1
++
++  interrupts:
++    items:
++      - description: The HDMI hot plug detection interrupt.
++
++  clocks:
++    items:
++      - description: System clock of HDMI module.
++      - description: Mclk clock of HDMI audio.
++      - description: Bclk clock of HDMI audio.
++      - description: Pixel clock generated by HDMI module.
++
++  clock-names:
++    items:
++      - const: sysclk
++      - const: mclk
++      - const: bclk
++      - const: pclk
++
++  resets:
++    items:
++      - description: Reset for HDMI module.
++
++  reset-names:
++    items:
++      - const: hdmi_tx
++
++  '#sound-dai-cells':
++    const: 0
++
++  port:
++    $ref: /schemas/graph.yaml#/properties/port
++    description:
++      Port node with one endpoint connected to a display connector node.
++
++required:
++  - compatible
++  - reg
++  - interrupts
++  - clocks
++  - clock-names
++  - resets
++  - reset-names
++  - '#sound-dai-cells'
++  - port
++
++additionalProperties: false
++
++examples:
++  - |
++    hdmi: hdmi@29590000 {
++      compatible = "starfive,hdmi";
++      reg = <0x29590000 0x4000>;
++      interrupts = <99>;
++      clocks = <&voutcrg 17>,
++               <&voutcrg 15>,
++               <&voutcrg 16>,
++               <&hdmitx0_pixelclk>;
++      clock-names = "sysclk", "mclk","bclk","pclk";
++      resets = <&voutcrg 9>;
++      reset-names = "hdmi_tx";
++      #sound-dai-cells = <0>;
++      hdmi_in: port {
++          #address-cells = <1>;
++          #size-cells = <0>;
++          hdmi_input: endpoint@0 {
++            reg = <0>;
++            remote-endpoint = <&dc_out_dpi0>;
++          };
++      };
++    };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/verisilicon/verisilicon-dc.yaml
+@@ -0,0 +1,109 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/verisilicon/verisilicon-dc.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive SoC display controller
++
++description:
++  The StarFive SoC uses the display controller based on Verisilicon IP 
++  to transfer the image data from a video memory
++  buffer to an external LCD interface.
++
++maintainers:
++  - Keith Zhao <keith.zhao@starfivetech.com>
++
++properties:
++  compatible:
++    const: verisilicon,dc8200
++
++  reg:
++    maxItems: 3
++
++  interrupts:
++    items:
++      - description: The interrupt will be generated when DC finish one frame
++
++  clocks:
++    items:
++      - description: Clock for display system noc bus.
++      - description: Pixel clock for display channel 0.
++      - description: Pixel clock for display channel 1.
++      - description: Clock for axi interface of display controller.
++      - description: Core clock for display controller.
++      - description: Clock for ahb interface of display controller.
++      - description: External HDMI pixel clock.
++      - description: Parent clock for pixel clock
++
++  clock-names:
++    items:
++      - const: clk_vout_noc_disp
++      - const: clk_vout_pix0
++      - const: clk_vout_pix1
++      - const: clk_vout_axi
++      - const: clk_vout_core
++      - const: clk_vout_vout_ahb
++      - const: hdmitx0_pixel
++      - const: clk_vout_dc8200
++
++  resets:
++    items:
++      - description: Reset for axi interface of display controller.
++      - description: Reset for ahb interface of display controller.
++      - description: Core reset of display controller.
++
++  reset-names:
++    items:
++      - const: rst_vout_axi
++      - const: rst_vout_ahb
++      - const: rst_vout_core
++
++  port:
++    $ref: /schemas/graph.yaml#/properties/port
++    description:
++      Port node with one endpoint connected to a hdmi node.
++
++required:
++  - compatible
++  - reg
++  - interrupts
++  - clocks
++  - clock-names
++  - resets
++  - reset-names
++  - port
++
++additionalProperties: false
++
++examples:
++  - |
++    dc8200: dc8200@29400000 {
++      compatible = "verisilicon,dc8200";
++      reg = <0x29400000 0x100>,
++            <0x29400800 0x2000>,
++            <0x295B0000 0x90>;
++      interrupts = <95>;
++      clocks = <&syscrg 60>,
++               <&voutcrg 7>,
++               <&voutcrg 8>,
++               <&voutcrg 4>,
++               <&voutcrg 5>,
++               <&voutcrg 6>,
++               <&hdmitx0_pixelclk>,
++               <&voutcrg 1>;
++      clock-names = "clk_vout_noc_disp", "clk_vout_pix0", "clk_vout_pix1", "clk_vout_axi",
++                    "clk_vout_core", "clk_vout_vout_ahb", "hdmitx0_pixel","clk_vout_dc8200";
++      resets = <&voutcrg 0>,
++               <&voutcrg 1>,
++               <&voutcrg 2>;
++      reset-names = "rst_vout_axi","rst_vout_ahb","rst_vout_core";
++      dc_out: port {
++          #address-cells = <1>;
++          #size-cells = <0>;
++          dc_out_dpi0: endpoint@0 {
++              reg = <0>;
++              remote-endpoint = <&hdmi_input>;
++          };
++      };
++    };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/verisilicon/verisilicon-drm.yaml
+@@ -0,0 +1,41 @@
++# SPDX-License-Identifier: (GPL-2.0-only)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/verisilicon/verisilicon-drm.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Verisilicon DRM master device
++
++maintainers:
++  - Keith Zhao <keith.zhao@starfivetech.com>
++
++description: |
++  The Verisilicon DRM master device is a virtual device needed to list all
++  display controller or other display interface nodes that comprise the
++  graphics subsystem.
++
++properties:
++  compatible:
++    const: verisilicon,display-subsystem
++
++  ports:
++    $ref: /schemas/types.yaml#/definitions/phandle-array
++    items:
++      maxItems: 1
++    description: |
++      Should contain a list of phandles pointing to display interface ports
++      of display controller devices. Display controller definitions as defined in
++      Documentation/devicetree/bindings/display/verisilicon/verisilicon-dc.yaml
++
++required:
++  - compatible
++  - ports
++
++additionalProperties: false
++
++examples:
++  - |
++    display-subsystem {
++        compatible = "verisilicon,display-subsystem";
++        ports = <&dc_out>;
++    };
diff --git a/target/linux/starfive/patches-6.6/0090-soc-starfive-jh71xx_pmu-Add-EVENT_TURN_OFF-register-.patch b/target/linux/starfive/patches-6.6/0090-soc-starfive-jh71xx_pmu-Add-EVENT_TURN_OFF-register-.patch
new file mode 100644 (file)
index 0000000..7a6bfa8
--- /dev/null
@@ -0,0 +1,82 @@
+From 5cbf4154da600af8a2e6a2f8d57d1f212abf3f92 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 13 Oct 2023 14:10:24 +0800
+Subject: [PATCH 090/116] soc: starfive: jh71xx_pmu: Add EVENT_TURN_OFF
+ register writing support
+
+Add and export starfive_pmu_hw_event_turn_off_mask() to
+write EVENT_TURN_OFF register.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/pmdomain/starfive/jh71xx-pmu.c | 11 ++++++++++
+ include/soc/starfive/jh7110_pmu.h      | 29 ++++++++++++++++++++++++++
+ 2 files changed, 40 insertions(+)
+ create mode 100644 include/soc/starfive/jh7110_pmu.h
+
+--- a/drivers/pmdomain/starfive/jh71xx-pmu.c
++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c
+@@ -16,6 +16,7 @@
+ #include <dt-bindings/power/starfive,jh7110-pmu.h>
+ /* register offset */
++#define JH71XX_PMU_HW_EVENT_TURN_OFF  0x08
+ #define JH71XX_PMU_SW_TURN_ON_POWER   0x0C
+ #define JH71XX_PMU_SW_TURN_OFF_POWER  0x10
+ #define JH71XX_PMU_SW_ENCOURAGE               0x44
+@@ -83,6 +84,14 @@ struct jh71xx_pmu_dev {
+       struct generic_pm_domain genpd;
+ };
++static void __iomem *pmu_base;
++
++void starfive_pmu_hw_event_turn_off_mask(u32 mask)
++{
++      writel(mask, pmu_base + JH71XX_PMU_HW_EVENT_TURN_OFF);
++}
++EXPORT_SYMBOL(starfive_pmu_hw_event_turn_off_mask);
++
+ static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool *is_on)
+ {
+       struct jh71xx_pmu *pmu = pmd->pmu;
+@@ -334,6 +343,8 @@ static int jh71xx_pmu_probe(struct platf
+       if (IS_ERR(pmu->base))
+               return PTR_ERR(pmu->base);
++      pmu_base = pmu->base;
++
+       spin_lock_init(&pmu->lock);
+       match_data = of_device_get_match_data(dev);
+--- /dev/null
++++ b/include/soc/starfive/jh7110_pmu.h
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * PMU driver for the StarFive JH7110 SoC
++ *
++ * Copyright (C) 2022 samin <samin.guo@starfivetech.com>
++ */
++
++#ifndef __SOC_STARFIVE_JH7110_PMU_H__
++#define __SOC_STARFIVE_JH7110_PMU_H__
++
++#include <linux/bits.h>
++#include <linux/types.h>
++
++enum PMU_HARD_EVENT {
++      PMU_HW_EVENT_RTC        = BIT(0),
++      PMU_HW_EVENT_GMAC       = BIT(1),
++      PMU_HW_EVENT_RFU        = BIT(2),
++      PMU_HW_EVENT_RGPIO0     = BIT(3),
++      PMU_HW_EVENT_RGPIO1     = BIT(4),
++      PMU_HW_EVENT_RGPIO2     = BIT(5),
++      PMU_HW_EVENT_RGPIO3     = BIT(6),
++      PMU_HW_EVENT_GPU        = BIT(7),
++      PMU_HW_EVENT_ALL        = GENMASK(7, 0),
++};
++
++void starfive_pmu_hw_event_turn_off_mask(u32 mask);
++
++#endif /* __SOC_STARFIVE_JH7110_PMU_H__ */
++
diff --git a/target/linux/starfive/patches-6.6/0091-workqueue-Enable-flush_scheduled_work.patch b/target/linux/starfive/patches-6.6/0091-workqueue-Enable-flush_scheduled_work.patch
new file mode 100644 (file)
index 0000000..a6b6966
--- /dev/null
@@ -0,0 +1,22 @@
+From ab436ea7ffb6c464616addad98632c81a321844a Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 12 Dec 2023 17:56:58 +0800
+Subject: [PATCH 091/116] workqueue: Enable flush_scheduled_work()
+
+Enable flush_scheduled_work() for JH7110 GPU.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ include/linux/workqueue.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/include/linux/workqueue.h
++++ b/include/linux/workqueue.h
+@@ -636,7 +636,6 @@ extern void __warn_flushing_systemwide_w
+ /* Please stop using this function, for this function will be removed in near future. */
+ #define flush_scheduled_work()                                                \
+ ({                                                                    \
+-      __warn_flushing_systemwide_wq();                                \
+       __flush_workqueue(system_wq);                                   \
+ })
diff --git a/target/linux/starfive/patches-6.6/0092-riscv-Optimize-memcpy-with-aligned-version.patch b/target/linux/starfive/patches-6.6/0092-riscv-Optimize-memcpy-with-aligned-version.patch
new file mode 100644 (file)
index 0000000..39c2723
--- /dev/null
@@ -0,0 +1,506 @@
+From dedbbfd891e4d0cdb825454eddfbf8386d0025b3 Mon Sep 17 00:00:00 2001
+From: Mason Huo <mason.huo@starfivetech.com>
+Date: Tue, 20 Jun 2023 13:37:52 +0800
+Subject: [PATCH 092/116] riscv: Optimize memcpy with aligned version
+
+Optimizing the 128 byte align case, this will improve the
+performance of large block memcpy.
+
+Here we combine the memcpy of glibc and kernel.
+
+Signed-off-by: Mason Huo <mason.huo@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/lib/Makefile                       |   3 +-
+ arch/riscv/lib/{memcpy.S => memcpy_aligned.S} |  36 +--
+ arch/riscv/lib/string.c                       | 266 ++++++++++++++++++
+ 3 files changed, 273 insertions(+), 32 deletions(-)
+ rename arch/riscv/lib/{memcpy.S => memcpy_aligned.S} (67%)
+ create mode 100644 arch/riscv/lib/string.c
+
+--- a/arch/riscv/lib/Makefile
++++ b/arch/riscv/lib/Makefile
+@@ -1,6 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ lib-y                 += delay.o
+-lib-y                 += memcpy.o
+ lib-y                 += memset.o
+ lib-y                 += memmove.o
+ lib-y                 += strcmp.o
+@@ -9,5 +8,7 @@ lib-y                  += strncmp.o
+ lib-$(CONFIG_MMU)     += uaccess.o
+ lib-$(CONFIG_64BIT)   += tishift.o
+ lib-$(CONFIG_RISCV_ISA_ZICBOZ)        += clear_page.o
++lib-y                 += string.o
++lib-y                 += memcpy_aligned.o
+ obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
+--- a/arch/riscv/lib/memcpy.S
++++ /dev/null
+@@ -1,110 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
+-/*
+- * Copyright (C) 2013 Regents of the University of California
+- */
+-
+-#include <linux/linkage.h>
+-#include <asm/asm.h>
+-
+-/* void *memcpy(void *, const void *, size_t) */
+-ENTRY(__memcpy)
+-WEAK(memcpy)
+-      move t6, a0  /* Preserve return value */
+-
+-      /* Defer to byte-oriented copy for small sizes */
+-      sltiu a3, a2, 128
+-      bnez a3, 4f
+-      /* Use word-oriented copy only if low-order bits match */
+-      andi a3, t6, SZREG-1
+-      andi a4, a1, SZREG-1
+-      bne a3, a4, 4f
+-
+-      beqz a3, 2f  /* Skip if already aligned */
+-      /*
+-       * Round to nearest double word-aligned address
+-       * greater than or equal to start address
+-       */
+-      andi a3, a1, ~(SZREG-1)
+-      addi a3, a3, SZREG
+-      /* Handle initial misalignment */
+-      sub a4, a3, a1
+-1:
+-      lb a5, 0(a1)
+-      addi a1, a1, 1
+-      sb a5, 0(t6)
+-      addi t6, t6, 1
+-      bltu a1, a3, 1b
+-      sub a2, a2, a4  /* Update count */
+-
+-2:
+-      andi a4, a2, ~((16*SZREG)-1)
+-      beqz a4, 4f
+-      add a3, a1, a4
+-3:
+-      REG_L a4,       0(a1)
+-      REG_L a5,   SZREG(a1)
+-      REG_L a6, 2*SZREG(a1)
+-      REG_L a7, 3*SZREG(a1)
+-      REG_L t0, 4*SZREG(a1)
+-      REG_L t1, 5*SZREG(a1)
+-      REG_L t2, 6*SZREG(a1)
+-      REG_L t3, 7*SZREG(a1)
+-      REG_L t4, 8*SZREG(a1)
+-      REG_L t5, 9*SZREG(a1)
+-      REG_S a4,       0(t6)
+-      REG_S a5,   SZREG(t6)
+-      REG_S a6, 2*SZREG(t6)
+-      REG_S a7, 3*SZREG(t6)
+-      REG_S t0, 4*SZREG(t6)
+-      REG_S t1, 5*SZREG(t6)
+-      REG_S t2, 6*SZREG(t6)
+-      REG_S t3, 7*SZREG(t6)
+-      REG_S t4, 8*SZREG(t6)
+-      REG_S t5, 9*SZREG(t6)
+-      REG_L a4, 10*SZREG(a1)
+-      REG_L a5, 11*SZREG(a1)
+-      REG_L a6, 12*SZREG(a1)
+-      REG_L a7, 13*SZREG(a1)
+-      REG_L t0, 14*SZREG(a1)
+-      REG_L t1, 15*SZREG(a1)
+-      addi a1, a1, 16*SZREG
+-      REG_S a4, 10*SZREG(t6)
+-      REG_S a5, 11*SZREG(t6)
+-      REG_S a6, 12*SZREG(t6)
+-      REG_S a7, 13*SZREG(t6)
+-      REG_S t0, 14*SZREG(t6)
+-      REG_S t1, 15*SZREG(t6)
+-      addi t6, t6, 16*SZREG
+-      bltu a1, a3, 3b
+-      andi a2, a2, (16*SZREG)-1  /* Update count */
+-
+-4:
+-      /* Handle trailing misalignment */
+-      beqz a2, 6f
+-      add a3, a1, a2
+-
+-      /* Use word-oriented copy if co-aligned to word boundary */
+-      or a5, a1, t6
+-      or a5, a5, a3
+-      andi a5, a5, 3
+-      bnez a5, 5f
+-7:
+-      lw a4, 0(a1)
+-      addi a1, a1, 4
+-      sw a4, 0(t6)
+-      addi t6, t6, 4
+-      bltu a1, a3, 7b
+-
+-      ret
+-
+-5:
+-      lb a4, 0(a1)
+-      addi a1, a1, 1
+-      sb a4, 0(t6)
+-      addi t6, t6, 1
+-      bltu a1, a3, 5b
+-6:
+-      ret
+-END(__memcpy)
+-SYM_FUNC_ALIAS(__pi_memcpy, __memcpy)
+-SYM_FUNC_ALIAS(__pi___memcpy, __memcpy)
+--- /dev/null
++++ b/arch/riscv/lib/memcpy_aligned.S
+@@ -0,0 +1,84 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (C) 2013 Regents of the University of California
++ */
++
++#include <linux/linkage.h>
++#include <asm/asm.h>
++
++/* void *__memcpy_aligned(void *, const void *, size_t) */
++ENTRY(__memcpy_aligned)
++      move t6, a0  /* Preserve return value */
++
++2:
++      andi a4, a2, ~((16*SZREG)-1)
++      beqz a4, 4f
++      add a3, a1, a4
++3:
++      REG_L a4,       0(a1)
++      REG_L a5,   SZREG(a1)
++      REG_L a6, 2*SZREG(a1)
++      REG_L a7, 3*SZREG(a1)
++      REG_L t0, 4*SZREG(a1)
++      REG_L t1, 5*SZREG(a1)
++      REG_L t2, 6*SZREG(a1)
++      REG_L t3, 7*SZREG(a1)
++      REG_L t4, 8*SZREG(a1)
++      REG_L t5, 9*SZREG(a1)
++      REG_S a4,       0(t6)
++      REG_S a5,   SZREG(t6)
++      REG_S a6, 2*SZREG(t6)
++      REG_S a7, 3*SZREG(t6)
++      REG_S t0, 4*SZREG(t6)
++      REG_S t1, 5*SZREG(t6)
++      REG_S t2, 6*SZREG(t6)
++      REG_S t3, 7*SZREG(t6)
++      REG_S t4, 8*SZREG(t6)
++      REG_S t5, 9*SZREG(t6)
++      REG_L a4, 10*SZREG(a1)
++      REG_L a5, 11*SZREG(a1)
++      REG_L a6, 12*SZREG(a1)
++      REG_L a7, 13*SZREG(a1)
++      REG_L t0, 14*SZREG(a1)
++      REG_L t1, 15*SZREG(a1)
++      addi a1, a1, 16*SZREG
++      REG_S a4, 10*SZREG(t6)
++      REG_S a5, 11*SZREG(t6)
++      REG_S a6, 12*SZREG(t6)
++      REG_S a7, 13*SZREG(t6)
++      REG_S t0, 14*SZREG(t6)
++      REG_S t1, 15*SZREG(t6)
++      addi t6, t6, 16*SZREG
++      bltu a1, a3, 3b
++      andi a2, a2, (16*SZREG)-1  /* Update count */
++
++4:
++      /* Handle trailing misalignment */
++      beqz a2, 6f
++      add a3, a1, a2
++
++      /* Use word-oriented copy if co-aligned to word boundary */
++      or a5, a1, t6
++      or a5, a5, a3
++      andi a5, a5, 3
++      bnez a5, 5f
++7:
++      lw a4, 0(a1)
++      addi a1, a1, 4
++      sw a4, 0(t6)
++      addi t6, t6, 4
++      bltu a1, a3, 7b
++
++      ret
++
++5:
++      lb a4, 0(a1)
++      addi a1, a1, 1
++      sb a4, 0(t6)
++      addi t6, t6, 1
++      bltu a1, a3, 5b
++6:
++      ret
++END(__memcpy_aligned)
++SYM_FUNC_ALIAS(__pi_memcpy, __memcpy_aligned)
++SYM_FUNC_ALIAS(__pi___memcpy, __memcpy_aligned)
+--- /dev/null
++++ b/arch/riscv/lib/string.c
+@@ -0,0 +1,266 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copy memory to memory until the specified number of bytes
++ * has been copied.  Overlap is NOT handled correctly.
++ * Copyright (C) 1991-2020 Free Software Foundation, Inc.
++ * This file is part of the GNU C Library.
++ * Contributed by Torbjorn Granlund (tege@sics.se).
++ *
++ * The GNU C Library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * The GNU C Library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with the GNU C Library; if not, see
++ * <https://www.gnu.org/licenses/>.
++ *
++ */
++
++#define __NO_FORTIFY
++#include <linux/types.h>
++#include <linux/module.h>
++
++#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
++#define OP_T_THRES      16
++#define op_t    unsigned long
++#define OPSIZ   (sizeof(op_t))
++#define OPSIZ_MASK   (sizeof(op_t) - 1)
++#define FAST_COPY_THRES  (128)
++#define byte    unsigned char
++
++static void _wordcopy_fwd_aligned(long dstp, long srcp, size_t len)
++{
++      op_t a0, a1;
++
++      switch (len % 8) {
++      case 2:
++              a0 = ((op_t *) srcp)[0];
++              srcp -= 6 * OPSIZ;
++              dstp -= 7 * OPSIZ;
++              len += 6;
++              goto do1;
++      case 3:
++              a1 = ((op_t *) srcp)[0];
++              srcp -= 5 * OPSIZ;
++              dstp -= 6 * OPSIZ;
++              len += 5;
++              goto do2;
++      case 4:
++              a0 = ((op_t *) srcp)[0];
++              srcp -= 4 * OPSIZ;
++              dstp -= 5 * OPSIZ;
++              len += 4;
++              goto do3;
++      case 5:
++              a1 = ((op_t *) srcp)[0];
++              srcp -= 3 * OPSIZ;
++              dstp -= 4 * OPSIZ;
++              len += 3;
++              goto do4;
++      case 6:
++              a0 = ((op_t *) srcp)[0];
++              srcp -= 2 * OPSIZ;
++              dstp -= 3 * OPSIZ;
++              len += 2;
++              goto do5;
++      case 7:
++              a1 = ((op_t *) srcp)[0];
++              srcp -= 1 * OPSIZ;
++              dstp -= 2 * OPSIZ;
++              len += 1;
++              goto do6;
++
++      case 0:
++              if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++                      return;
++              a0 = ((op_t *) srcp)[0];
++              srcp -= 0 * OPSIZ;
++              dstp -= 1 * OPSIZ;
++              goto do7;
++      case 1:
++              a1 = ((op_t *) srcp)[0];
++              srcp -= -1 * OPSIZ;
++              dstp -= 0 * OPSIZ;
++              len -= 1;
++              if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++                      goto do0;
++              goto do8;                 /* No-op.  */
++      }
++
++      do {
++do8:
++              a0 = ((op_t *) srcp)[0];
++              ((op_t *) dstp)[0] = a1;
++do7:
++              a1 = ((op_t *) srcp)[1];
++              ((op_t *) dstp)[1] = a0;
++do6:
++              a0 = ((op_t *) srcp)[2];
++              ((op_t *) dstp)[2] = a1;
++do5:
++              a1 = ((op_t *) srcp)[3];
++              ((op_t *) dstp)[3] = a0;
++do4:
++              a0 = ((op_t *) srcp)[4];
++              ((op_t *) dstp)[4] = a1;
++do3:
++              a1 = ((op_t *) srcp)[5];
++              ((op_t *) dstp)[5] = a0;
++do2:
++              a0 = ((op_t *) srcp)[6];
++              ((op_t *) dstp)[6] = a1;
++do1:
++              a1 = ((op_t *) srcp)[7];
++              ((op_t *) dstp)[7] = a0;
++
++              srcp += 8 * OPSIZ;
++              dstp += 8 * OPSIZ;
++              len -= 8;
++      } while (len != 0);
++
++      /* This is the right position for do0.  Please don't move
++       * it into the loop.
++       */
++do0:
++      ((op_t *) dstp)[0] = a1;
++}
++
++static void _wordcopy_fwd_dest_aligned(long dstp, long srcp, size_t len)
++{
++      op_t a0, a1, a2, a3;
++      int sh_1, sh_2;
++
++      /* Calculate how to shift a word read at the memory operation
++       * aligned srcp to make it aligned for copy.
++       */
++
++      sh_1 = 8 * (srcp % OPSIZ);
++      sh_2 = 8 * OPSIZ - sh_1;
++
++      /* Make SRCP aligned by rounding it down to the beginning of the `op_t'
++       * it points in the middle of.
++       */
++      srcp &= -OPSIZ;
++
++      switch (len % 4) {
++      case 2:
++              a1 = ((op_t *) srcp)[0];
++              a2 = ((op_t *) srcp)[1];
++              srcp -= 1 * OPSIZ;
++              dstp -= 3 * OPSIZ;
++              len += 2;
++              goto do1;
++      case 3:
++              a0 = ((op_t *) srcp)[0];
++              a1 = ((op_t *) srcp)[1];
++              srcp -= 0 * OPSIZ;
++              dstp -= 2 * OPSIZ;
++              len += 1;
++              goto do2;
++      case 0:
++              if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++                      return;
++              a3 = ((op_t *) srcp)[0];
++              a0 = ((op_t *) srcp)[1];
++              srcp -= -1 * OPSIZ;
++              dstp -= 1 * OPSIZ;
++              len += 0;
++              goto do3;
++      case 1:
++              a2 = ((op_t *) srcp)[0];
++              a3 = ((op_t *) srcp)[1];
++              srcp -= -2 * OPSIZ;
++              dstp -= 0 * OPSIZ;
++              len -= 1;
++              if (OP_T_THRES <= 3 * OPSIZ && len == 0)
++                      goto do0;
++              goto do4;                 /* No-op.  */
++      }
++
++      do {
++do4:
++              a0 = ((op_t *) srcp)[0];
++              ((op_t *) dstp)[0] = MERGE(a2, sh_1, a3, sh_2);
++do3:
++              a1 = ((op_t *) srcp)[1];
++              ((op_t *) dstp)[1] = MERGE(a3, sh_1, a0, sh_2);
++do2:
++              a2 = ((op_t *) srcp)[2];
++              ((op_t *) dstp)[2] = MERGE(a0, sh_1, a1, sh_2);
++do1:
++              a3 = ((op_t *) srcp)[3];
++              ((op_t *) dstp)[3] = MERGE(a1, sh_1, a2, sh_2);
++
++              srcp += 4 * OPSIZ;
++              dstp += 4 * OPSIZ;
++              len -= 4;
++      } while (len != 0);
++
++      /* This is the right position for do0.  Please don't move
++       * it into the loop.
++       */
++do0:
++      ((op_t *) dstp)[0] = MERGE(a2, sh_1, a3, sh_2);
++}
++
++#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes)         \
++do {                                                  \
++      size_t __nbytes = (nbytes);                     \
++      while (__nbytes > 0) {                                          \
++              byte __x = ((byte *) src_bp)[0];                \
++              src_bp += 1;                            \
++              __nbytes -= 1;                          \
++              ((byte *) dst_bp)[0] = __x;             \
++              dst_bp += 1;                            \
++      }                                               \
++} while (0)
++
++#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes)                    \
++do {                                                                          \
++      if (src_bp % OPSIZ == 0)                                                \
++              _wordcopy_fwd_aligned(dst_bp, src_bp, (nbytes) / OPSIZ);        \
++      else                                                                    \
++              _wordcopy_fwd_dest_aligned(dst_bp, src_bp, (nbytes) / OPSIZ);   \
++      src_bp += (nbytes) & -OPSIZ;                                            \
++      dst_bp += (nbytes) & -OPSIZ;                                            \
++      (nbytes_left) = (nbytes) % OPSIZ;                                       \
++} while (0)
++
++extern void *__memcpy_aligned(void *dest, const void *src, size_t len);
++void *__memcpy(void *dest, const void *src, size_t len)
++{
++      unsigned long dstp = (long) dest;
++      unsigned long srcp = (long) src;
++
++      /* If there not too few bytes to copy, use word copy.  */
++      if (len >= OP_T_THRES) {
++              if ((len >= FAST_COPY_THRES) && ((dstp & OPSIZ_MASK) == 0) &&
++                      ((srcp & OPSIZ_MASK) == 0)) {
++                      __memcpy_aligned(dest, src, len);
++                      return dest;
++              }
++              /* Copy just a few bytes to make DSTP aligned.  */
++              len -= (-dstp) % OPSIZ;
++              BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ);
++
++              /* Copy from SRCP to DSTP taking advantage of the known alignment of
++               * DSTP.  Number of bytes remaining is put in the third argument,
++               * i.e. in LEN.  This number may vary from machine to machine.
++               */
++              WORD_COPY_FWD(dstp, srcp, len, len);
++      /* Fall out and copy the tail.  */
++      }
++
++      /* There are just a few bytes to copy.  Use byte memory operations.  */
++      BYTE_COPY_FWD(dstp, srcp, len);
++
++      return dest;
++}
++
++void *memcpy(void *dest, const void *src, size_t len) __weak __alias(__memcpy);
diff --git a/target/linux/starfive/patches-6.6/0093-riscv-purgatory-Change-memcpy-to-the-aligned-version.patch b/target/linux/starfive/patches-6.6/0093-riscv-purgatory-Change-memcpy-to-the-aligned-version.patch
new file mode 100644 (file)
index 0000000..0b7418f
--- /dev/null
@@ -0,0 +1,37 @@
+From 41c9e97bb70321f7848bd489e45246a9dc985974 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Sun, 4 Feb 2024 15:27:09 +0800
+Subject: [PATCH 093/116] riscv/purgatory: Change memcpy to the aligned version
+
+Change memcpy to the aligned version, for purgatory.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/purgatory/Makefile | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/arch/riscv/purgatory/Makefile
++++ b/arch/riscv/purgatory/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ OBJECT_FILES_NON_STANDARD := y
+-purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
++purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy_aligned.o memcpy.o memset.o
+ purgatory-y += strcmp.o strlen.o strncmp.o
+ targets += $(purgatory-y)
+@@ -13,9 +13,12 @@ $(obj)/string.o: $(srctree)/lib/string.c
+ $(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE
+       $(call if_changed_rule,cc_o_c)
+-$(obj)/memcpy.o: $(srctree)/arch/riscv/lib/memcpy.S FORCE
++$(obj)/memcpy_aligned.o: $(srctree)/arch/riscv/lib/memcpy_aligned.S FORCE
+       $(call if_changed_rule,as_o_S)
++$(obj)/memcpy.o: $(srctree)/arch/riscv/lib/string.c FORCE
++      $(call if_changed_rule,cc_o_c)
++
+ $(obj)/memset.o: $(srctree)/arch/riscv/lib/memset.S FORCE
+       $(call if_changed_rule,as_o_S)
diff --git a/target/linux/starfive/patches-6.6/0094-Add-16-ISP-controls-remove-the-frame-SYNC-event-to-v.patch b/target/linux/starfive/patches-6.6/0094-Add-16-ISP-controls-remove-the-frame-SYNC-event-to-v.patch
new file mode 100644 (file)
index 0000000..7ce12c6
--- /dev/null
@@ -0,0 +1,856 @@
+From f36d458104101050478e290919ef4f05fbde0b3e Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 3 Jul 2023 16:52:13 +0800
+Subject: [PATCH 094/116] Add 16 ISP controls, remove the frame SYNC event to
+ video7 (SC) These controls are: WB, CAR, CCM, CFA, CTC, DBC, DNYUV, GMARGB,
+ LCCF, OBC, OECF, R2Y, SAT, SHRP, YCRV, SC
+
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c   | 628 ++++++++++++++++++
+ .../platform/starfive/v4l2_driver/stf_video.c |  22 +
+ .../platform/starfive/v4l2_driver/stf_vin.c   |  16 +-
+ include/uapi/linux/jh7110-isp.h               |  48 +-
+ 4 files changed, 706 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -195,6 +195,41 @@ static const char * const test_pattern_m
+       "Color squares w/ rolling bar",
+ };
++enum isp_modules_index {
++      imi_obc = 0,
++      imi_oecf,
++      imi_lccf,
++      imi_awb,
++      imi_dbc,
++      imi_ctc,
++      imi_cfa,
++      imi_car,
++      imi_ccm,
++      imi_gmargb,
++      imi_r2y,
++      imi_ycrv,
++      imi_shrp,
++      imi_dnyuv,
++      imi_sat,
++      imi_sc
++};
++
++#define MODULE_ENABLE_REGISTER0                               0x10
++#define MODULE_ENABLE_REGISTER1                               0xa08
++
++struct module_register_info {
++      __u32 en_reg;
++      __u32 en_nbit;
++      __u32 cfg_reg;
++};
++
++static const struct module_register_info mod_reg_info[] = {
++      {MODULE_ENABLE_REGISTER0, 2, 0x034}, {MODULE_ENABLE_REGISTER0, 4, 0x100}, {MODULE_ENABLE_REGISTER0, 6, 0x050}, {MODULE_ENABLE_REGISTER0, 7, 0x280},
++      {MODULE_ENABLE_REGISTER1, 22, 0xa14}, {MODULE_ENABLE_REGISTER1, 21, 0xa10}, {MODULE_ENABLE_REGISTER1, 1, 0xa1c}, {MODULE_ENABLE_REGISTER1, 2, 0x000},
++      {MODULE_ENABLE_REGISTER1, 3, 0xc40}, {MODULE_ENABLE_REGISTER1, 4, 0xe00}, {MODULE_ENABLE_REGISTER1, 5, 0xe40}, {MODULE_ENABLE_REGISTER1, 19, 0xf00},
++      {MODULE_ENABLE_REGISTER1, 7, 0xe80}, {MODULE_ENABLE_REGISTER1, 17, 0xc00}, {MODULE_ENABLE_REGISTER1, 8, 0xa30}, {MODULE_ENABLE_REGISTER0, 17, 0x09c}
++};
++
+ #define ISP_TEST_ENABLE                       BIT(7)
+ #define ISP_TEST_ROLLING              BIT(6)  /* rolling horizontal bar */
+ #define ISP_TEST_TRANSPARENT          BIT(5)
+@@ -260,6 +295,401 @@ static int isp_g_volatile_ctrl(struct v4
+       return 0;
+ }
++#define CREATE_REG_VALUE_FUNCTION(type)       \
++      static u32 create_reg_value_##type(const type * value, s32 size, u32 mask, s32 nbits) { \
++      s32 npos = 0;   \
++      u32 res = 0;    \
++      s32 sz = size;  \
++      s32 i = 0;      \
++      if(size * nbits > 32) sz = 32 / nbits;  \
++      for(i = 0; i < sz; i++, npos += nbits, value++) res |= (u32)(value[0] & mask) << npos;  \
++      return res;     \
++}
++
++CREATE_REG_VALUE_FUNCTION(u8);
++CREATE_REG_VALUE_FUNCTION(u16);
++#define CREATE_REG_VALUE(type, value, size, mask, nbits) create_reg_value_##type(value, size, mask, nbits)
++
++#define FILL_ISP_REGS_FUNC(type)      \
++static void fill_isp_regs_##type(void __iomem *ispbase, u32 offset, const type * value, s32 size, u32 mask, u32 nbits) {      \
++      s32 i;  \
++      for(i = 0; i < size; i++, value++)      \
++              reg_write(ispbase, offset + i * 4, (u32)(value[0] & mask) << nbits);    \
++}
++FILL_ISP_REGS_FUNC(u32);
++FILL_ISP_REGS_FUNC(u8);
++FILL_ISP_REGS_FUNC(u16);
++
++#define FILL_ISP_REGS(type, ispbase, offset, value, size, mask, nbits)        \
++      fill_isp_regs_##type(ispbase, offset, value, size, mask, nbits)
++
++static int isp_set_ctrl_wb(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_awb];
++      const struct jh7110_isp_wb_setting * setting = (const struct jh7110_isp_wb_setting *)value;
++      const struct jh7110_isp_wb_gain * gains = &setting->gains;
++      u32 r_g = (((u32)gains->gain_r << 16) | gains->gain_r) & 0x03ff03ff;
++      u32 g_g = (((u32)gains->gain_g << 16) | gains->gain_g) & 0x03ff03ff;
++      u32 b_g = (((u32)gains->gain_b << 16) | gains->gain_b) & 0x03ff03ff;
++      u32 reg_addr = reg_info->cfg_reg + 16 * sizeof(u32);
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++
++      reg_write(ispbase, reg_addr, r_g);
++      reg_write(ispbase, reg_addr + 1 * 4, r_g);
++      reg_write(ispbase, reg_addr + 2 * 4, g_g);
++      reg_write(ispbase, reg_addr + 3 * 4, g_g);
++      reg_write(ispbase, reg_addr + 4 * 4, g_g);
++      reg_write(ispbase, reg_addr + 5 * 4, g_g);
++      reg_write(ispbase, reg_addr + 6 * 4, b_g);
++      reg_write(ispbase, reg_addr + 7 * 4, b_g);
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++      return 0;
++}
++
++static int isp_set_ctrl_car(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_car];
++      const struct jh7110_isp_car_setting * setting = (const struct jh7110_isp_car_setting *)value;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++      return 0;
++}
++
++static int isp_set_ctrl_ccm(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_ccm];
++      const struct jh7110_isp_ccm_setting * setting = (const struct jh7110_isp_ccm_setting *)value;
++      const struct jh7110_isp_ccm_smlow * ccm = (const struct jh7110_isp_ccm_smlow *)(&(setting->ccm_smlow));
++      u32 reg_addr = reg_info->cfg_reg + 12 * sizeof(u32);
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++
++      reg_write(ispbase, reg_info->cfg_reg, 6 << 16);
++      FILL_ISP_REGS(u32, ispbase, reg_addr, (u32 *)ccm, 12, 0x7ff, 0);
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++      return 0;
++}
++
++static int isp_set_ctrl_cfa(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_cfa];
++      const struct jh7110_isp_cfa_setting * setting = (const struct jh7110_isp_cfa_setting *)value;
++      const struct jh7110_isp_cfa_params * cfg = (const struct jh7110_isp_cfa_params *)(&(setting->settings));
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++
++      reg_write(ispbase, reg_addr, ((u32)(cfg->cross_cov & 0x3) << 4) | (cfg->hv_width & 0xf));
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_ctc(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_ctc];
++      const struct jh7110_isp_ctc_setting * setting = (const struct jh7110_isp_ctc_setting *)value;
++      const struct jh7110_isp_ctc_params * cfg = (const struct jh7110_isp_ctc_params *)(&(setting->settings));
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      u32 reg_value = (u32)(((cfg->saf_mode & 1) << 1) | (cfg->daf_mode & 1)) << 30;
++
++      reg_value |= ((u32)(cfg->max_gt & 0x3ff) << 16) | (cfg->min_gt & 0x3ff);
++      reg_write(ispbase, reg_addr, reg_value);
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_dbc(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_dbc];
++      const struct jh7110_isp_dbc_setting * setting = (const struct jh7110_isp_dbc_setting *)value;
++      const struct jh7110_isp_dbc_params * cfg = (const struct jh7110_isp_dbc_params *)(&(setting->settings));
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++
++      reg_write(ispbase, reg_addr, ((u32)(cfg->bad_gt & 0x3ff) << 16) | (cfg->bad_xt & 0x3ff));
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_dnyuv(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_dnyuv];
++      const struct jh7110_isp_dnyuv_setting * setting = (const struct jh7110_isp_dnyuv_setting *)value;
++      const struct jh7110_isp_dnyuv_params * cfg = (const struct jh7110_isp_dnyuv_params *)(&(setting->settings));
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++
++      reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, cfg->y_sweight, 6, 0x7, 4));
++      reg_write(ispbase, reg_addr + 4, CREATE_REG_VALUE(u8, &cfg->y_sweight[6], 4, 0x7, 4));
++      reg_write(ispbase, reg_addr + 8, CREATE_REG_VALUE(u8, cfg->uv_sweight, 6, 0x7, 4));
++      reg_write(ispbase, reg_addr + 12, CREATE_REG_VALUE(u8, &cfg->uv_sweight[6], 4, 0x7, 4));
++      reg_write(ispbase, reg_addr + 16, CREATE_REG_VALUE(u16, &cfg->y_curve[0], 2, 0x3ff, 16));
++      reg_write(ispbase, reg_addr + 20, CREATE_REG_VALUE(u16, &cfg->y_curve[2], 2, 0x3ff, 16));
++      reg_write(ispbase, reg_addr + 24, CREATE_REG_VALUE(u16, &cfg->y_curve[4], 2, 0x3ff, 16));
++      reg_write(ispbase, reg_addr + 28, CREATE_REG_VALUE(u16, &cfg->uv_curve[0], 2, 0x3ff, 16));
++      reg_write(ispbase, reg_addr + 32, CREATE_REG_VALUE(u16, &cfg->uv_curve[2], 2, 0x3ff, 16));
++      reg_write(ispbase, reg_addr + 36, CREATE_REG_VALUE(u16, &cfg->uv_curve[4], 2, 0x3ff, 16));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_gmargb(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_gmargb];
++      const struct jh7110_isp_gmargb_setting * setting = (const struct jh7110_isp_gmargb_setting *)value;
++      const struct jh7110_isp_gmargb_point * curve = setting->curve;
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      s32 i;
++      
++      for(i = 0; i < 15; i++, curve++, reg_addr += 4)
++              reg_write(ispbase, reg_addr, ((u32)curve->sg_val << 16) | (curve->g_val & 0x3ff));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++      return 0;
++}
++
++static int isp_set_ctrl_lccf(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_lccf];
++      const struct jh7110_isp_lccf_setting * setting = (const struct jh7110_isp_lccf_setting *)value;
++      const s16 * params = (s16 *)(&setting->r_param);
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      s32 i;
++      
++      reg_write(ispbase, reg_addr, ((u32)(setting->circle.center_y & 0x7fff) << 16) | (setting->circle.center_x & 0x7fff));
++      reg_write(ispbase, reg_addr + 8, setting->circle.radius & 0xf);
++      reg_addr += 0x90;
++      for(i = 0; i < 4; i++, reg_addr += 4, params += 2)
++              reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x1fff, 16));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_obc(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_obc];
++      const struct jh7110_isp_blacklevel_setting * setting = (const struct jh7110_isp_blacklevel_setting *)value;
++      const u8 * params = (u8 *)setting->gain;
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      s32 i;
++      
++      reg_write(ispbase, reg_addr, ((u32)(setting->win_size.height & 0xf) << 4) | (setting->win_size.width & 0xf));
++
++      reg_addr += 0x2ac;      //0x2e0
++      for(i = 0; i < 8; i++, reg_addr += 4, params += 4)
++              reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 4, 0xff, 8));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_oecf(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_oecf];
++      const struct jh7110_isp_oecf_setting * setting = (const struct jh7110_isp_oecf_setting *)value;
++      const struct jh7110_isp_oecf_point * pts = (struct jh7110_isp_oecf_point *)(setting->r_curve);
++      u32 reg_x_addr = reg_info->cfg_reg;
++      u32 reg_y_addr = reg_info->cfg_reg + 0x080;
++      u32 reg_s_addr = reg_info->cfg_reg + 0x100;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      u32 x, y, slope;
++      s32 i;
++      
++      for(i = 0; i < 32; i++, reg_x_addr += 4, reg_y_addr += 4, reg_s_addr += 4) {
++              x = pts->x & 0x3ff;
++              y = pts->y & 0x3ff;
++              slope = pts->slope & 0x3ff;
++              pts++;
++              x |= ((pts->x & 0x3ff) << 16);
++              y |= ((pts->y & 0x3ff) << 16);
++              slope |= ((pts->slope & 0x3ff) << 16);
++              pts++;
++
++              reg_write(ispbase, reg_x_addr, x);
++              reg_write(ispbase, reg_y_addr, y);
++              reg_write(ispbase, reg_s_addr, slope);
++      }
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++
++      return 0;
++}
++
++static int isp_set_ctrl_r2y(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_r2y];
++      const struct jh7110_isp_r2y_setting * setting = (const struct jh7110_isp_r2y_setting *)value;
++      const s16 * params = setting->matrix.m;
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      s32 i;
++      
++      for(i = 0; i < 9; i++, reg_addr += 4)
++              reg_write(ispbase, reg_addr, (u32)(params[i] & 0x1ff));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++
++static int isp_set_ctrl_sat(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_sat];
++      const struct jh7110_isp_sat_setting * setting = (const struct jh7110_isp_sat_setting *)value;
++      const u16 * params = (u16 *)(&setting->sat_info);
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      s32 i;
++      
++      for(i = 0; i < 3; i++, reg_addr += 4, params += 2)
++              reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0xfff, 16));
++      reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, &setting->hue_info.cos, 2, 0x3ff, 16));
++      reg_addr += 4;
++      reg_write(ispbase, reg_addr, setting->sat_info.cmsf & 0xf);
++
++      params = (u16 *)(&setting->curve);
++      reg_addr += 0x14;               // 0xa54
++      for(i = 0; i < 2; i++, reg_addr += 4, params += 2)
++              reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x3fff, 16));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_shrp(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_shrp];
++      const struct jh7110_isp_sharp_setting * setting = (const struct jh7110_isp_sharp_setting *)value;
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      s32 i;
++      
++      for(i = 0; i < 4; i++, reg_addr += 4)
++              reg_write(ispbase, reg_addr, ((u32)(setting->strength.diff[i] & 0x3ff) << 16) | ((u32)(setting->weight.weight[i] & 0xf) << 8));
++      FILL_ISP_REGS(u8, ispbase, reg_addr, (u8 *)(&setting->weight.weight[4]), 15 - 4, 0xf, 8);
++      reg_addr += (15 - 4) * 4;
++
++      for(i = 0; i < 3; i++, reg_addr += 4)
++              reg_write(ispbase, reg_addr, ((u32)(setting->strength.f[i] & 0x7f) << 24) | (setting->strength.s[i] & 0x1fffff));
++
++      reg_addr += 3 * 4;
++      reg_write(ispbase, reg_addr, ((u32)(setting->pdirf & 0xf) << 28) | ((u32)(setting->ndirf & 0xf) << 24) | (setting->weight.recip_wei_sum & 0x3fffff));
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_ycrv(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_ycrv];
++      const struct jh7110_isp_ycrv_setting * setting = (const struct jh7110_isp_ycrv_setting *)value;
++      u32 reg_addr = reg_info->cfg_reg;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      
++      FILL_ISP_REGS(u16, ispbase, reg_addr, (u16 *)(setting->curve.y), 64, 0x3ff, 0);
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
++static int isp_set_ctrl_sc(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct module_register_info * reg_info = &mod_reg_info[imi_sc];
++      const struct jh7110_isp_sc_setting * setting = (const struct jh7110_isp_sc_setting *)value;
++      const u8 * params = setting->awb_config.awb_cw;
++      u32 reg_addr = 0x00;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      u32 weight_cfg[6] = {0};
++      u32 * weight = weight_cfg;
++      u32 * w_diff = weight_cfg + 2;
++      s32 i;
++
++      // AF register
++      reg_write(ispbase, 0xc0, 
++              ((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
++              ((u32)(setting->af_config.es_ver_thr & 0xff) << 8) |
++              ((setting->af_config.ver_en & 0x1) << 3) |
++              ((setting->af_config.hor_en & 0x1) << 2) |
++              ((setting->af_config.es_sum_mode & 0x1) << 1) |
++              (setting->af_config.es_hor_mode & 0x1));
++
++      // AWB weight sum register
++      reg_write(ispbase, 0x5d0, CREATE_REG_VALUE(u8, &setting->awb_config.ws_config.awb_ws_rl, 4, 0xff, 8));
++      reg_write(ispbase, 0x5d4, CREATE_REG_VALUE(u8, &setting->awb_config.ws_config.awb_ws_gbl, 4, 0xff, 8));
++
++      // AWB weight value point
++      reg_addr = 0x4d0;
++      for(i = 0; i < 13; i++) {
++              reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 8, 0xf, 4));
++              reg_addr += 4;
++              params += 8;
++              reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 5, 0xf, 4));
++              reg_addr += 4;
++              params += 5;
++      }
++
++      // AWB intensity weight curve register
++      reg_addr = 0x538;
++      for(i = 0; i < 4; i++) {
++              weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++              w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++      }
++      for(w_diff++; i < 8; i++) {
++              weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++              w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++      }
++      for(weight++, w_diff++; i < 12; i++) {
++              weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++              w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++      }
++      for(w_diff++; i < 16; i++) {
++              weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
++              w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
++      }
++
++      FILL_ISP_REGS(u32, ispbase, reg_addr, weight_cfg, 6, 0xffffffff, 0);
++
++      reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
++      
++      return 0;
++}
++
+ static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+       struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+@@ -310,10 +740,52 @@ static int isp_s_ctrl(struct v4l2_ctrl *
+               ret = isp_set_ctrl_vflip(isp_dev, ctrl->val);
+               break;
+       case V4L2_CID_USER_JH7110_ISP_WB_SETTING:
++              ret = isp_set_ctrl_wb(isp_dev, ctrl->p_new.p_u8);
+               break;
+       case V4L2_CID_USER_JH7110_ISP_CAR_SETTING:
++              ret = isp_set_ctrl_car(isp_dev, ctrl->p_new.p_u8);
+               break;
+       case V4L2_CID_USER_JH7110_ISP_CCM_SETTING:
++              ret = isp_set_ctrl_ccm(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_CFA_SETTING:
++              ret = isp_set_ctrl_cfa(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_CTC_SETTING:
++              ret = isp_set_ctrl_ctc(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_DBC_SETTING:
++              ret = isp_set_ctrl_dbc(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING:
++              ret = isp_set_ctrl_dnyuv(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING:
++              ret = isp_set_ctrl_gmargb(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_LCCF_SETTING:
++              ret = isp_set_ctrl_lccf(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_OBC_SETTING:
++              ret = isp_set_ctrl_obc(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_OECF_SETTING:
++              ret = isp_set_ctrl_oecf(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_R2Y_SETTING:
++              ret = isp_set_ctrl_r2y(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_SAT_SETTING:
++              ret = isp_set_ctrl_sat(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_SHRP_SETTING:
++              ret = isp_set_ctrl_shrp(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_YCRV_SETTING:
++              ret = isp_set_ctrl_ycrv(isp_dev, ctrl->p_new.p_u8);
++              break;
++      case V4L2_CID_USER_JH7110_ISP_STAT_SETTING:
++              ret = isp_set_ctrl_sc(isp_dev, ctrl->p_new.p_u8);
+               break;
+       default:
+               ret = -EINVAL;
+@@ -365,6 +837,162 @@ struct v4l2_ctrl_config isp_ctrl[] = {
+               .dims[0]        = sizeof(struct jh7110_isp_ccm_setting),
+               .flags          = 0,
+       },
++      [3] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "CFA Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_CFA_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_cfa_setting),
++              .flags          = 0,
++      },
++      [4] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "CTC Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_CTC_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_ctc_setting),
++              .flags          = 0,
++      },
++      [5] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "CTC Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_DBC_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_dbc_setting),
++              .flags          = 0,
++      },
++      [6] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "DNYUV Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_dnyuv_setting),
++              .flags          = 0,
++      },
++      [7] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "DNYUV Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_gmargb_setting),
++              .flags          = 0,
++      },
++      [8] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "LCCF Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_LCCF_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_lccf_setting),
++              .flags          = 0,
++      },
++      [9] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "OBC Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_OBC_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_blacklevel_setting),
++              .flags          = 0,
++      },
++      [10] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "OECF Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_OECF_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_oecf_setting),
++              .flags          = 0,
++      },
++      [11] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "R2Y Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_R2Y_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_r2y_setting),
++              .flags          = 0,
++      },
++      [12] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "SAT Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_SAT_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_sat_setting),
++              .flags          = 0,
++      },
++      [13] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "SAT Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_SHRP_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_sharp_setting),
++              .flags          = 0,
++      },
++      [14] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "YCRV Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_YCRV_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_ycrv_setting),
++              .flags          = 0,
++      },
++      [15] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "SC Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_STAT_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_sc_setting),
++              .flags          = 0,
++      },
+ };
+ static int isp_init_controls(struct stf_isp_dev *isp_dev)
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_video.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+@@ -1281,6 +1281,22 @@ int video_s_selection(struct file *file,
+       return ret;
+ }
++static int stf_video_subscribe_event(struct v4l2_fh *fh,
++                                 const struct v4l2_event_subscription *sub)
++{
++      switch (sub->type) {
++      case V4L2_EVENT_FRAME_SYNC:
++              return v4l2_event_subscribe(fh, sub, 2, NULL);
++              //int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
++              //pr_info("subscribe ret: %d\n", ret);
++              //return ret;
++      default:
++              return v4l2_ctrl_subscribe_event(fh, sub);
++              //st_debug(ST_VIN, "unsupport subscribe_event\n");
++              //return -EINVAL;
++      }
++}
++
+ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
+       .vidioc_querycap                = video_querycap,
+       .vidioc_enum_fmt_vid_cap        = video_enum_fmt,
+@@ -1305,6 +1321,8 @@ static const struct v4l2_ioctl_ops stf_v
+       .vidioc_s_parm                  = video_s_parm,
+       .vidioc_s_selection             = video_s_selection,
+       .vidioc_g_selection             = video_g_selection,
++      .vidioc_subscribe_event                 = stf_video_subscribe_event,
++      .vidioc_unsubscribe_event               = v4l2_event_unsubscribe,
+ };
+ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_mp = {
+@@ -1331,6 +1349,8 @@ static const struct v4l2_ioctl_ops stf_v
+       .vidioc_s_parm                  = video_s_parm,
+       .vidioc_s_selection             = video_s_selection,
+       .vidioc_g_selection             = video_g_selection,
++      .vidioc_subscribe_event                 = stf_video_subscribe_event,
++      .vidioc_unsubscribe_event               = v4l2_event_unsubscribe,
+ };
+ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_out = {
+@@ -1350,6 +1370,8 @@ static const struct v4l2_ioctl_ops stf_v
+       .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
++      .vidioc_subscribe_event                 = stf_video_subscribe_event,
++      .vidioc_unsubscribe_event               = v4l2_event_unsubscribe,
+ };
+ static int video_open(struct file *file)
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+@@ -1145,9 +1145,12 @@ static void vin_buffer_done(struct vin_l
+       spin_lock_irqsave(&line->output_lock, flags);
+       while ((ready_buf = vin_buf_get_ready(output))) {
+-              if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
++              //if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
++              if (line->id == VIN_LINE_ISP_SCD_Y) {
+                       event.u.frame_sync.frame_sequence = output->sequence;
+-                      v4l2_event_queue(line->subdev.devnode, &event);
++                      v4l2_event_queue(&(line->video_out.vdev), &event);
++                      //v4l2_event_queue(line->subdev.devnode, &event);
++                      //pr_info("----------frame sync-----------\n");
+               }
+               ready_buf->vb.vb2_buf.timestamp = ts;
+@@ -1346,7 +1349,10 @@ static int stf_vin_subscribe_event(struc
+ {
+       switch (sub->type) {
+       case V4L2_EVENT_FRAME_SYNC:
+-              return v4l2_event_subscribe(fh, sub, 0, NULL);
++              //return v4l2_event_subscribe(fh, sub, 2, NULL);
++              int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
++              pr_info("subscribe ret: %d\n", ret);
++              return ret;
+       default:
+               st_debug(ST_VIN, "unsupport subscribe_event\n");
+               return -EINVAL;
+@@ -1355,8 +1361,8 @@ static int stf_vin_subscribe_event(struc
+ static const struct v4l2_subdev_core_ops vin_core_ops = {
+       .s_power = vin_set_power,
+-      .subscribe_event = stf_vin_subscribe_event,
+-      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++      //.subscribe_event = stf_vin_subscribe_event,
++      //.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+ };
+ static const struct v4l2_subdev_video_ops vin_video_ops = {
+--- a/include/uapi/linux/jh7110-isp.h
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -15,6 +15,8 @@
+ #include <linux/v4l2-controls.h>
++#define V4L2_CID_USER_JH7110_ISP_BASE                         (V4L2_CID_USER_BASE + 0x1170)
++
+ #define V4L2_CID_USER_JH7110_ISP_WB_SETTING   \
+                               (V4L2_CID_USER_JH7110_ISP_BASE + 0x0001)
+ #define V4L2_CID_USER_JH7110_ISP_CAR_SETTING  \
+@@ -45,6 +47,8 @@
+                               (V4L2_CID_USER_JH7110_ISP_BASE + 0x000e)
+ #define V4L2_CID_USER_JH7110_ISP_YCRV_SETTING \
+                               (V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
++#define V4L2_CID_USER_JH7110_ISP_STAT_SETTING \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0010)
+ struct jh7110_isp_wb_gain {
+       __u16 gain_r;
+@@ -202,13 +206,13 @@ struct jh7110_isp_sat_curve {
+ };
+ struct jh7110_isp_sat_hue_info {
+-      __s16 sin;
+       __s16 cos;
++      __s16 sin;
+ };
+ struct jh7110_isp_sat_info {
+       __s16 gain_cmab;
+-      __s16 gain_cmad;
++      __s16 gain_cmmd;
+       __s16 threshold_cmb;
+       __s16 threshold_cmd;
+       __s16 offset_u;
+@@ -230,7 +234,8 @@ struct jh7110_isp_sharp_weight {
+ struct jh7110_isp_sharp_strength {
+       __s16 diff[4];
+-      __s16 f[4];
++      __s16 f[3];
++      __s32 s[3];
+ };
+ struct jh7110_isp_sharp_setting {
+@@ -250,4 +255,41 @@ struct jh7110_isp_ycrv_setting {
+       struct jh7110_isp_ycrv_curve curve;
+ };
++struct jh7110_isp_sc_af_config {
++      __u8 es_hor_mode;
++      __u8 es_sum_mode;
++      __u8 hor_en;
++      __u8 ver_en;
++      __u8 es_ver_thr;
++      __u16 es_hor_thr;
++};
++
++struct jh7110_isp_sc_awb_ws {
++      __u8 awb_ws_rl;
++      __u8 awb_ws_ru;
++      __u8 awb_ws_grl;
++      __u8 awb_ws_gru;
++      __u8 awb_ws_gbl;
++      __u8 awb_ws_gbu;
++      __u8 awb_ws_bl;
++      __u8 awb_ws_bu;
++};
++
++struct jh7110_isp_sc_awb_point {
++      __u16 intensity;
++      __u8 weight;
++};
++
++struct jh7110_isp_sc_awb_config {
++      struct jh7110_isp_sc_awb_ws ws_config;
++      __u8 awb_cw[169];
++      struct jh7110_isp_sc_awb_point pts[17];
++};
++
++struct jh7110_isp_sc_setting {
++      __u32 enabled;
++      struct jh7110_isp_sc_af_config af_config;
++      struct jh7110_isp_sc_awb_config awb_config;
++};
++
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0095-Expand-2-bytes-after-the-SC-buffer-for-the-AE-AWB-fl.patch b/target/linux/starfive/patches-6.6/0095-Expand-2-bytes-after-the-SC-buffer-for-the-AE-AWB-fl.patch
new file mode 100644 (file)
index 0000000..d023674
--- /dev/null
@@ -0,0 +1,249 @@
+From 5d61c6fd10144605238311d68c99449c4667a345 Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 7 Aug 2023 10:38:36 +0800
+Subject: [PATCH 095/116] Expand 2 bytes after the SC buffer for the AE/AWB
+ flag and copy the histogram data to the SC buffer.
+
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c   | 37 ++++++++++++++++++-
+ .../platform/starfive/v4l2_driver/stf_isp.h   |  2 +-
+ .../platform/starfive/v4l2_driver/stf_video.c |  5 ---
+ .../platform/starfive/v4l2_driver/stf_vin.c   | 36 +++++++++---------
+ include/uapi/linux/jh7110-isp.h               | 33 +++++++++++++++++
+ 5 files changed, 87 insertions(+), 26 deletions(-)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -323,6 +323,13 @@ FILL_ISP_REGS_FUNC(u16);
+ #define FILL_ISP_REGS(type, ispbase, offset, value, size, mask, nbits)        \
+       fill_isp_regs_##type(ispbase, offset, value, size, mask, nbits)
++static void fill_regs_with_zero(void __iomem *ispbase, u32 offset, u32 size)
++{
++      u32 i;
++      for(i = 0; i < size; i++, offset += 4)
++              reg_write(ispbase, offset, 0);
++}
++
+ static int isp_set_ctrl_wb(struct stf_isp_dev *isp_dev, const void * value)
+ {
+       const struct module_register_info * reg_info = &mod_reg_info[imi_awb];
+@@ -335,6 +342,8 @@ static int isp_set_ctrl_wb(struct stf_is
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
++      fill_regs_with_zero(ispbase, reg_info->cfg_reg, 16);
++
+       reg_write(ispbase, reg_addr, r_g);
+       reg_write(ispbase, reg_addr + 1 * 4, r_g);
+       reg_write(ispbase, reg_addr + 2 * 4, g_g);
+@@ -370,8 +379,13 @@ static int isp_set_ctrl_ccm(struct stf_i
+       void __iomem *ispbase = vin->isp_base;
+       reg_write(ispbase, reg_info->cfg_reg, 6 << 16);
++      fill_regs_with_zero(ispbase, reg_info->cfg_reg + 4, 11);
++
+       FILL_ISP_REGS(u32, ispbase, reg_addr, (u32 *)ccm, 12, 0x7ff, 0);
++      reg_addr += 12 * 4;
++      fill_regs_with_zero(ispbase, reg_addr, 2);
++
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+       return 0;
+@@ -640,6 +654,27 @@ static int isp_set_ctrl_sc(struct stf_is
+       u32 * w_diff = weight_cfg + 2;
+       s32 i;
++      // SC dumping axi id
++      reg_write(ispbase, 0x9c, 1 << 24);
++
++      // SC frame crop
++      reg_write(ispbase, 0xb8, ((u32)(setting->crop_config.v_start) << 16) | setting->crop_config.h_start);
++
++      // SC config1
++      reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) | 
++              ((u32)(setting->crop_config.sw_height) << 8) | setting->crop_config.sw_width);
++
++      // SC decimation config
++      reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) | 
++              ((u32)(setting->crop_config.hkeep) << 8) | setting->crop_config.hperiod);
++
++      // SC AWB pixel sum config
++      reg_write(ispbase, 0xc4, CREATE_REG_VALUE(u8, &setting->awb_config.ws_ps_config.awb_ps_rl, 4, 0xff, 8));
++      reg_write(ispbase, 0xc8, CREATE_REG_VALUE(u8, &setting->awb_config.ws_ps_config.awb_ps_bl, 4, 0xff, 8));
++      reg_write(ispbase, 0xcc, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grl, 2, 0xffff, 16));
++      reg_write(ispbase, 0xd0, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_gbl, 2, 0xffff, 16));
++      reg_write(ispbase, 0xd4, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grbl, 2, 0xffff, 16));
++
+       // AF register
+       reg_write(ispbase, 0xc0, 
+               ((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
+@@ -686,7 +721,7 @@ static int isp_set_ctrl_sc(struct stf_is
+       FILL_ISP_REGS(u32, ispbase, reg_addr, weight_cfg, 6, 0xffffffff, 0);
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+@@ -18,7 +18,7 @@
+ #define ISP_SCD_BUFFER_SIZE     (19 * 256 * 4)  // align 128
+ #define ISP_YHIST_BUFFER_SIZE   (64 * 4)
+-#define ISP_SCD_Y_BUFFER_SIZE   (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)
++#define ISP_SCD_Y_BUFFER_SIZE   (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE + 2)
+ #define ISP_RAW_DATA_BITS       12
+ #define SCALER_RATIO_MAX        1  // no compose function
+ #define STF_ISP_REG_OFFSET_MAX  0x0FFF
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_video.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+@@ -1287,13 +1287,8 @@ static int stf_video_subscribe_event(str
+       switch (sub->type) {
+       case V4L2_EVENT_FRAME_SYNC:
+               return v4l2_event_subscribe(fh, sub, 2, NULL);
+-              //int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
+-              //pr_info("subscribe ret: %d\n", ret);
+-              //return ret;
+       default:
+               return v4l2_ctrl_subscribe_event(fh, sub);
+-              //st_debug(ST_VIN, "unsupport subscribe_event\n");
+-              //return -EINVAL;
+       }
+ }
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+@@ -1147,6 +1147,17 @@ static void vin_buffer_done(struct vin_l
+       while ((ready_buf = vin_buf_get_ready(output))) {
+               //if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
+               if (line->id == VIN_LINE_ISP_SCD_Y) {
++#define ADDR_REG_YHIST_ACC_0               0x0D00
++                      struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
++                      struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
++                      void __iomem *ispbase = vin->isp_base;
++                      u32 y_hist_reg_addr = ADDR_REG_YHIST_ACC_0;
++                      u32 * y_hist_addr = (u32 *)ready_buf->vaddr_sc;
++                      s32 i = 0;
++
++                      for(i = 0; i < 64; i++, y_hist_reg_addr += 4)
++                              y_hist_addr[i] = reg_read(ispbase, y_hist_reg_addr);
++
+                       event.u.frame_sync.frame_sequence = output->sequence;
+                       v4l2_event_queue(&(line->video_out.vdev), &event);
+                       //v4l2_event_queue(line->subdev.devnode, &event);
+@@ -1246,9 +1257,14 @@ static void vin_change_buffer(struct vin
+                       scd_type = vin_dev->hw_ops->vin_isp_get_scd_type(vin_dev);
+                       ready_buf->vb.flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
+                       if (scd_type == AWB_TYPE)
++                      {
+                               ready_buf->vb.flags |= V4L2_BUF_FLAG_PFRAME;
+-                      else
++                              *((u16 *)(ready_buf->vaddr_sc + ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)) = 0xffff;
++                      }else{
+                               ready_buf->vb.flags |= V4L2_BUF_FLAG_BFRAME;
++                              *((u16 *)(ready_buf->vaddr_sc + ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)) = 0;
++                      }
++
+                       if (!output->frame_skip) {
+                               output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
+                               scd_type = scd_type == AWB_TYPE ? OECF_TYPE : AWB_TYPE;
+@@ -1343,26 +1359,8 @@ static int vin_link_setup(struct media_e
+       return 0;
+ }
+-static int stf_vin_subscribe_event(struct v4l2_subdev *sd,
+-                                 struct v4l2_fh *fh,
+-                                 struct v4l2_event_subscription *sub)
+-{
+-      switch (sub->type) {
+-      case V4L2_EVENT_FRAME_SYNC:
+-              //return v4l2_event_subscribe(fh, sub, 2, NULL);
+-              int ret = v4l2_event_subscribe(fh, sub, 2, NULL);
+-              pr_info("subscribe ret: %d\n", ret);
+-              return ret;
+-      default:
+-              st_debug(ST_VIN, "unsupport subscribe_event\n");
+-              return -EINVAL;
+-      }
+-}
+-
+ static const struct v4l2_subdev_core_ops vin_core_ops = {
+       .s_power = vin_set_power,
+-      //.subscribe_event = stf_vin_subscribe_event,
+-      //.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+ };
+ static const struct v4l2_subdev_video_ops vin_video_ops = {
+--- a/include/uapi/linux/jh7110-isp.h
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -255,6 +255,17 @@ struct jh7110_isp_ycrv_setting {
+       struct jh7110_isp_ycrv_curve curve;
+ };
++struct jh7110_isp_sc_config {
++      __u16 h_start;
++      __u16 v_start;
++      __u8 sw_width;
++      __u8 sw_height;
++      __u8 hperiod;
++      __u8 hkeep;
++      __u8 vperiod;
++      __u8 vkeep;
++};
++
+ struct jh7110_isp_sc_af_config {
+       __u8 es_hor_mode;
+       __u8 es_sum_mode;
+@@ -264,6 +275,23 @@ struct jh7110_isp_sc_af_config {
+       __u16 es_hor_thr;
+ };
++struct jh7110_isp_sc_awb_ps {
++      __u8 awb_ps_rl;
++      __u8 awb_ps_ru;
++      __u8 awb_ps_gl;
++      __u8 awb_ps_gu;
++      __u8 awb_ps_bl;
++      __u8 awb_ps_bu;
++      __u8 awb_ps_yl;
++      __u8 awb_ps_yu;
++      __u16 awb_ps_grl;
++      __u16 awb_ps_gru;
++      __u16 awb_ps_gbl;
++      __u16 awb_ps_gbu;
++      __u16 awb_ps_grbl;
++      __u16 awb_ps_grbu;
++};
++
+ struct jh7110_isp_sc_awb_ws {
+       __u8 awb_ws_rl;
+       __u8 awb_ws_ru;
+@@ -275,12 +303,16 @@ struct jh7110_isp_sc_awb_ws {
+       __u8 awb_ws_bu;
+ };
++
+ struct jh7110_isp_sc_awb_point {
+       __u16 intensity;
+       __u8 weight;
+ };
+ struct jh7110_isp_sc_awb_config {
++      struct jh7110_isp_sc_awb_ps ws_ps_config;
++      __u8 awb_ps_grb_ba;
++      __u8 sel;
+       struct jh7110_isp_sc_awb_ws ws_config;
+       __u8 awb_cw[169];
+       struct jh7110_isp_sc_awb_point pts[17];
+@@ -288,6 +320,7 @@ struct jh7110_isp_sc_awb_config {
+ struct jh7110_isp_sc_setting {
+       __u32 enabled;
++      struct jh7110_isp_sc_config crop_config;
+       struct jh7110_isp_sc_af_config af_config;
+       struct jh7110_isp_sc_awb_config awb_config;
+ };
diff --git a/target/linux/starfive/patches-6.6/0096-Add-ISP-control-for-video2-and-video3.patch b/target/linux/starfive/patches-6.6/0096-Add-ISP-control-for-video2-and-video3.patch
new file mode 100644 (file)
index 0000000..5737fb2
--- /dev/null
@@ -0,0 +1,117 @@
+From 04eff0f76091015cbecd39de41d45c493e7a91db Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 30 Oct 2023 16:09:58 +0800
+Subject: [PATCH 096/116] Add ISP control for video2 and video3.
+
+Signed-off-by: zejian.su <zejian.su@starfivetech.com>
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c   | 46 +++++++++++++++++++
+ include/uapi/linux/jh7110-isp.h               | 23 ++++++++++
+ 2 files changed, 69 insertions(+)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -725,6 +725,24 @@ static int isp_set_ctrl_sc(struct stf_is
+       return 0;
+ }
++static int isp_set_ctrl_outss(struct stf_isp_dev *isp_dev, const void * value)
++{
++      const struct jh7110_isp_outss_setting * setting = (const struct jh7110_isp_outss_setting *)value;
++      struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
++      void __iomem *ispbase = vin->isp_base;
++      u32 reg_addr = !setting->which ? 0xa9c : 0xab4;
++
++      if(!setting->stride)
++              return 0;
++
++      // Output Image Stride Register, 8-byte(64bit) granularity.
++      reg_write(ispbase, reg_addr, setting->stride);
++      reg_write(ispbase, reg_addr + 4, ((setting->hsm << 16) | (setting->hsm & 0x3)));
++      reg_write(ispbase, reg_addr + 8, ((setting->vsm << 16) | (setting->vsm & 0x3)));
++
++      return 0;
++}
++
+ static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+       struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+@@ -822,6 +840,10 @@ static int isp_s_ctrl(struct v4l2_ctrl *
+       case V4L2_CID_USER_JH7110_ISP_STAT_SETTING:
+               ret = isp_set_ctrl_sc(isp_dev, ctrl->p_new.p_u8);
+               break;
++      case V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING:
++      case V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING:
++              ret = isp_set_ctrl_outss(isp_dev, ctrl->p_new.p_u8);
++              break;
+       default:
+               ret = -EINVAL;
+               break;
+@@ -1028,6 +1050,30 @@ struct v4l2_ctrl_config isp_ctrl[] = {
+               .dims[0]        = sizeof(struct jh7110_isp_sc_setting),
+               .flags          = 0,
+       },
++      [16] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "OUTSS Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_outss_setting),
++              .flags          = 0,
++      },
++      [17] = {
++              .ops            = &isp_ctrl_ops,
++              .type           = V4L2_CTRL_TYPE_U8,
++              .def            = 0,
++              .min            = 0x00,
++              .max            = 0xff,
++              .step           = 1,
++              .name           = "OUTSS Setting",
++              .id             = V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING,
++              .dims[0]        = sizeof(struct jh7110_isp_outss_setting),
++              .flags          = 0,
++      },
+ };
+ static int isp_init_controls(struct stf_isp_dev *isp_dev)
+--- a/include/uapi/linux/jh7110-isp.h
++++ b/include/uapi/linux/jh7110-isp.h
+@@ -49,6 +49,10 @@
+                               (V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
+ #define V4L2_CID_USER_JH7110_ISP_STAT_SETTING \
+                               (V4L2_CID_USER_JH7110_ISP_BASE + 0x0010)
++#define V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0011)
++#define V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING \
++                              (V4L2_CID_USER_JH7110_ISP_BASE + 0x0012)
+ struct jh7110_isp_wb_gain {
+       __u16 gain_r;
+@@ -325,4 +329,23 @@ struct jh7110_isp_sc_setting {
+       struct jh7110_isp_sc_awb_config awb_config;
+ };
++struct jh7110_isp_outss_setting {
++      __u8 which;
++      __u16 stride;   // Output Image Stride Register, 8-byte(64bit) granularity.
++      __u8 hsm;               // horizontal scale mode
++      __u32 hsf;              // horizontal scale factor (time 4096)
++      __u8 vsm;               // vertical scale mode
++      __u32 vsf;              // vertical scale factor (time 4096)
++};
++
++struct jh7110_isp_sc_buffer {
++      __u32 y_histogram[64];
++      __u32 reserv0[33];
++      __u32 bright_sc[4096];
++      __u32 reserv1[96];
++      __u32 ae_hist_y[128];
++      __u32 reserv2[511];
++      __u16 flag;
++};
++
+ #endif
diff --git a/target/linux/starfive/patches-6.6/0097-media-starfive-Update-ISP-initialzation.patch b/target/linux/starfive/patches-6.6/0097-media-starfive-Update-ISP-initialzation.patch
new file mode 100644 (file)
index 0000000..b1d857e
--- /dev/null
@@ -0,0 +1,226 @@
+From 883745d4728e524babfe7d04cbb8a925c22aa6b5 Mon Sep 17 00:00:00 2001
+From: Changhuang Liang <changhuang.liang@starfivetech.com>
+Date: Mon, 13 Nov 2023 14:46:50 +0800
+Subject: [PATCH 097/116] media: starfive: Update ISP initialzation
+
+ISP compatible stf_isp_ctrl and libcamera.
+
+Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
+---
+ .../platform/starfive/v4l2_driver/stf_isp.c   | 55 ++++++++-----------
+ 1 file changed, 24 insertions(+), 31 deletions(-)
+
+--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
++++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+@@ -402,7 +402,7 @@ static int isp_set_ctrl_cfa(struct stf_i
+       reg_write(ispbase, reg_addr, ((u32)(cfg->cross_cov & 0x3) << 4) | (cfg->hv_width & 0xf));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -419,7 +419,7 @@ static int isp_set_ctrl_ctc(struct stf_i
+       reg_value |= ((u32)(cfg->max_gt & 0x3ff) << 16) | (cfg->min_gt & 0x3ff);
+       reg_write(ispbase, reg_addr, reg_value);
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -434,7 +434,7 @@ static int isp_set_ctrl_dbc(struct stf_i
+       reg_write(ispbase, reg_addr, ((u32)(cfg->bad_gt & 0x3ff) << 16) | (cfg->bad_xt & 0x3ff));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -459,7 +459,7 @@ static int isp_set_ctrl_dnyuv(struct stf
+       reg_write(ispbase, reg_addr + 36, CREATE_REG_VALUE(u16, &cfg->uv_curve[4], 2, 0x3ff, 16));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -472,7 +472,7 @@ static int isp_set_ctrl_gmargb(struct st
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+       s32 i;
+-      
++
+       for(i = 0; i < 15; i++, curve++, reg_addr += 4)
+               reg_write(ispbase, reg_addr, ((u32)curve->sg_val << 16) | (curve->g_val & 0x3ff));
+@@ -490,7 +490,7 @@ static int isp_set_ctrl_lccf(struct stf_
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+       s32 i;
+-      
++
+       reg_write(ispbase, reg_addr, ((u32)(setting->circle.center_y & 0x7fff) << 16) | (setting->circle.center_x & 0x7fff));
+       reg_write(ispbase, reg_addr + 8, setting->circle.radius & 0xf);
+       reg_addr += 0x90;
+@@ -498,7 +498,7 @@ static int isp_set_ctrl_lccf(struct stf_
+               reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x1fff, 16));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -511,7 +511,7 @@ static int isp_set_ctrl_obc(struct stf_i
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+       s32 i;
+-      
++
+       reg_write(ispbase, reg_addr, ((u32)(setting->win_size.height & 0xf) << 4) | (setting->win_size.width & 0xf));
+       reg_addr += 0x2ac;      //0x2e0
+@@ -519,7 +519,7 @@ static int isp_set_ctrl_obc(struct stf_i
+               reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 4, 0xff, 8));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -535,7 +535,7 @@ static int isp_set_ctrl_oecf(struct stf_
+       void __iomem *ispbase = vin->isp_base;
+       u32 x, y, slope;
+       s32 i;
+-      
++
+       for(i = 0; i < 32; i++, reg_x_addr += 4, reg_y_addr += 4, reg_s_addr += 4) {
+               x = pts->x & 0x3ff;
+               y = pts->y & 0x3ff;
+@@ -565,12 +565,12 @@ static int isp_set_ctrl_r2y(struct stf_i
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+       s32 i;
+-      
++
+       for(i = 0; i < 9; i++, reg_addr += 4)
+               reg_write(ispbase, reg_addr, (u32)(params[i] & 0x1ff));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -584,7 +584,7 @@ static int isp_set_ctrl_sat(struct stf_i
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+       s32 i;
+-      
++
+       for(i = 0; i < 3; i++, reg_addr += 4, params += 2)
+               reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0xfff, 16));
+       reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, &setting->hue_info.cos, 2, 0x3ff, 16));
+@@ -597,7 +597,7 @@ static int isp_set_ctrl_sat(struct stf_i
+               reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x3fff, 16));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -609,7 +609,7 @@ static int isp_set_ctrl_shrp(struct stf_
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+       s32 i;
+-      
++
+       for(i = 0; i < 4; i++, reg_addr += 4)
+               reg_write(ispbase, reg_addr, ((u32)(setting->strength.diff[i] & 0x3ff) << 16) | ((u32)(setting->weight.weight[i] & 0xf) << 8));
+       FILL_ISP_REGS(u8, ispbase, reg_addr, (u8 *)(&setting->weight.weight[4]), 15 - 4, 0xf, 8);
+@@ -622,7 +622,7 @@ static int isp_set_ctrl_shrp(struct stf_
+       reg_write(ispbase, reg_addr, ((u32)(setting->pdirf & 0xf) << 28) | ((u32)(setting->ndirf & 0xf) << 24) | (setting->weight.recip_wei_sum & 0x3fffff));
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -633,11 +633,11 @@ static int isp_set_ctrl_ycrv(struct stf_
+       u32 reg_addr = reg_info->cfg_reg;
+       struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+       void __iomem *ispbase = vin->isp_base;
+-      
++
+       FILL_ISP_REGS(u16, ispbase, reg_addr, (u16 *)(setting->curve.y), 64, 0x3ff, 0);
+       reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+-      
++
+       return 0;
+ }
+@@ -661,11 +661,11 @@ static int isp_set_ctrl_sc(struct stf_is
+       reg_write(ispbase, 0xb8, ((u32)(setting->crop_config.v_start) << 16) | setting->crop_config.h_start);
+       // SC config1
+-      reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) | 
++      reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) |
+               ((u32)(setting->crop_config.sw_height) << 8) | setting->crop_config.sw_width);
+       // SC decimation config
+-      reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) | 
++      reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) |
+               ((u32)(setting->crop_config.hkeep) << 8) | setting->crop_config.hperiod);
+       // SC AWB pixel sum config
+@@ -676,7 +676,7 @@ static int isp_set_ctrl_sc(struct stf_is
+       reg_write(ispbase, 0xd4, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grbl, 2, 0xffff, 16));
+       // AF register
+-      reg_write(ispbase, 0xc0, 
++      reg_write(ispbase, 0xc0,
+               ((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
+               ((u32)(setting->af_config.es_ver_thr & 0xff) << 8) |
+               ((setting->af_config.ver_en & 0x1) << 3) |
+@@ -1217,7 +1217,7 @@ static int isp_get_interface_type(struct
+ static int isp_set_stream(struct v4l2_subdev *sd, int enable)
+ {
+       struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
+-      int ret = 0, interface_type;
++      int interface_type;
+       struct v4l2_mbus_framefmt *fmt;
+       struct v4l2_event src_ch = { 0 };
+@@ -1225,6 +1225,7 @@ static int isp_set_stream(struct v4l2_su
+       mutex_lock(&isp_dev->stream_lock);
+       if (enable) {
+               if (isp_dev->stream_count == 0) {
++                      v4l2_ctrl_handler_setup(&isp_dev->ctrls.handler);
+                       isp_dev->hw_ops->isp_clk_enable(isp_dev);
+                       if (!user_config_isp)
+                               isp_dev->hw_ops->isp_config_set(isp_dev);
+@@ -1256,15 +1257,7 @@ static int isp_set_stream(struct v4l2_su
+ exit:
+       mutex_unlock(&isp_dev->stream_lock);
+-      mutex_lock(&isp_dev->power_lock);
+-      /* restore controls */
+-      if (enable && isp_dev->power_count == 1) {
+-              mutex_unlock(&isp_dev->power_lock);
+-              ret = v4l2_ctrl_handler_setup(&isp_dev->ctrls.handler);
+-      } else
+-              mutex_unlock(&isp_dev->power_lock);
+-
+-      return ret;
++      return 0;
+ }
+ /*Try to match sensor format with sink, and then get the index as default.*/
diff --git a/target/linux/starfive/patches-6.6/0098-crypto-jh7110-Comment-RSA-algo-register.patch b/target/linux/starfive/patches-6.6/0098-crypto-jh7110-Comment-RSA-algo-register.patch
new file mode 100644 (file)
index 0000000..ca36e5b
--- /dev/null
@@ -0,0 +1,36 @@
+From 02f84ff43453b0f8dc2a1e4885e7003929349ee6 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Tue, 5 Mar 2024 11:00:21 +0800
+Subject: [PATCH 098/116] crypto: jh7110: Comment RSA algo register
+
+There are some issues in RSA algo, which will cause kernel crash.
+So comment RSA algo register temporarily.
+This commit should be reverted after the RSA issues are fixed.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/crypto/starfive/jh7110-cryp.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/crypto/starfive/jh7110-cryp.c
++++ b/drivers/crypto/starfive/jh7110-cryp.c
+@@ -194,14 +194,14 @@ static int starfive_cryp_probe(struct pl
+       if (ret)
+               goto err_algs_hash;
+-      ret = starfive_rsa_register_algs();
+-      if (ret)
+-              goto err_algs_rsa;
++//    ret = starfive_rsa_register_algs();
++//    if (ret)
++//            goto err_algs_rsa;
+       return 0;
+-err_algs_rsa:
+-      starfive_hash_unregister_algs();
++// err_algs_rsa:
++//    starfive_hash_unregister_algs();
+ err_algs_hash:
+       starfive_aes_unregister_algs();
+ err_algs_aes:
diff --git a/target/linux/starfive/patches-6.6/0099-riscv-dts-starfive-jh7110-evb-Add-qspi-norflash-part.patch b/target/linux/starfive/patches-6.6/0099-riscv-dts-starfive-jh7110-evb-Add-qspi-norflash-part.patch
new file mode 100644 (file)
index 0000000..d04b082
--- /dev/null
@@ -0,0 +1,26 @@
+From a7ff2b51e2941526d6924dcf8f1760187d7e5d03 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 11 Mar 2024 11:10:45 +0800
+Subject: [PATCH 099/116] riscv: dts: starfive: jh7110-evb: Add qspi norflash
+ partition for uboot-env
+
+Add qspi norflash partition "uboot-env@f0000",
+for synchronizing with other branches.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-evb.dtsi | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi
+@@ -568,6 +568,9 @@
+                       spl@0 {
+                               reg = <0x0 0x40000>;
+                       };
++                      uboot-env@f0000 {
++                              reg = <0xf0000 0x10000>;
++                      };
+                       uboot@100000 {
+                               reg = <0x100000 0x300000>;
+                       };
diff --git a/target/linux/starfive/patches-6.6/0100-driver-regulator-pmic-driver-support-kernel-6.6.patch b/target/linux/starfive/patches-6.6/0100-driver-regulator-pmic-driver-support-kernel-6.6.patch
new file mode 100644 (file)
index 0000000..ad8b98d
--- /dev/null
@@ -0,0 +1,22 @@
+From f96ba2eb39ba950f67edf43e0c5c88ac660bc2a0 Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Wed, 13 Mar 2024 18:43:27 +0800
+Subject: [PATCH 100/116] driver: regulator: pmic driver support kernel 6.6
+
+pmic driver support kernel 6.6
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ drivers/regulator/axp20x-regulator.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/regulator/axp20x-regulator.c
++++ b/drivers/regulator/axp20x-regulator.c
+@@ -20,6 +20,7 @@
+ #include <linux/mfd/axp20x.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
++#include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/driver.h>
diff --git a/target/linux/starfive/patches-6.6/0101-spi-pl022-starfive-Add-platform-bus-register-to-adap.patch b/target/linux/starfive/patches-6.6/0101-spi-pl022-starfive-Add-platform-bus-register-to-adap.patch
new file mode 100644 (file)
index 0000000..8112243
--- /dev/null
@@ -0,0 +1,232 @@
+From c609818850807a1ae5fa17e165f2b66b914188b4 Mon Sep 17 00:00:00 2001
+From: "xingyu.wu" <xingyu.wu@starfivetech.com>
+Date: Tue, 28 Jun 2022 22:48:15 +0800
+Subject: [PATCH 101/116] spi-pl022:starfive:Add platform bus register to adapt
+ overlay
+
+Add platform bus register to adapt dtbo overlay.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 137 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 131 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -34,6 +34,7 @@
+ #include <linux/of.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/reset.h>
++#include <linux/platform_device.h>
+ /*
+  * This macro is used to define some register default values.
+@@ -2088,7 +2089,10 @@ pl022_platform_data_dt_get(struct device
+               return NULL;
+       }
+-      pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
++      if (strncmp(dev->bus->name, "platform", strlen("platform")))
++              pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
++      else
++              pd = kzalloc(sizeof(struct pl022_ssp_controller), GFP_KERNEL);
+       if (!pd)
+               return NULL;
+@@ -2108,6 +2112,14 @@ static int pl022_probe(struct amba_devic
+       struct spi_controller *host;
+       struct pl022 *pl022 = NULL;     /*Data for this driver */
+       int status = 0;
++      int platform_flag = 0;
++
++      if (strncmp(dev->bus->name, "platform", strlen("platform")))
++              platform_flag = 0;
++      else
++              platform_flag = 1;
++      dev_dbg(&adev->dev, "bus name:%s platform flag:%d",
++                      dev->bus->name, platform_flag);
+       dev_info(&adev->dev,
+                "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
+@@ -2161,7 +2173,11 @@ static int pl022_probe(struct amba_devic
+               goto err_no_ioregion;
+       pl022->phybase = adev->res.start;
+-      pl022->virtbase = devm_ioremap(dev, adev->res.start,
++      if (platform_flag)
++              pl022->virtbase = ioremap(adev->res.start,
++                                     resource_size(&adev->res));
++      else
++              pl022->virtbase = devm_ioremap(dev, adev->res.start,
+                                      resource_size(&adev->res));
+       if (pl022->virtbase == NULL) {
+               status = -ENOMEM;
+@@ -2170,7 +2186,10 @@ static int pl022_probe(struct amba_devic
+       dev_info(&adev->dev, "mapped registers from %pa to %p\n",
+               &adev->res.start, pl022->virtbase);
+-      pl022->clk = devm_clk_get(&adev->dev, NULL);
++      if (platform_flag)
++              pl022->clk = clk_get(&adev->dev, NULL);
++      else
++              pl022->clk = devm_clk_get(&adev->dev, NULL);
+       if (IS_ERR(pl022->clk)) {
+               status = PTR_ERR(pl022->clk);
+               dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
+@@ -2183,7 +2202,10 @@ static int pl022_probe(struct amba_devic
+               goto err_no_clk_en;
+       }
+-      pl022->rst = devm_reset_control_get(&adev->dev, NULL);
++      if (platform_flag)
++              pl022->rst = reset_control_get_exclusive(&adev->dev, NULL);
++      else
++              pl022->rst = devm_reset_control_get(&adev->dev, NULL);
+       if (IS_ERR(pl022->rst)) {
+               status = PTR_ERR(pl022->rst);
+               dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n");
+@@ -2205,7 +2227,11 @@ static int pl022_probe(struct amba_devic
+              SSP_CR1(pl022->virtbase));
+       load_ssp_default_config(pl022);
+-      status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
++      if (platform_flag)
++              status = request_irq(adev->irq[0], pl022_interrupt_handler,
++                                0, "pl022", pl022);
++      else
++              status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
+                                 0, "pl022", pl022);
+       if (status < 0) {
+               dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
+@@ -2230,7 +2256,10 @@ static int pl022_probe(struct amba_devic
+       /* Register with the SPI framework */
+       amba_set_drvdata(adev, pl022);
+-      status = devm_spi_register_controller(&adev->dev, host);
++      if (platform_flag)
++              status = spi_register_controller(host);
++      else
++              status = devm_spi_register_controller(&adev->dev, host);
+       if (status != 0) {
+               dev_err_probe(&adev->dev, status,
+                             "problem registering spi host\n");
+@@ -2255,15 +2284,26 @@ static int pl022_probe(struct amba_devic
+       if (platform_info->enable_dma)
+               pl022_dma_remove(pl022);
+  err_no_irq:
++      if (platform_flag)
++              free_irq(adev->irq[0], pl022);
++      reset_control_assert(pl022->rst);
+  err_no_rst_de:
++      if (platform_flag)
++              reset_control_put(pl022->rst);
+  err_no_rst:
+       clk_disable_unprepare(pl022->clk);
+  err_no_clk_en:
++      if (platform_flag)
++              clk_put(pl022->clk);
+  err_no_clk:
++      if (platform_flag)
++              iounmap(pl022->virtbase);
+  err_no_ioremap:
+       amba_release_regions(adev);
+  err_no_ioregion:
+       spi_controller_put(host);
++      if (platform_flag)
++              kfree(platform_info);
+       return status;
+ }
+@@ -2464,6 +2504,91 @@ static void __exit pl022_exit(void)
+ }
+ module_exit(pl022_exit);
++/*
++ * Register PL022 in platform bus to accommodate overlay use.
++ * Because overlay only trigger response from the platform bus
++ * not amba bus.
++ */
++static int starfive_of_pl022_probe(struct platform_device *pdev)
++{
++      int ret;
++      const struct amba_id id = {
++              .id = 0x00041022,
++              .mask = 0x000fffff,
++              .data = &vendor_arm
++      };
++      struct amba_device *pcdev;
++      struct device *dev = &pdev->dev;
++
++      pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
++      if (!pcdev)
++              return -ENOMEM;
++
++      pcdev->dev = pdev->dev;
++      pcdev->periphid = id.id;
++      pcdev->res = *(pdev->resource);
++
++      pcdev->irq[0] = platform_get_irq(pdev, 0);
++      if (pcdev->irq[0] < 0) {
++              dev_err(dev, "failed to get irq\n");
++              ret = -EINVAL;
++      }
++
++      ret = pl022_probe(pcdev, &id);
++
++      return ret;
++}
++
++static int starfive_of_pl022_remove(struct platform_device *pdev)
++{
++      u32 size;
++      int irq;
++      struct pl022 *pl022 = dev_get_drvdata(&pdev->dev);
++
++      if (!pl022)
++              return 0;
++
++      pm_runtime_get_noresume(&pdev->dev);
++
++      load_ssp_default_config(pl022);
++      if (pl022->host_info->enable_dma)
++              pl022_dma_remove(pl022);
++
++      irq = platform_get_irq(pdev, 0);
++      free_irq(irq, pl022);
++      reset_control_assert(pl022->rst);
++      reset_control_put(pl022->rst);
++      clk_disable_unprepare(pl022->clk);
++      clk_put(pl022->clk);
++      iounmap(pl022->virtbase);
++      kfree(pl022->host_info);
++
++      size = resource_size(pdev->resource);
++      release_mem_region(pdev->resource->start, size);
++      tasklet_disable(&pl022->pump_transfers);
++      return 0;
++}
++
++static const struct of_device_id starfive_of_pl022_match[] = {
++      { .compatible = "starfive,jh7110-spi-pl022" },
++      { },
++};
++MODULE_DEVICE_TABLE(of, starfive_of_pl022_match);
++
++static struct platform_driver starfive_of_pl022_driver = {
++      .driver = {
++              .name = "starfive-spi-pl022",
++              .of_match_table = starfive_of_pl022_match,
++              .pm     = &pl022_dev_pm_ops,
++      },
++      .probe = starfive_of_pl022_probe,
++      .remove = starfive_of_pl022_remove,
++};
++
++module_platform_driver(starfive_of_pl022_driver);
++/* platform register end */
++
++MODULE_AUTHOR("xingyu.wu <xingyu.wu@starfivetech.com>");
+ MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+ MODULE_DESCRIPTION("PL022 SSP Controller Driver");
+ MODULE_LICENSE("GPL");
diff --git a/target/linux/starfive/patches-6.6/0102-spi-pl022-starfive-Avoid-power-device-error-when-CON.patch b/target/linux/starfive/patches-6.6/0102-spi-pl022-starfive-Avoid-power-device-error-when-CON.patch
new file mode 100644 (file)
index 0000000..bd4709f
--- /dev/null
@@ -0,0 +1,92 @@
+From f94a07310f18720faa0cc773a1aec5a7b1bf3928 Mon Sep 17 00:00:00 2001
+From: "xingyu.wu" <xingyu.wu@starfivetech.com>
+Date: Tue, 19 Jul 2022 14:49:20 +0800
+Subject: [PATCH 102/116] spi:pl022-starfive:Avoid power device error when
+ CONFIG_PM enable
+
+It would be error when CONFIG_PM enable and use overlay by of-platform to register.
+
+Add some power manager operation in platform probe function.
+
+Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 36 ++++++++++++++++++++++++++++++++++--
+ 1 file changed, 34 insertions(+), 2 deletions(-)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -35,6 +35,8 @@
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/reset.h>
+ #include <linux/platform_device.h>
++#include <linux/clk/clk-conf.h>
++#include <linux/pm_domain.h>
+ /*
+  * This macro is used to define some register default values.
+@@ -2266,7 +2268,8 @@ static int pl022_probe(struct amba_devic
+               goto err_spi_register;
+       }
+       dev_dbg(dev, "probe succeeded\n");
+-
++      if (!platform_flag)
++              platform_info->autosuspend_delay = 100;
+       /* let runtime pm put suspend */
+       if (platform_info->autosuspend_delay > 0) {
+               dev_info(&adev->dev,
+@@ -2276,7 +2279,10 @@ static int pl022_probe(struct amba_devic
+                       platform_info->autosuspend_delay);
+               pm_runtime_use_autosuspend(dev);
+       }
+-      pm_runtime_put(dev);
++      if (platform_flag)
++              clk_disable_unprepare(pl022->clk);
++      else
++              pm_runtime_put(dev);
+       return 0;
+@@ -2534,8 +2540,33 @@ static int starfive_of_pl022_probe(struc
+               ret = -EINVAL;
+       }
++      ret = of_clk_set_defaults(dev->of_node, false);
++      if (ret < 0)
++              goto err_probe;
++
++      ret = dev_pm_domain_attach(dev, true);
++      if (ret)
++              goto err_probe;
++
+       ret = pl022_probe(pcdev, &id);
++      struct pl022 *pl022 = amba_get_drvdata(pcdev);
++
++      pl022->host->dev.parent = &pdev->dev;
++      platform_set_drvdata(pdev, pl022);
++
++      pm_runtime_enable(&pdev->dev);
++      pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
++      pm_runtime_use_autosuspend(&pdev->dev);
++
++      if (ret) {
++              pm_runtime_disable(dev);
++              pm_runtime_set_suspended(dev);
++              pm_runtime_put_noidle(dev);
++              dev_pm_domain_detach(dev, true);
++      }
++
++err_probe:
+       return ret;
+ }
+@@ -2566,6 +2597,7 @@ static int starfive_of_pl022_remove(stru
+       size = resource_size(pdev->resource);
+       release_mem_region(pdev->resource->start, size);
+       tasklet_disable(&pl022->pump_transfers);
++      pm_runtime_disable(&pdev->dev);
+       return 0;
+ }
diff --git a/target/linux/starfive/patches-6.6/0103-spi-pl022-starfive-fix-the-problem-of-spi-overlay-re.patch b/target/linux/starfive/patches-6.6/0103-spi-pl022-starfive-fix-the-problem-of-spi-overlay-re.patch
new file mode 100644 (file)
index 0000000..3e36e84
--- /dev/null
@@ -0,0 +1,403 @@
+From 733e7bd23a1efad15a724fbdbce8d9f06aa6813a Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfive.com>
+Date: Wed, 23 Nov 2022 14:53:58 +0800
+Subject: [PATCH 103/116] spi-pl022-starfive:fix the problem of spi overlay
+ reload
+
+fix the problem of spi overlay reload
+
+Signed-off-by: ziv.xu <ziv.xu@starfive.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 270 ++++++++++++++++++++++++++++------------
+ 1 file changed, 188 insertions(+), 82 deletions(-)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -2106,6 +2106,172 @@ pl022_platform_data_dt_get(struct device
+       return pd;
+ }
++static int pl022_platform_probe(struct platform_device *pdev, const struct amba_id *id)
++{
++      struct device *dev = &pdev->dev;
++      struct spi_controller *host;
++      struct pl022_ssp_controller *platform_info;
++      struct amba_device *adev;
++      struct pl022 *pl022 = NULL;
++      struct resource *res;
++      int status = 0;
++      int irq;
++
++      dev_info(dev,
++              "ARM PL022 driver for StarFive SoC platform, device ID: 0x%08x\n",
++              id->id);
++
++      adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
++      adev->dev = pdev->dev;
++      platform_info = pl022_platform_data_dt_get(dev);
++      if (!platform_info) {
++              dev_err(dev, "probe: no platform data defined\n");
++              return -ENODEV;
++      }
++      /* Allocate host with space for data */
++      host = spi_alloc_host(dev, sizeof(struct pl022));
++      if (host == NULL) {
++              dev_err(dev, "probe - cannot alloc SPI host\n");
++              return -ENOMEM;
++      }
++
++      pl022 = spi_controller_get_devdata(host);
++      pl022->host = host;
++      pl022->host_info = platform_info;
++      pl022->adev = adev;
++      pl022->vendor = id->data;
++      pl022->host->dev.parent = &pdev->dev;
++      /*
++       * Bus Number Which has been Assigned to this SSP controller
++       * on this board
++       */
++      host->bus_num = platform_info->bus_id;
++      host->cleanup = pl022_cleanup;
++      host->setup = pl022_setup;
++      /* If open CONFIG_PM, auto_runtime_pm should be false when of-platform.*/
++      host->auto_runtime_pm = true;
++      host->transfer_one_message = pl022_transfer_one_message;
++      host->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
++      host->rt = platform_info->rt;
++      host->dev.of_node = dev->of_node;
++      host->use_gpio_descriptors = true;
++
++      /*
++       * Supports mode 0-3, loopback, and active low CS. Transfers are
++       * always MS bit first on the original pl022.
++       */
++      host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
++      if (pl022->vendor->extended_cr)
++              host->mode_bits |= SPI_LSB_FIRST;
++
++      dev_dbg(dev, "BUSNO: %d\n", host->bus_num);
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      pl022->phybase = res->start;
++      pl022->virtbase = devm_ioremap_resource(dev, res);
++      if (pl022->virtbase == NULL) {
++              status = -ENOMEM;
++              goto err_no_ioremap;
++      }
++      dev_info(dev, "mapped registers from %llx to %llx\n",
++               pdev->resource->start, pdev->resource->end);
++
++      pl022->clk = devm_clk_get(dev, NULL);
++      if (IS_ERR(pl022->clk)) {
++              status = PTR_ERR(pl022->clk);
++              dev_err(dev, "could not retrieve SSP/SPI bus clock\n");
++              goto err_no_clk;
++      }
++      status = clk_prepare_enable(pl022->clk);
++      if (status) {
++              dev_err(dev, "could not enable SSP/SPI bus clock\n");
++              goto err_no_clk_en;
++      }
++
++      pl022->rst = devm_reset_control_get_exclusive(dev, NULL);
++      if (!IS_ERR(pl022->rst)) {
++              status = reset_control_deassert(pl022->rst);
++              if (status) {
++                      dev_err(dev, "could not deassert SSP/SPI bus reset\n");
++                      goto err_no_rst_clr;
++              }
++      } else {
++              status = PTR_ERR(pl022->rst);
++              dev_err(dev, "could not retrieve SSP/SPI bus reset\n");
++              goto err_no_rst;
++      }
++
++      /* Initialize transfer pump */
++      tasklet_init(&pl022->pump_transfers, pump_transfers,
++                   (unsigned long)pl022);
++
++      /* Disable SSP */
++      writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
++             SSP_CR1(pl022->virtbase));
++      load_ssp_default_config(pl022);
++
++      /* Obtain IRQ line. */
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0) {
++              status = -ENXIO;
++              goto err_no_irq;
++      }
++      status = devm_request_irq(dev, irq, pl022_interrupt_handler,
++                                0, "pl022", pl022);
++      if (status < 0) {
++              dev_err(dev, "probe - cannot get IRQ (%d)\n", status);
++              goto err_no_irq;
++      }
++
++      /* Get DMA channels, try autoconfiguration first */
++      status = pl022_dma_autoprobe(pl022);
++      if (status == -EPROBE_DEFER) {
++              dev_dbg(dev, "deferring probe to get DMA channel\n");
++              goto err_no_irq;
++      }
++
++      /* dma is not used unless configured in the device tree */
++      platform_info->enable_dma = 0;
++
++      /* If that failed, use channels from platform_info */
++      if (status == 0)
++              platform_info->enable_dma = 1;
++      else if (platform_info->enable_dma) {
++              status = pl022_dma_probe(pl022);
++              if (status != 0)
++                      platform_info->enable_dma = 0;
++      }
++
++      /* Register with the SPI framework */
++      dev_set_drvdata(dev, pl022);
++
++      status = devm_spi_register_controller(dev, host);
++      if (status != 0) {
++              dev_err(dev,
++                      "probe - problem registering spi host\n");
++              goto err_spi_register;
++      }
++      dev_dbg(dev, "probe succeeded\n");
++
++      clk_disable_unprepare(pl022->clk);
++
++      return 0;
++ err_spi_register:
++      if (platform_info->enable_dma)
++              pl022_dma_remove(pl022);
++ err_no_irq:
++      reset_control_assert(pl022->rst);
++ err_no_rst_clr:
++ err_no_rst:
++      clk_disable_unprepare(pl022->clk);
++ err_no_clk_en:
++ err_no_clk:
++ err_no_ioremap:
++      release_mem_region(pdev->resource->start, resource_size(pdev->resource));
++      spi_controller_put(host);
++      return status;
++}
++
+ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
+ {
+       struct device *dev = &adev->dev;
+@@ -2114,14 +2280,6 @@ static int pl022_probe(struct amba_devic
+       struct spi_controller *host;
+       struct pl022 *pl022 = NULL;     /*Data for this driver */
+       int status = 0;
+-      int platform_flag = 0;
+-
+-      if (strncmp(dev->bus->name, "platform", strlen("platform")))
+-              platform_flag = 0;
+-      else
+-              platform_flag = 1;
+-      dev_dbg(&adev->dev, "bus name:%s platform flag:%d",
+-                      dev->bus->name, platform_flag);
+       dev_info(&adev->dev,
+                "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
+@@ -2175,11 +2333,7 @@ static int pl022_probe(struct amba_devic
+               goto err_no_ioregion;
+       pl022->phybase = adev->res.start;
+-      if (platform_flag)
+-              pl022->virtbase = ioremap(adev->res.start,
+-                                     resource_size(&adev->res));
+-      else
+-              pl022->virtbase = devm_ioremap(dev, adev->res.start,
++      pl022->virtbase = devm_ioremap(dev, adev->res.start,
+                                      resource_size(&adev->res));
+       if (pl022->virtbase == NULL) {
+               status = -ENOMEM;
+@@ -2188,10 +2342,7 @@ static int pl022_probe(struct amba_devic
+       dev_info(&adev->dev, "mapped registers from %pa to %p\n",
+               &adev->res.start, pl022->virtbase);
+-      if (platform_flag)
+-              pl022->clk = clk_get(&adev->dev, NULL);
+-      else
+-              pl022->clk = devm_clk_get(&adev->dev, NULL);
++      pl022->clk = devm_clk_get(&adev->dev, NULL);
+       if (IS_ERR(pl022->clk)) {
+               status = PTR_ERR(pl022->clk);
+               dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
+@@ -2204,10 +2355,7 @@ static int pl022_probe(struct amba_devic
+               goto err_no_clk_en;
+       }
+-      if (platform_flag)
+-              pl022->rst = reset_control_get_exclusive(&adev->dev, NULL);
+-      else
+-              pl022->rst = devm_reset_control_get(&adev->dev, NULL);
++      pl022->rst = devm_reset_control_get(&adev->dev, NULL);
+       if (IS_ERR(pl022->rst)) {
+               status = PTR_ERR(pl022->rst);
+               dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n");
+@@ -2229,11 +2377,7 @@ static int pl022_probe(struct amba_devic
+              SSP_CR1(pl022->virtbase));
+       load_ssp_default_config(pl022);
+-      if (platform_flag)
+-              status = request_irq(adev->irq[0], pl022_interrupt_handler,
+-                                0, "pl022", pl022);
+-      else
+-              status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
++      status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
+                                 0, "pl022", pl022);
+       if (status < 0) {
+               dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
+@@ -2258,18 +2402,16 @@ static int pl022_probe(struct amba_devic
+       /* Register with the SPI framework */
+       amba_set_drvdata(adev, pl022);
+-      if (platform_flag)
+-              status = spi_register_controller(host);
+-      else
+-              status = devm_spi_register_controller(&adev->dev, host);
++
++      status = devm_spi_register_controller(&adev->dev, host);
+       if (status != 0) {
+               dev_err_probe(&adev->dev, status,
+                             "problem registering spi host\n");
+               goto err_spi_register;
+       }
+       dev_dbg(dev, "probe succeeded\n");
+-      if (!platform_flag)
+-              platform_info->autosuspend_delay = 100;
++
++      platform_info->autosuspend_delay = 100;
+       /* let runtime pm put suspend */
+       if (platform_info->autosuspend_delay > 0) {
+               dev_info(&adev->dev,
+@@ -2279,10 +2421,8 @@ static int pl022_probe(struct amba_devic
+                       platform_info->autosuspend_delay);
+               pm_runtime_use_autosuspend(dev);
+       }
+-      if (platform_flag)
+-              clk_disable_unprepare(pl022->clk);
+-      else
+-              pm_runtime_put(dev);
++
++      pm_runtime_put(dev);
+       return 0;
+@@ -2290,26 +2430,17 @@ static int pl022_probe(struct amba_devic
+       if (platform_info->enable_dma)
+               pl022_dma_remove(pl022);
+  err_no_irq:
+-      if (platform_flag)
+-              free_irq(adev->irq[0], pl022);
+       reset_control_assert(pl022->rst);
+  err_no_rst_de:
+-      if (platform_flag)
+-              reset_control_put(pl022->rst);
+  err_no_rst:
+       clk_disable_unprepare(pl022->clk);
+  err_no_clk_en:
+-      if (platform_flag)
+-              clk_put(pl022->clk);
+  err_no_clk:
+-      if (platform_flag)
+-              iounmap(pl022->virtbase);
+  err_no_ioremap:
+       amba_release_regions(adev);
+  err_no_ioregion:
+       spi_controller_put(host);
+-      if (platform_flag)
+-              kfree(platform_info);
++
+       return status;
+ }
+@@ -2523,23 +2654,8 @@ static int starfive_of_pl022_probe(struc
+               .mask = 0x000fffff,
+               .data = &vendor_arm
+       };
+-      struct amba_device *pcdev;
+       struct device *dev = &pdev->dev;
+-      pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+-      if (!pcdev)
+-              return -ENOMEM;
+-
+-      pcdev->dev = pdev->dev;
+-      pcdev->periphid = id.id;
+-      pcdev->res = *(pdev->resource);
+-
+-      pcdev->irq[0] = platform_get_irq(pdev, 0);
+-      if (pcdev->irq[0] < 0) {
+-              dev_err(dev, "failed to get irq\n");
+-              ret = -EINVAL;
+-      }
+-
+       ret = of_clk_set_defaults(dev->of_node, false);
+       if (ret < 0)
+               goto err_probe;
+@@ -2548,16 +2664,11 @@ static int starfive_of_pl022_probe(struc
+       if (ret)
+               goto err_probe;
+-      ret = pl022_probe(pcdev, &id);
++      ret = pl022_platform_probe(pdev, &id);
+-      struct pl022 *pl022 = amba_get_drvdata(pcdev);
+-
+-      pl022->host->dev.parent = &pdev->dev;
+-      platform_set_drvdata(pdev, pl022);
+-
+-      pm_runtime_enable(&pdev->dev);
+-      pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+-      pm_runtime_use_autosuspend(&pdev->dev);
++      pm_runtime_enable(dev);
++      pm_runtime_set_autosuspend_delay(dev, 100);
++      pm_runtime_use_autosuspend(dev);
+       if (ret) {
+               pm_runtime_disable(dev);
+@@ -2572,32 +2683,27 @@ err_probe:
+ static int starfive_of_pl022_remove(struct platform_device *pdev)
+ {
+-      u32 size;
+-      int irq;
+       struct pl022 *pl022 = dev_get_drvdata(&pdev->dev);
+       if (!pl022)
+               return 0;
++      pm_runtime_get_sync(&pdev->dev);
+       pm_runtime_get_noresume(&pdev->dev);
+       load_ssp_default_config(pl022);
+       if (pl022->host_info->enable_dma)
+               pl022_dma_remove(pl022);
+-      irq = platform_get_irq(pdev, 0);
+-      free_irq(irq, pl022);
+-      reset_control_assert(pl022->rst);
+-      reset_control_put(pl022->rst);
+       clk_disable_unprepare(pl022->clk);
+-      clk_put(pl022->clk);
+-      iounmap(pl022->virtbase);
+-      kfree(pl022->host_info);
+-
+-      size = resource_size(pdev->resource);
+-      release_mem_region(pdev->resource->start, size);
+       tasklet_disable(&pl022->pump_transfers);
++
++      pm_runtime_put_noidle(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
++      pm_runtime_set_suspended(&pdev->dev);
++      pm_runtime_put_noidle(&pdev->dev);
++      dev_pm_domain_detach(&pdev->dev, true);
++
+       return 0;
+ }
diff --git a/target/linux/starfive/patches-6.6/0104-spi-pl022-starfive-Enable-spi-to-be-compiled-into-mo.patch b/target/linux/starfive/patches-6.6/0104-spi-pl022-starfive-Enable-spi-to-be-compiled-into-mo.patch
new file mode 100644 (file)
index 0000000..aecce73
--- /dev/null
@@ -0,0 +1,38 @@
+From 11a917bf429a5714e34584f90ab1ac376d399d8f Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfive.com>
+Date: Wed, 18 Jan 2023 15:50:47 +0800
+Subject: [PATCH 104/116] spi-pl022-starfive:Enable spi to be compiled into
+ modules
+
+Enable spi to be compiled into modules
+
+Signed-off-by: ziv.xu <ziv.xu@starfive.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/spi/spi-pl022.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/spi/spi-pl022.c
++++ b/drivers/spi/spi-pl022.c
+@@ -2633,7 +2633,11 @@ static int __init pl022_init(void)
+ {
+       return amba_driver_register(&pl022_driver);
+ }
++#if !IS_MODULE(CONFIG_SPI_PL022)
+ subsys_initcall(pl022_init);
++#else
++module_init(pl022_init);
++#endif
+ static void __exit pl022_exit(void)
+ {
+@@ -2723,7 +2727,9 @@ static struct platform_driver starfive_o
+       .remove = starfive_of_pl022_remove,
+ };
++#if !IS_MODULE(CONFIG_SPI_PL022)
+ module_platform_driver(starfive_of_pl022_driver);
++#endif
+ /* platform register end */
+ MODULE_AUTHOR("xingyu.wu <xingyu.wu@starfivetech.com>");
diff --git a/target/linux/starfive/patches-6.6/0105-riscv-configs-add-visionfive2-defconfig-to-kernel-6..patch b/target/linux/starfive/patches-6.6/0105-riscv-configs-add-visionfive2-defconfig-to-kernel-6..patch
new file mode 100644 (file)
index 0000000..bc1d836
--- /dev/null
@@ -0,0 +1,444 @@
+From e7b01c0a9ad79c1933ce85c4aeca6a037cc4f77f Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Wed, 13 Mar 2024 18:37:31 +0800
+Subject: [PATCH 105/116] riscv: configs: add visionfive2 defconfig to kernel
+ 6.6
+
+add visionfive2 defconfig to kernel 6.6
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ .../configs/starfive_visionfive2_defconfig    | 427 ++++++++++++++++++
+ 1 file changed, 427 insertions(+)
+ create mode 100644 arch/riscv/configs/starfive_visionfive2_defconfig
+
+--- /dev/null
++++ b/arch/riscv/configs/starfive_visionfive2_defconfig
+@@ -0,0 +1,427 @@
++CONFIG_COMPILE_TEST=y
++# CONFIG_WERROR is not set
++CONFIG_DEFAULT_HOSTNAME="StarFive"
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_USELIB=y
++CONFIG_NO_HZ_IDLE=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_BPF_SYSCALL=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_CGROUPS=y
++CONFIG_CGROUP_SCHED=y
++CONFIG_CFS_BANDWIDTH=y
++CONFIG_CGROUP_BPF=y
++CONFIG_NAMESPACES=y
++CONFIG_USER_NS=y
++CONFIG_CHECKPOINT_RESTORE=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_EXPERT=y
++CONFIG_PERF_EVENTS=y
++CONFIG_SOC_STARFIVE=y
++CONFIG_NONPORTABLE=y
++CONFIG_SMP=y
++CONFIG_HZ_100=y
++CONFIG_HIBERNATION=y
++CONFIG_PM_DEBUG=y
++CONFIG_PM_ADVANCED_DEBUG=y
++CONFIG_PM_TEST_SUSPEND=y
++CONFIG_CPU_IDLE=y
++CONFIG_RISCV_SBI_CPUIDLE=y
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_STAT=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
++CONFIG_CPUFREQ_DT=y
++# CONFIG_SECCOMP is not set
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++CONFIG_BINFMT_MISC=y
++CONFIG_PAGE_REPORTING=y
++CONFIG_CMA=y
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_IP_MULTICAST=y
++CONFIG_IP_ADVANCED_ROUTER=y
++CONFIG_IP_PNP=y
++CONFIG_IP_PNP_DHCP=y
++CONFIG_IP_PNP_BOOTP=y
++CONFIG_IP_PNP_RARP=y
++CONFIG_NETFILTER=y
++CONFIG_NETFILTER_NETLINK_ACCT=y
++CONFIG_NETFILTER_NETLINK_QUEUE=y
++CONFIG_NF_CONNTRACK=y
++CONFIG_NF_TABLES=y
++CONFIG_NFT_CT=y
++CONFIG_NFT_COMPAT=y
++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
++CONFIG_NETFILTER_XT_MATCH_IPCOMP=y
++CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
++CONFIG_NETFILTER_XT_MATCH_MAC=y
++CONFIG_NETFILTER_XT_MATCH_MARK=y
++CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
++CONFIG_NETFILTER_XT_MATCH_SOCKET=y
++CONFIG_NETFILTER_XT_MATCH_STATE=y
++CONFIG_NETFILTER_XT_MATCH_STRING=y
++CONFIG_NETFILTER_XT_MATCH_U32=y
++CONFIG_NF_TABLES_IPV4=y
++CONFIG_NFT_DUP_IPV4=y
++CONFIG_NFT_FIB_IPV4=y
++CONFIG_IP_NF_IPTABLES=y
++CONFIG_IP_NF_FILTER=y
++CONFIG_IP_NF_TARGET_REJECT=y
++CONFIG_IP_NF_NAT=y
++CONFIG_IP_NF_TARGET_MASQUERADE=y
++CONFIG_IP_NF_TARGET_NETMAP=y
++CONFIG_IP_NF_TARGET_REDIRECT=y
++CONFIG_NETLINK_DIAG=y
++CONFIG_CAN=y
++CONFIG_BT=y
++CONFIG_BT_RFCOMM=y
++CONFIG_BT_RFCOMM_TTY=y
++CONFIG_BT_BNEP=y
++CONFIG_BT_BNEP_MC_FILTER=y
++CONFIG_BT_BNEP_PROTO_FILTER=y
++CONFIG_BT_HCIBTUSB=m
++# CONFIG_BT_HCIBTUSB_BCM is not set
++CONFIG_CFG80211=y
++CONFIG_MAC80211=y
++CONFIG_RFKILL=y
++CONFIG_NET_9P=y
++CONFIG_NET_9P_VIRTIO=y
++CONFIG_PCI=y
++# CONFIG_PCIEASPM is not set
++CONFIG_PCIE_STARFIVE_HOST=y
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_MTD=y
++CONFIG_MTD_BLOCK=y
++CONFIG_MTD_CFI=y
++CONFIG_MTD_CFI_ADV_OPTIONS=y
++CONFIG_MTD_SPI_NOR=y
++CONFIG_OF_CONFIGFS=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_VIRTIO_BLK=y
++CONFIG_BLK_DEV_NVME=y
++CONFIG_EEPROM_AT24=y
++CONFIG_BLK_DEV_SD=y
++CONFIG_BLK_DEV_SR=y
++CONFIG_SCSI_VIRTIO=y
++CONFIG_ATA=y
++CONFIG_SATA_AHCI=y
++CONFIG_MD=y
++CONFIG_BLK_DEV_DM=m
++CONFIG_NETDEVICES=y
++CONFIG_VIRTIO_NET=y
++# CONFIG_NET_VENDOR_ALACRITECH is not set
++# CONFIG_NET_VENDOR_AMAZON is not set
++# CONFIG_NET_VENDOR_AQUANTIA is not set
++# CONFIG_NET_VENDOR_ARC is not set
++# CONFIG_NET_VENDOR_BROADCOM is not set
++# CONFIG_NET_VENDOR_CADENCE is not set
++# CONFIG_NET_VENDOR_CAVIUM is not set
++# CONFIG_NET_VENDOR_CORTINA is not set
++# CONFIG_NET_VENDOR_EZCHIP is not set
++# CONFIG_NET_VENDOR_GOOGLE is not set
++# CONFIG_NET_VENDOR_HUAWEI is not set
++# CONFIG_NET_VENDOR_INTEL is not set
++# CONFIG_NET_VENDOR_MARVELL is not set
++# CONFIG_NET_VENDOR_MELLANOX is not set
++# CONFIG_NET_VENDOR_MICREL is not set
++# CONFIG_NET_VENDOR_MICROCHIP is not set
++# CONFIG_NET_VENDOR_MICROSEMI is not set
++# CONFIG_NET_VENDOR_NI is not set
++# CONFIG_NET_VENDOR_NATSEMI is not set
++# CONFIG_NET_VENDOR_NETRONOME is not set
++# CONFIG_NET_VENDOR_PENSANDO is not set
++# CONFIG_NET_VENDOR_QUALCOMM is not set
++CONFIG_R8169=y
++# CONFIG_NET_VENDOR_RENESAS is not set
++# CONFIG_NET_VENDOR_ROCKER is not set
++# CONFIG_NET_VENDOR_SAMSUNG is not set
++# CONFIG_NET_VENDOR_SEEQ is not set
++# CONFIG_NET_VENDOR_SOLARFLARE is not set
++# CONFIG_NET_VENDOR_SOCIONEXT is not set
++CONFIG_STMMAC_ETH=y
++CONFIG_STMMAC_SELFTESTS=y
++CONFIG_DWMAC_DWC_QOS_ETH=y
++CONFIG_DWMAC_STARFIVE=y
++# CONFIG_NET_VENDOR_SYNOPSYS is not set
++# CONFIG_NET_VENDOR_VIA is not set
++# CONFIG_NET_VENDOR_WIZNET is not set
++# CONFIG_NET_VENDOR_XILINX is not set
++CONFIG_MARVELL_PHY=y
++CONFIG_MICREL_PHY=y
++CONFIG_MICROCHIP_PHY=y
++CONFIG_MOTORCOMM_PHY=y
++CONFIG_IPMS_CAN=y
++CONFIG_IWLWIFI=y
++CONFIG_IWLDVM=y
++CONFIG_IWLMVM=y
++CONFIG_HOSTAP=y
++# CONFIG_RTL_CARDS is not set
++CONFIG_USB_WIFI_ECR6600U=y
++CONFIG_AIC_WLAN_SUPPORT=y
++CONFIG_AIC8800_WLAN_SUPPORT=m
++CONFIG_AIC_LOADFW_SUPPORT=m
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_KEYBOARD is not set
++# CONFIG_INPUT_MOUSE is not set
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_TOUCHSCREEN_GOODIX=y
++CONFIG_TOUCHSCREEN_TINKER_FT5406=y
++CONFIG_SERIO_LIBPS2=y
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_SERIAL_8250_NR_UARTS=6
++CONFIG_SERIAL_8250_RUNTIME_UARTS=6
++CONFIG_SERIAL_8250_EXTENDED=y
++CONFIG_SERIAL_8250_MANY_PORTS=y
++CONFIG_SERIAL_8250_DW=y
++CONFIG_SERIAL_OF_PLATFORM=y
++CONFIG_TTY_PRINTK=y
++CONFIG_VIRTIO_CONSOLE=y
++CONFIG_HW_RANDOM=y
++CONFIG_HW_RANDOM_JH7110=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_DESIGNWARE_PLATFORM=y
++CONFIG_SPI=y
++CONFIG_SPI_CADENCE_QUADSPI=y
++CONFIG_SPI_PL022=y
++CONFIG_SPI_SIFIVE=y
++CONFIG_SPI_SPIDEV=y
++# CONFIG_PTP_1588_CLOCK is not set
++CONFIG_GPIO_SYSFS=y
++CONFIG_POWER_RESET=y
++CONFIG_POWER_RESET_GPIO_RESTART=y
++CONFIG_POWER_RESET_SYSCON=y
++CONFIG_POWER_RESET_SYSCON_POWEROFF=y
++CONFIG_SENSORS_SFCTEMP=y
++CONFIG_THERMAL=y
++CONFIG_THERMAL_WRITABLE_TRIPS=y
++CONFIG_CPU_THERMAL=y
++CONFIG_DEVFREQ_THERMAL=y
++CONFIG_THERMAL_EMULATION=y
++# CONFIG_HISI_THERMAL is not set
++CONFIG_WATCHDOG=y
++CONFIG_WATCHDOG_SYSFS=y
++CONFIG_MFD_AXP20X_I2C=y
++CONFIG_REGULATOR=y
++CONFIG_REGULATOR_AXP20X=y
++CONFIG_REGULATOR_GPIO=y
++CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY=y
++# CONFIG_MEDIA_CEC_SUPPORT is not set
++CONFIG_MEDIA_SUPPORT=y
++CONFIG_MEDIA_USB_SUPPORT=y
++CONFIG_USB_VIDEO_CLASS=y
++CONFIG_V4L_PLATFORM_DRIVERS=y
++CONFIG_V4L_MEM2MEM_DRIVERS=y
++CONFIG_VIDEO_CADENCE_CSI2RX=y
++CONFIG_VIDEO_WAVE_VPU=m
++CONFIG_VIN_SENSOR_OV4689=y
++CONFIG_VIN_SENSOR_IMX219=y
++CONFIG_VIDEO_STF_VIN=y
++CONFIG_VIDEO_IMX219=y
++CONFIG_VIDEO_IMX708=y
++CONFIG_DRM_PANEL_SIMPLE=y
++CONFIG_DRM_PANEL_JADARD_JD9365DA_H3=y
++CONFIG_DRM_TOSHIBA_TC358762=y
++CONFIG_DRM_VERISILICON=y
++CONFIG_STARFIVE_INNO_HDMI=y
++CONFIG_STARFIVE_DSI=y
++CONFIG_DRM_IMG_ROGUE=y
++CONFIG_DRM_LEGACY=y
++CONFIG_FB=y
++CONFIG_BACKLIGHT_CLASS_DEVICE=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_USB_AUDIO=y
++CONFIG_SND_SOC=y
++CONFIG_SND_DESIGNWARE_I2S=y
++CONFIG_SND_SOC_RZ=m
++CONFIG_SND_SOC_STARFIVE=y
++CONFIG_SND_SOC_JH7110_PWMDAC=y
++CONFIG_SND_SOC_JH7110_TDM=y
++CONFIG_SND_SOC_AC108=y
++CONFIG_SND_SOC_WM8960=y
++CONFIG_SND_SIMPLE_CARD=y
++CONFIG_UHID=y
++CONFIG_USB=y
++CONFIG_USB_OTG=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_RENESAS_USBHS=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_UAS=y
++CONFIG_USB_CDNS_SUPPORT=y
++CONFIG_USB_CDNS3=y
++CONFIG_USB_CDNS3_GADGET=y
++CONFIG_USB_CDNS3_HOST=y
++CONFIG_USB_CDNS3_STARFIVE=y
++CONFIG_USB_SERIAL=m
++CONFIG_USB_SERIAL_GENERIC=y
++CONFIG_USB_SERIAL_AIRCABLE=m
++CONFIG_USB_SERIAL_ARK3116=m
++CONFIG_USB_SERIAL_BELKIN=m
++CONFIG_USB_SERIAL_CH341=m
++CONFIG_USB_SERIAL_WHITEHEAT=m
++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
++CONFIG_USB_SERIAL_CP210X=m
++CONFIG_USB_SERIAL_CYPRESS_M8=m
++CONFIG_USB_SERIAL_EMPEG=m
++CONFIG_USB_SERIAL_FTDI_SIO=m
++CONFIG_USB_SERIAL_VISOR=m
++CONFIG_USB_SERIAL_IPAQ=m
++CONFIG_USB_SERIAL_IR=m
++CONFIG_USB_SERIAL_EDGEPORT=m
++CONFIG_USB_SERIAL_EDGEPORT_TI=m
++CONFIG_USB_SERIAL_F81232=m
++CONFIG_USB_SERIAL_GARMIN=m
++CONFIG_USB_SERIAL_IPW=m
++CONFIG_USB_SERIAL_IUU=m
++CONFIG_USB_SERIAL_KEYSPAN_PDA=m
++CONFIG_USB_SERIAL_KEYSPAN=m
++CONFIG_USB_SERIAL_KLSI=m
++CONFIG_USB_SERIAL_KOBIL_SCT=m
++CONFIG_USB_SERIAL_MCT_U232=m
++CONFIG_USB_SERIAL_METRO=m
++CONFIG_USB_SERIAL_MOS7720=m
++CONFIG_USB_SERIAL_MOS7840=m
++CONFIG_USB_SERIAL_NAVMAN=m
++CONFIG_USB_SERIAL_PL2303=m
++CONFIG_USB_SERIAL_OTI6858=m
++CONFIG_USB_SERIAL_QCAUX=m
++CONFIG_USB_SERIAL_QUALCOMM=m
++CONFIG_USB_SERIAL_SPCP8X5=m
++CONFIG_USB_SERIAL_SAFE=m
++CONFIG_USB_SERIAL_SIERRAWIRELESS=m
++CONFIG_USB_SERIAL_SYMBOL=m
++CONFIG_USB_SERIAL_TI=m
++CONFIG_USB_SERIAL_CYBERJACK=m
++CONFIG_USB_SERIAL_OMNINET=m
++CONFIG_USB_SERIAL_OPTICON=m
++CONFIG_USB_SERIAL_XSENS_MT=m
++CONFIG_USB_SERIAL_WISHBONE=m
++CONFIG_USB_SERIAL_SSU100=m
++CONFIG_USB_SERIAL_DEBUG=m
++CONFIG_USB_GADGET=y
++CONFIG_USB_RENESAS_USBHS_UDC=m
++CONFIG_USB_CONFIGFS=y
++CONFIG_USB_CONFIGFS_SERIAL=y
++CONFIG_USB_CONFIGFS_ACM=y
++CONFIG_USB_CONFIGFS_OBEX=y
++CONFIG_USB_CONFIGFS_NCM=y
++CONFIG_USB_CONFIGFS_ECM=y
++CONFIG_USB_CONFIGFS_ECM_SUBSET=y
++CONFIG_USB_CONFIGFS_RNDIS=y
++CONFIG_USB_CONFIGFS_EEM=y
++CONFIG_USB_CONFIGFS_MASS_STORAGE=y
++CONFIG_USB_CONFIGFS_F_FS=y
++CONFIG_MMC=y
++CONFIG_MMC_DEBUG=y
++CONFIG_MMC_SDHCI=y
++CONFIG_MMC_SDHCI_PLTFM=y
++CONFIG_MMC_SDHCI_OF_DWCMSHC=y
++CONFIG_MMC_SPI=y
++CONFIG_MMC_SDHI=y
++CONFIG_MMC_DW=y
++CONFIG_MMC_DW_STARFIVE=y
++CONFIG_NEW_LEDS=y
++CONFIG_LEDS_CLASS=y
++CONFIG_LEDS_GPIO=y
++CONFIG_LEDS_TRIGGER_HEARTBEAT=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_STARFIVE=y
++CONFIG_RTC_DRV_GOLDFISH=y
++CONFIG_DMADEVICES=y
++CONFIG_AMBA_PL08X=y
++CONFIG_DW_AXI_DMAC=y
++CONFIG_DMATEST=y
++# CONFIG_VIRTIO_MENU is not set
++# CONFIG_VHOST_MENU is not set
++CONFIG_GOLDFISH=y
++CONFIG_CLK_STARFIVE_JH7110_AON=y
++CONFIG_CLK_STARFIVE_JH7110_STG=y
++CONFIG_CLK_STARFIVE_JH7110_ISP=y
++CONFIG_CLK_STARFIVE_JH7110_VOUT=y
++CONFIG_MAILBOX=y
++CONFIG_STARFIVE_MBOX=m
++CONFIG_STARFIVE_MBOX_TEST=m
++# CONFIG_IOMMU_SUPPORT is not set
++CONFIG_RPMSG_CHAR=y
++CONFIG_RPMSG_VIRTIO=y
++CONFIG_SIFIVE_CCACHE=y
++CONFIG_PM_DEVFREQ=y
++CONFIG_IIO=y
++CONFIG_IIO_ST_ACCEL_3AXIS=y
++CONFIG_PWM=y
++CONFIG_PWM_OCORES=y
++CONFIG_PHY_STARFIVE_JH7110_PCIE=y
++CONFIG_PHY_STARFIVE_JH7110_USB=y
++CONFIG_PHY_M31_DPHY_RX0=y
++CONFIG_RAS=y
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_POSIX_ACL=y
++CONFIG_BTRFS_FS=m
++CONFIG_BTRFS_FS_POSIX_ACL=y
++CONFIG_AUTOFS_FS=y
++CONFIG_FUSE_FS=y
++CONFIG_CUSE=y
++CONFIG_VIRTIO_FS=y
++CONFIG_FSCACHE=y
++CONFIG_FSCACHE_STATS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_UTF8=y
++CONFIG_EXFAT_FS=y
++CONFIG_NTFS_FS=y
++CONFIG_NTFS_RW=y
++CONFIG_TMPFS=y
++CONFIG_TMPFS_POSIX_ACL=y
++CONFIG_HUGETLBFS=y
++CONFIG_JFFS2_FS=y
++CONFIG_NFS_FS=y
++CONFIG_NFS_V4=y
++CONFIG_NFS_V4_1=y
++CONFIG_NFS_V4_2=y
++CONFIG_ROOT_NFS=y
++CONFIG_CIFS=m
++# CONFIG_CIFS_STATS2 is not set
++CONFIG_CIFS_UPCALL=y
++CONFIG_CIFS_XATTR=y
++CONFIG_CIFS_POSIX=y
++# CONFIG_CIFS_DEBUG is not set
++CONFIG_CIFS_DFS_UPCALL=y
++CONFIG_CIFS_FSCACHE=y
++CONFIG_SMB_SERVER=m
++CONFIG_NLS_CODEPAGE_437=y
++CONFIG_NLS_ISO8859_1=y
++CONFIG_INIT_STACK_NONE=y
++CONFIG_CRYPTO_USER=y
++CONFIG_CRYPTO_TEST=m
++CONFIG_CRYPTO_USER_API_HASH=y
++CONFIG_CRYPTO_USER_API_SKCIPHER=y
++CONFIG_CRYPTO_USER_API_RNG=y
++CONFIG_CRYPTO_USER_API_AEAD=y
++CONFIG_CRYPTO_DEV_VIRTIO=y
++CONFIG_CRYPTO_DEV_JH7110=y
++CONFIG_DMA_CMA=y
++CONFIG_PRINTK_TIME=y
++CONFIG_DEBUG_FS=y
++CONFIG_SOFTLOCKUP_DETECTOR=y
++CONFIG_WQ_WATCHDOG=y
++CONFIG_DEBUG_TIMEKEEPING=y
++CONFIG_DEBUG_RT_MUTEXES=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_RWSEMS=y
++CONFIG_DEBUG_LIST=y
++CONFIG_DEBUG_PLIST=y
++CONFIG_DEBUG_SG=y
++# CONFIG_RCU_TRACE is not set
++CONFIG_RCU_EQS_DEBUG=y
++# CONFIG_FTRACE is not set
++# CONFIG_RUNTIME_TESTING_MENU is not set
++CONFIG_MEMTEST=y
diff --git a/target/linux/starfive/patches-6.6/0106-riscv-dts-starfive-update-dts-to-kernel-6.6.patch b/target/linux/starfive/patches-6.6/0106-riscv-dts-starfive-update-dts-to-kernel-6.6.patch
new file mode 100644 (file)
index 0000000..defc0e6
--- /dev/null
@@ -0,0 +1,759 @@
+From 51c1cccb202d741eeb1de57e0ecf8fcaa8f059e8 Mon Sep 17 00:00:00 2001
+From: Ziv Xu <ziv.xu@starfivetech.com>
+Date: Wed, 13 Mar 2024 18:36:31 +0800
+Subject: [PATCH 106/116] riscv: dts: starfive: update dts to kernel 6.6
+
+update dts to kernel 6.6
+
+Signed-off-by: Ziv Xu <ziv.xu@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile         |   5 +-
+ .../jh7110-starfive-visionfive-2-ac108.dts    |  64 +++
+ .../jh7110-starfive-visionfive-2-tdm.dts      |  59 +++
+ .../jh7110-starfive-visionfive-2-wm8960.dts   |  71 +++
+ .../jh7110-starfive-visionfive-2.dtsi         | 437 +++++++++++++++++-
+ 5 files changed, 633 insertions(+), 3 deletions(-)
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
+ create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -10,7 +10,10 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-be
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb \
++                                              jh7110-starfive-visionfive-2-ac108.dtb  \
++                                              jh7110-starfive-visionfive-2-tdm.dtb    \
++                                              jh7110-starfive-visionfive-2-wm8960.dtb
+ subdir-y += evb-overlay
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb                 \
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts
+@@ -0,0 +1,64 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
++ */
++
++/dts-v1/;
++#include "jh7110-starfive-visionfive-2-v1.3b.dts"
++
++/ {
++      /* i2s + ac108 */
++      sound0: snd-card0 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "Starfive-AC108-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "i2s";
++                      bitclock-master = <&sndcodec1>;
++                      frame-master = <&sndcodec1>;
++
++                      widgets =
++                                      "Microphone", "Mic Jack",
++                                      "Line", "Line In",
++                                      "Line", "Line Out",
++                                      "Speaker", "Speaker",
++                                      "Headphone", "Headphone Jack";
++                      routing =
++                                      "Headphone Jack", "HP_L",
++                                      "Headphone Jack", "HP_R",
++                                      "Speaker", "SPK_LP",
++                                      "Speaker", "SPK_LN",
++                                      "LINPUT1", "Mic Jack",
++                                      "LINPUT3", "Mic Jack",
++                                      "RINPUT1", "Mic Jack",
++                                      "RINPUT2", "Mic Jack";
++
++                      cpu {
++                              sound-dai = <&i2srx_mst>;
++                      };
++
++                      sndcodec1: codec {
++                              sound-dai = <&ac108>;
++                              clocks = <&ac108_mclk>;
++                              clock-names = "mclk";
++                      };
++              };
++      };
++};
++
++&i2c0 {
++      ac108: ac108@3b {
++              compatible = "x-power,ac108_0";
++              reg = <0x3b>;
++              #sound-dai-cells = <0>;
++              data-protocol = <0>;
++      };
++};
++
++&i2srx_mst {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
+@@ -0,0 +1,59 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
++ */
++
++/dts-v1/;
++#include "jh7110-starfive-visionfive-2-v1.3b.dts"
++
++/ {
++      sound-tdm {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "Starfive-TDM-Sound-Card";
++              simple-audio-card,widgets = "Microphone", "Mic Jack",
++                                          "Line", "Line In",
++                                          "Line", "Line Out",
++                                          "Speaker", "Speaker",
++                                          "Headphone", "Headphone Jack";
++              simple-audio-card,routing = "Headphone Jack", "HP_L",
++                                          "Headphone Jack", "HP_R",
++                                          "Speaker", "SPK_LP",
++                                          "Speaker", "SPK_LN",
++                                          "LINPUT1", "Mic Jack",
++                                          "LINPUT3", "Mic Jack",
++                                          "RINPUT1", "Mic Jack",
++                                          "RINPUT2", "Mic Jack";
++
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "dsp_a";
++                      bitclock-master = <&dailink_master>;
++                      frame-master = <&dailink_master>;
++
++                      cpu {
++                              sound-dai = <&tdm>;
++                      };
++                      dailink_master: codec {
++                              sound-dai = <&wm8960>;
++                              clocks = <&wm8960_mclk>;
++                      };
++              };
++      };
++};
++
++&i2c0 {
++      wm8960: codec@1a {
++              compatible = "wlf,wm8960";
++              reg = <0x1a>;
++              wlf,shared-lrclk;
++              #sound-dai-cells = <0>;
++      };
++};
++
++&tdm {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
+@@ -0,0 +1,71 @@
++// SPDX-License-Identifier: GPL-2.0 OR MIT
++/*
++ * Copyright (C) 2022 StarFive Technology Co., Ltd.
++ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
++ */
++
++/dts-v1/;
++#include "jh7110-starfive-visionfive-2-v1.3b.dts"
++
++/ {
++      /* i2s + wm8960 */
++      sound-wm8960 {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "Starfive-WM8960-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      status = "okay";
++                      format = "i2s";
++                      bitclock-master = <&sndcodec1>;
++                      frame-master = <&sndcodec1>;
++                      
++                      widgets =
++                                      "Microphone", "Mic Jack",
++                                      "Line", "Line In",
++                                      "Line", "Line Out",
++                                      "Speaker", "Speaker",
++                                      "Headphone", "Headphone Jack";
++                      routing =
++                                      "Headphone Jack", "HP_L",
++                                      "Headphone Jack", "HP_R",
++                                      "Speaker", "SPK_LP",
++                                      "Speaker", "SPK_LN",
++                                      "LINPUT1", "Mic Jack",
++                                      "LINPUT3", "Mic Jack",
++                                      "RINPUT1", "Mic Jack",
++                                      "RINPUT2", "Mic Jack";
++                      cpu0 {
++                              sound-dai = <&i2srx>;
++                      };
++                      cpu1 {
++                              sound-dai = <&i2stx1>;
++                      };
++                      
++                      sndcodec1:codec {
++                              sound-dai = <&wm8960>;
++                              clocks = <&wm8960_mclk>;
++                              clock-names = "mclk";
++                      };
++              };
++      };
++};
++
++&i2c0 {
++      wm8960: codec@1a {
++              compatible = "wlf,wm8960";
++              reg = <0x1a>;
++              wlf,shared-lrclk;
++              #sound-dai-cells = <0>;
++      };
++};
++
++&i2srx {
++      status = "okay";
++};
++
++&i2stx1 {
++      status = "okay";
++};
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -7,6 +7,7 @@
+ /dts-v1/;
+ #include "jh7110.dtsi"
+ #include "jh7110-pinfunc.h"
++#include <dt-bindings/leds/common.h>
+ #include <dt-bindings/gpio/gpio.h>
+ / {
+@@ -37,6 +38,44 @@
+               reg = <0x0 0x40000000 0x1 0x0>;
+       };
++      reserved-memory {
++              #address-cells = <2>;
++              #size-cells = <2>;
++              ranges;
++
++              linux,cma {
++                      compatible = "shared-dma-pool";
++                      reusable;
++                      size = <0x0 0x20000000>;
++                      alignment = <0x0 0x1000>;
++                      alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
++                      linux,cma-default;
++              };
++
++              e24_mem: e24@c0000000 {
++                      reg = <0x0 0x6ce00000 0x0 0x1600000>;
++              };
++
++              xrp_reserved: xrpbuffer@f0000000 {
++                      reg = <0x0 0x69c00000 0x0 0x01ffffff
++                              0x0 0x6bc00000 0x0 0x00001000
++                              0x0 0x6bc01000 0x0 0x00fff000
++                              0x0 0x6cc00000 0x0 0x00001000>;
++              };
++      };
++
++      leds {
++              compatible = "gpio-leds";
++
++              led-ack {
++                      gpios = <&aongpio 3 GPIO_ACTIVE_HIGH>;
++                      color = <LED_COLOR_ID_GREEN>;
++                      function = LED_FUNCTION_HEARTBEAT;
++                      linux,default-trigger = "heartbeat";
++                      label = "ack";
++              };
++      };
++
+       gpio-restart {
+               compatible = "gpio-restart";
+               gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
+@@ -69,6 +108,48 @@
+                       };
+               };
+       };
++
++      sound-hdmi {
++              compatible = "simple-audio-card";
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              simple-audio-card,name = "StarFive-HDMI-Sound-Card";
++              simple-audio-card,dai-link@0 {
++                      reg = <0>;
++                      format = "i2s";
++                      bitclock-master = <&sndi2s0>;
++                      frame-master = <&sndi2s0>;
++                      mclk-fs = <256>;
++                      status = "okay";
++
++                      sndi2s0: cpu {
++                              sound-dai = <&i2stx0>;
++                      };
++
++                      codec {
++                              sound-dai = <&hdmi>;
++                      };
++              };
++      };
++
++      ac108_mclk: ac108_mclk {
++              compatible = "fixed-clock";
++              #clock-cells = <0>;
++              clock-frequency = <24000000>;
++      };
++
++      clk_ext_camera: clk-ext-camera {
++              compatible = "fixed-clock";
++              #clock-cells = <0>;
++              clock-frequency = <24000000>;
++      };
++
++      wm8960_mclk: wm8960_mclk {
++              compatible = "fixed-clock";
++              #clock-cells = <0>;
++              clock-frequency = <24576000>;
++      };
+ };
+ &dvp_clk {
+@@ -177,6 +258,42 @@
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c2_pins>;
+       status = "okay";
++
++      seeed_plane_i2c@45 {
++              compatible = "seeed_panel";
++              reg = <0x45>;
++
++              port {
++                      panel_out0: endpoint {
++                              remote-endpoint = <&dsi0_output>;
++                      };
++              };
++      };
++
++      tinker_ft5406: tinker_ft5406@38 {
++              compatible = "tinker_ft5406";
++              reg = <0x38>;
++      };
++
++      touchscreen@14 {
++              compatible = "goodix,gt911";
++              reg = <0x14>;
++              irq-gpios = <&sysgpio 30 GPIO_ACTIVE_HIGH>;
++              reset-gpios = <&sysgpio 31 GPIO_ACTIVE_HIGH>;
++      };
++
++      panel_radxa@19 {
++              compatible ="starfive_jadard";
++              reg = <0x19>;
++              reset-gpio = <&sysgpio 23 0>;
++              enable-gpio = <&sysgpio 22 0>;
++
++              port {
++                      panel_out1: endpoint {
++                              remote-endpoint = <&dsi1_output>;
++                      };
++              };
++      };
+ };
+ &i2c5 {
+@@ -195,11 +312,36 @@
+               #interrupt-cells = <1>;
+               regulators {
++                      mipi_0p9: ALDO1 {
++                              regulator-boot-on;
++                              regulator-compatible = "aldo1";
++                              regulator-name = "mipi_0p9";
++                              regulator-min-microvolt = <900000>;
++                              regulator-max-microvolt = <900000>;
++                      };
++
++                      hdmi_0p9: ALDO5 {
++                              regulator-boot-on;
++                              regulator-compatible = "aldo5";
++                              regulator-name = "hdmi_0p9";
++                              regulator-min-microvolt = <900000>;
++                              regulator-max-microvolt = <900000>;
++                      };
++
++                      hdmi_1p8: ALDO3 {
++                              regulator-boot-on;
++                              regulator-compatible = "aldo3";
++                              regulator-name = "hdmi_1p8";
++                              regulator-min-microvolt = <1800000>;
++                              regulator-max-microvolt = <1800000>;
++                      };
++
+                       vcc_3v3: dcdc1 {
+                               regulator-boot-on;
+                               regulator-always-on;
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
++                              regulator-compatible = "dcdc1";
+                               regulator-name = "vcc_3v3";
+                       };
+@@ -207,6 +349,7 @@
+                               regulator-always-on;
+                               regulator-min-microvolt = <500000>;
+                               regulator-max-microvolt = <1540000>;
++                              regulator-compatible = "dcdc2";
+                               regulator-name = "vdd-cpu";
+                       };
+@@ -215,6 +358,7 @@
+                               regulator-always-on;
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
++                              regulator-compatible = "aldo4";
+                               regulator-name = "emmc_vdd";
+                       };
+               };
+@@ -229,12 +373,68 @@
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c6_pins>;
+       status = "okay";
++
++      imx219: imx219@10 {
++              compatible = "sony,imx219";
++              reg = <0x10>;
++              clocks = <&clk_ext_camera>;
++              clock-names = "xclk";
++              reset-gpio = <&sysgpio 18 0>;
++              rotation = <0>;
++              orientation = <1>; //CAMERA_ORIENTATION_BACK
++
++              port {
++                      /* CSI2 bus endpoint */
++                      imx219_to_csi2rx0: endpoint {
++                              remote-endpoint = <&csi2rx0_from_imx219>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <4>;
++                              data-lanes = <0 1>;
++                              lane-polarities = <0 0 0>;
++                              link-frequencies = /bits/ 64 <456000000>;
++                      };
++              };
++      };
++
++      imx708: imx708@1a {
++              compatible = "sony,imx708";
++              reg = <0x1a>;
++              clocks = <&clk_ext_camera>;
++              reset-gpio = <&sysgpio 18 0>;
++
++              port {
++                      imx708_to_csi2rx0: endpoint {
++                              remote-endpoint = <&csi2rx0_from_imx708>;
++                              data-lanes = <1 2>;
++                              clock-noncontinuous;
++                              link-frequencies = /bits/ 64 <450000000>;
++                      };
++              };
++      };
++
++      ov4689: ov4689@36 {
++              compatible = "ovti,ov4689";
++              reg = <0x36>;
++              clocks = <&clk_ext_camera>;
++              clock-names = "xclk";
++              rotation = <180>;
++
++              port {
++                      /* Parallel bus endpoint */
++                      ov4689_to_csi2rx0: endpoint {
++                              remote-endpoint = <&csi2rx0_from_ov4689>;
++                              bus-type = <4>;         /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <0>;
++                              data-lanes = <1 2>;
++                      };
++              };
++      };
+ };
+ &i2srx {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2srx_pins>;
+-      status = "okay";
++      status = "disabled";
+ };
+ &i2stx0 {
+@@ -246,7 +446,7 @@
+ &i2stx1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2stx1_pins>;
+-      status = "okay";
++      status = "disabled";
+ };
+ &mmc0 {
+@@ -661,6 +861,40 @@
+                       slew-rate = <0>;
+               };
+       };
++
++      hdmi_pins: hdmi-0 {
++              scl-pins {
++                      pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
++                                           GPOEN_SYS_HDMI_DDC_SCL,
++                                           GPI_SYS_HDMI_DDC_SCL)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++
++              sda-pins {
++                      pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
++                                           GPOEN_SYS_HDMI_DDC_SDA,
++                                           GPI_SYS_HDMI_DDC_SDA)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++
++              cec-pins {
++                      pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
++                                            GPOEN_SYS_HDMI_CEC_SDA,
++                                            GPI_SYS_HDMI_CEC_SDA)>;
++                      bias-pull-up;
++                      input-enable;
++              };
++
++              hpd-pins {
++                      pinmux = <GPIOMUX(15, GPOUT_LOW,
++                                            GPOEN_DISABLE,
++                                            GPI_SYS_HDMI_HPD)>;
++                      bias-disable; /* external pull-up */
++                      input-enable;
++              };
++      };
+ };
+ &uart0 {
+@@ -689,3 +923,202 @@
+ &U74_4 {
+       cpu-supply = <&vdd_cpu>;
+ };
++
++
++&display {
++      ports = <&dc_out_dpi0>;
++      status = "okay";
++};
++
++&dc8200 {
++      status = "okay";
++
++      dc_out: port {
++              #address-cells = <1>;
++              #size-cells = <0>;
++              dc_out_dpi0: endpoint@0 {
++                      reg = <0>;
++                      remote-endpoint = <&hdmi_input0>;
++              };
++              dc_out_dpi1: endpoint@1 {
++                      reg = <1>;
++                      remote-endpoint = <&hdmi_in_lcdc>;
++              };
++
++              dc_out_dpi2: endpoint@2 {
++                      reg = <2>;
++                      remote-endpoint = <&mipi_in>;
++              };
++      };
++};
++
++&hdmi {
++      status = "okay";
++      pinctrl-names = "default";
++      pinctrl-0 = <&hdmi_pins>;
++      hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
++
++      hdmi_in: port {
++              #address-cells = <1>;
++              #size-cells = <0>;
++              hdmi_in_lcdc: endpoint@0 {
++                      reg = <0>;
++                      remote-endpoint = <&dc_out_dpi1>;
++              };
++      };
++};
++
++&rgb_output {
++      status = "disabled";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++              port@0 {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      reg = <0>;
++                      hdmi_input0:endpoint@0 {
++                              reg = <0>;
++                              remote-endpoint = <&dc_out_dpi0>;
++                      };
++              };
++      };
++};
++
++&dsi_output {
++      status = "okay";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@0 {
++                      reg = <0>;
++                      mipi_in: endpoint {
++                              remote-endpoint = <&dc_out_dpi2>;
++                      };
++              };
++
++              port@1 {
++                      reg = <1>;
++                      sf_dpi_output: endpoint {
++                              remote-endpoint = <&dsi_in_port>;
++                      };
++              };
++      };
++};
++
++&mipi_dsi {
++      status = "okay";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@0 {
++                      reg = <0>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++
++                      dsi0_output: endpoint@0 {
++                              reg = <0>;
++                              remote-endpoint = <&panel_out0>;
++                      };
++
++                      dsi1_output: endpoint@1 {
++                              reg = <1>;
++                              remote-endpoint = <&panel_out1>;
++                      };
++
++              };
++
++              port@1{
++                      reg = <1>;
++                      dsi_in_port: endpoint {
++                              remote-endpoint = <&sf_dpi_output>;
++                      };
++              };
++
++      };
++};
++
++&mipi_dphy {
++      status = "okay";
++};
++
++&co_process {
++      memory-region = <&e24_mem>;
++      status = "okay";
++};
++
++
++&mailbox_contrl0 {
++      status = "okay";
++};
++
++&mailbox_client0 {
++      status = "okay";
++};
++
++&vpu_dec {
++      status = "okay";
++};
++
++&vpu_enc {
++      status = "okay";
++};
++
++&jpu {
++      status = "okay";
++};
++
++&gpu {
++      status = "okay";
++};
++
++&vin_sysctl {
++      /* when use dvp open this pinctrl*/
++      status = "okay";
++
++      ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@1 {
++                      reg = <1>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++
++                      /* CSI2 bus endpoint */
++                      csi2rx0_from_imx219: endpoint@0 {
++                              reg = <0>;
++                              remote-endpoint = <&imx219_to_csi2rx0>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <4>;
++                              data-lanes = <0 1>;
++                              lane-polarities = <0 0 0>;
++                              status = "okay";
++                      };
++
++                      csi2rx0_from_imx708: endpoint@1 {
++                              reg = <1>;
++                              remote-endpoint = <&imx708_to_csi2rx0>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <4>;
++                              data-lanes = <0 1>;
++                              lane-polarities = <0 0 0>;
++                              status = "okay";
++                      };
++
++                      csi2rx0_from_ov4689: endpoint@2 {
++                              reg = <2>;
++                              remote-endpoint = <&ov4689_to_csi2rx0>;
++                              bus-type = <4>;      /* MIPI CSI-2 D-PHY */
++                              clock-lanes = <4>;
++                              data-lanes = <0 1>;
++                              status = "okay";
++                      };
++              };
++      };
++};
diff --git a/target/linux/starfive/patches-6.6/0107-riscv-dts-starfive-evb-overlay-Support-SPI-overlay.patch b/target/linux/starfive/patches-6.6/0107-riscv-dts-starfive-evb-overlay-Support-SPI-overlay.patch
new file mode 100644 (file)
index 0000000..8738831
--- /dev/null
@@ -0,0 +1,71 @@
+From 17a781895079f8f9b2c089d13a1e4d9789e018c5 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Wed, 20 Mar 2024 17:36:14 +0800
+Subject: [PATCH 107/116] riscv: dts: starfive: evb-overlay: Support SPI
+ overlay
+
+Add new compatibles to support SPI overlay.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso   | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso
+@@ -9,6 +9,7 @@
+       fragment@0 {
+               target-path = "/soc/spi@10060000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
+@@ -17,6 +18,7 @@
+       fragment@1 {
+               target-path = "/soc/spi@10070000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
+@@ -25,6 +27,7 @@
+       fragment@2 {
+               target-path = "/soc/spi@10080000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
+@@ -33,6 +36,7 @@
+       fragment@3 {
+               target-path = "/soc/spi@12070000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
+@@ -41,6 +45,7 @@
+       fragment@4 {
+               target-path = "/soc/spi@12080000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
+@@ -49,6 +54,7 @@
+       fragment@5 {
+               target-path = "/soc/spi@12090000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
+@@ -57,6 +63,7 @@
+       fragment@6 {
+               target-path = "/soc/spi@120a0000";
+               __overlay__ {
++                      compatible = "starfive,jh7110-spi-pl022";
+                       status = "okay";
+               };
+       };
diff --git a/target/linux/starfive/patches-6.6/0108-riscv-configs-visionfive2-Add-standard-partition-for.patch b/target/linux/starfive/patches-6.6/0108-riscv-configs-visionfive2-Add-standard-partition-for.patch
new file mode 100644 (file)
index 0000000..a4b2e14
--- /dev/null
@@ -0,0 +1,23 @@
+From 49b818cdd08fca7ff761277635c0e5ab659becb8 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Thu, 21 Mar 2024 16:53:51 +0800
+Subject: [PATCH 108/116] riscv: configs: visionfive2: Add standard partition
+ for hibernation
+
+Add CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation".
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/configs/starfive_visionfive2_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/riscv/configs/starfive_visionfive2_defconfig
++++ b/arch/riscv/configs/starfive_visionfive2_defconfig
+@@ -24,6 +24,7 @@ CONFIG_NONPORTABLE=y
+ CONFIG_SMP=y
+ CONFIG_HZ_100=y
+ CONFIG_HIBERNATION=y
++CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation"
+ CONFIG_PM_DEBUG=y
+ CONFIG_PM_ADVANCED_DEBUG=y
+ CONFIG_PM_TEST_SUSPEND=y
diff --git a/target/linux/starfive/patches-6.6/0109-usb-xhci-To-improve-performance-usb-using-lowmem-for.patch b/target/linux/starfive/patches-6.6/0109-usb-xhci-To-improve-performance-usb-using-lowmem-for.patch
new file mode 100644 (file)
index 0000000..3f94999
--- /dev/null
@@ -0,0 +1,297 @@
+From 146eb94a08d12b5831e1d30455469750f7c5f2a3 Mon Sep 17 00:00:00 2001
+From: "minda.chen" <minda.chen@starfivetech.com>
+Date: Tue, 18 Oct 2022 09:57:39 +0800
+Subject: [PATCH 109/116] usb:xhci:To improve performance,usb using lowmem for
+ bulk xfer.
+
+Generate an usb low memory pool for usb 3.0 host
+read/write transfer, default size is 8M.
+
+Signed-off-by: minda.chen <minda.chen@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-evb.dts |  1 +
+ drivers/usb/core/hcd.c                      |  4 +-
+ drivers/usb/host/xhci-mem.c                 | 64 +++++++++++++++++++++
+ drivers/usb/host/xhci-plat.c                |  8 +++
+ drivers/usb/host/xhci-ring.c                |  3 +-
+ drivers/usb/host/xhci.c                     | 57 +++++++++++++++++-
+ drivers/usb/host/xhci.h                     | 11 ++++
+ 7 files changed, 145 insertions(+), 3 deletions(-)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-evb.dts
++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts
+@@ -31,5 +31,6 @@
+ };
+ &usb0 {
++      xhci-lowmem-pool;
+       status = "okay";
+ };
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -1439,7 +1439,9 @@ int usb_hcd_map_urb_for_dma(struct usb_h
+                       if (ret == 0)
+                               urb->transfer_flags |= URB_MAP_LOCAL;
+               } else if (hcd_uses_dma(hcd)) {
+-                      if (urb->num_sgs) {
++                      if (urb->transfer_flags & URB_MAP_LOCAL)
++                              return ret;
++                      else if (urb->num_sgs) {
+                               int n;
+                               /* We don't support sg for isoc transfers ! */
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -14,6 +14,7 @@
+ #include <linux/slab.h>
+ #include <linux/dmapool.h>
+ #include <linux/dma-mapping.h>
++#include <linux/genalloc.h>
+ #include "xhci.h"
+ #include "xhci-trace.h"
+@@ -1842,6 +1843,7 @@ xhci_free_interrupter(struct xhci_hcd *x
+ void xhci_mem_cleanup(struct xhci_hcd *xhci)
+ {
+       struct device   *dev = xhci_to_hcd(xhci)->self.sysdev;
++      struct xhci_lowmem_pool *pool;
+       int i, j, num_ports;
+       cancel_delayed_work_sync(&xhci->cmd_timer);
+@@ -1887,6 +1889,13 @@ void xhci_mem_cleanup(struct xhci_hcd *x
+       xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+                       "Freed medium stream array pool");
++      if (xhci->lowmem_pool.pool) {
++              pool = &xhci->lowmem_pool;
++              dma_free_coherent(dev, pool->size, (void *)pool->cached_base, pool->dma_addr);
++              gen_pool_destroy(pool->pool);
++              pool->pool = NULL;
++      }
++
+       if (xhci->dcbaa)
+               dma_free_coherent(dev, sizeof(*xhci->dcbaa),
+                               xhci->dcbaa, xhci->dcbaa->dma);
+@@ -2297,6 +2306,55 @@ xhci_add_interrupter(struct xhci_hcd *xh
+       return 0;
+ }
++int xhci_setup_local_lowmem(struct xhci_hcd *xhci, size_t size)
++{
++      int err;
++      void *buffer;
++      dma_addr_t dma_addr;
++      struct usb_hcd *hcd = xhci_to_hcd(xhci);
++      struct xhci_lowmem_pool *pool = &xhci->lowmem_pool;
++
++      if (!pool->pool) {
++              /* minimal alloc one page */
++              pool->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(hcd->self.sysdev));
++              if (IS_ERR(pool->pool))
++                      return PTR_ERR(pool->pool);
++      }
++
++      buffer = dma_alloc_coherent(hcd->self.sysdev, size, &dma_addr,
++                      GFP_KERNEL | GFP_DMA32);
++
++      if (IS_ERR(buffer)) {
++              err = PTR_ERR(buffer);
++              goto destroy_pool;
++      }
++
++      /*
++       * Here we pass a dma_addr_t but the arg type is a phys_addr_t.
++       * It's not backed by system memory and thus there's no kernel mapping
++       * for it.
++       */
++      err = gen_pool_add_virt(pool->pool, (unsigned long)buffer,
++                              dma_addr, size, dev_to_node(hcd->self.sysdev));
++      if (err < 0) {
++              dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n",
++                      err);
++              dma_free_coherent(hcd->self.sysdev, size, buffer, dma_addr);
++              goto destroy_pool;
++      }
++
++      pool->cached_base = (u64)buffer;
++      pool->dma_addr = dma_addr;
++
++      return 0;
++
++destroy_pool:
++      gen_pool_destroy(pool->pool);
++      pool->pool = NULL;
++      return err;
++}
++EXPORT_SYMBOL_GPL(xhci_setup_local_lowmem);
++
+ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
+ {
+       dma_addr_t      dma;
+@@ -2433,6 +2491,12 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+       xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
++      if (xhci->quirks & XHCI_LOCAL_BUFFER) {
++              if (xhci_setup_local_lowmem(xhci,
++                      xhci->lowmem_pool.size))
++                      goto fail;
++      }
++
+       /*
+        * XXX: Might need to set the Interrupter Moderation Register to
+        * something other than the default (~1ms minimum between interrupts).
+--- a/drivers/usb/host/xhci-plat.c
++++ b/drivers/usb/host/xhci-plat.c
+@@ -253,6 +253,14 @@ int xhci_plat_probe(struct platform_devi
+               if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
+                       xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
++              if (device_property_read_bool(tmpdev, "xhci-lowmem-pool")) {
++                      xhci->quirks |= XHCI_LOCAL_BUFFER;
++                      if (device_property_read_u32(tmpdev, "lowmem-pool-size",
++                              &xhci->lowmem_pool.size)) {
++                              xhci->lowmem_pool.size = 8 << 20;
++                      } else
++                              xhci->lowmem_pool.size <<= 20;
++              }
+               device_property_read_u32(tmpdev, "imod-interval-ns",
+                                        &xhci->imod_interval);
+       }
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -3650,7 +3650,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
+       full_len = urb->transfer_buffer_length;
+       /* If we have scatter/gather list, we use it. */
+-      if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) {
++      if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)
++                      && !(urb->transfer_flags & URB_MAP_LOCAL)) {
+               num_sgs = urb->num_mapped_sgs;
+               sg = urb->sg;
+               addr = (u64) sg_dma_address(sg);
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -18,6 +18,8 @@
+ #include <linux/slab.h>
+ #include <linux/dmi.h>
+ #include <linux/dma-mapping.h>
++#include <linux/dma-map-ops.h>
++#include <linux/genalloc.h>
+ #include "xhci.h"
+ #include "xhci-trace.h"
+@@ -1285,6 +1287,55 @@ static void xhci_unmap_temp_buf(struct u
+       urb->transfer_buffer = NULL;
+ }
++static void xhci_map_urb_local(struct usb_hcd *hcd, struct urb *urb,
++                              gfp_t mem_flags)
++{
++      void *buffer;
++      dma_addr_t dma_handle;
++      struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++      struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool;
++
++      if (lowmem_pool->pool
++              && (usb_endpoint_type(&urb->ep->desc) == USB_ENDPOINT_XFER_BULK)
++              && (urb->transfer_buffer_length > PAGE_SIZE)
++              && urb->num_sgs && urb->sg && (sg_phys(urb->sg) > 0xffffffff)) {
++              buffer = gen_pool_dma_alloc(lowmem_pool->pool,
++                      urb->transfer_buffer_length, &dma_handle);
++              if (buffer) {
++                      urb->transfer_dma = dma_handle;
++                      urb->transfer_buffer = buffer;
++                      urb->transfer_flags |= URB_MAP_LOCAL;
++                      if (usb_urb_dir_out(urb))
++                              sg_copy_to_buffer(urb->sg, urb->num_sgs,
++                                      (void *)buffer,
++                                      urb->transfer_buffer_length);
++              }
++      }
++
++}
++
++static void xhci_unmap_urb_local(struct usb_hcd *hcd, struct urb *urb)
++{
++      dma_addr_t dma_handle;
++      u64 cached_buffer;
++      struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++      struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool;
++
++      if (urb->transfer_flags & URB_MAP_LOCAL) {
++              dma_handle = urb->transfer_dma;
++              cached_buffer = lowmem_pool->cached_base +
++                      ((u32)urb->transfer_dma & (lowmem_pool->size - 1));
++              if (usb_urb_dir_in(urb))
++                      sg_copy_from_buffer(urb->sg, urb->num_sgs,
++                              (void *)cached_buffer, urb->transfer_buffer_length);
++              gen_pool_free(lowmem_pool->pool, (unsigned long)urb->transfer_buffer,
++                              urb->transfer_buffer_length);
++              urb->transfer_flags &= ~URB_MAP_LOCAL;
++              urb->transfer_buffer = NULL;
++      }
++}
++
++
+ /*
+  * Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT),
+  * we'll copy the actual data into the TRB address register. This is limited to
+@@ -1305,9 +1356,11 @@ static int xhci_map_urb_for_dma(struct u
+               if (xhci_urb_temp_buffer_required(hcd, urb))
+                       return xhci_map_temp_buffer(hcd, urb);
+       }
++      xhci_map_urb_local(hcd, urb, mem_flags);
+       return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+ }
++
+ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+ {
+       struct xhci_hcd *xhci;
+@@ -1320,8 +1373,10 @@ static void xhci_unmap_urb_for_dma(struc
+       if ((xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) && unmap_temp_buf)
+               xhci_unmap_temp_buf(hcd, urb);
+-      else
++      else {
++              xhci_unmap_urb_local(hcd, urb);
+               usb_hcd_unmap_urb_for_dma(hcd, urb);
++      }
+ }
+ /**
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1763,6 +1763,13 @@ struct xhci_hub {
+       u8                      min_rev;
+ };
++struct xhci_lowmem_pool {
++      struct gen_pool *pool;
++      u64             cached_base;
++      dma_addr_t      dma_addr;
++      unsigned int    size;
++};
++
+ /* There is one xhci_hcd structure per controller */
+ struct xhci_hcd {
+       struct usb_hcd *main_hcd;
+@@ -1914,6 +1921,8 @@ struct xhci_hcd {
+ #define XHCI_ZHAOXIN_TRB_FETCH        BIT_ULL(45)
+ #define XHCI_ZHAOXIN_HOST     BIT_ULL(46)
++#define XHCI_LOCAL_BUFFER     BIT_ULL(63)
++
+       unsigned int            num_active_eps;
+       unsigned int            limit_active_eps;
+       struct xhci_port        *hw_ports;
+@@ -1943,6 +1952,8 @@ struct xhci_hcd {
+       struct list_head        regset_list;
+       void                    *dbc;
++      struct xhci_lowmem_pool lowmem_pool;
++
+       /* platform-specific data -- must come last */
+       unsigned long           priv[] __aligned(sizeof(s64));
+ };
diff --git a/target/linux/starfive/patches-6.6/0110-usb-xhci-using-dma_alloc_noncoherent-to-alloc-low-me.patch b/target/linux/starfive/patches-6.6/0110-usb-xhci-using-dma_alloc_noncoherent-to-alloc-low-me.patch
new file mode 100644 (file)
index 0000000..739bc76
--- /dev/null
@@ -0,0 +1,106 @@
+From a170cb9936bb0b00d58aaea40984dbce1169fe42 Mon Sep 17 00:00:00 2001
+From: Minda Chen <minda.chen@starfivetech.com>
+Date: Mon, 3 Jul 2023 16:14:20 +0800
+Subject: [PATCH 110/116] usb: xhci: using dma_alloc_noncoherent to alloc low
+ memory pool
+
+For RISCV_NONCACHEHERENT is set, using dma_alloc_noncoherent
+to alloc cached large block low memory buffer. And set default
+size to 4M. (largest size of continuous memory can be supported)
+
+Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
+---
+ drivers/usb/host/xhci-mem.c  | 29 ++++++++++++++++-------------
+ drivers/usb/host/xhci-plat.c |  9 +++++----
+ 2 files changed, 21 insertions(+), 17 deletions(-)
+
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -1891,7 +1891,8 @@ void xhci_mem_cleanup(struct xhci_hcd *x
+       if (xhci->lowmem_pool.pool) {
+               pool = &xhci->lowmem_pool;
+-              dma_free_coherent(dev, pool->size, (void *)pool->cached_base, pool->dma_addr);
++              dma_free_noncoherent(dev, pool->size, (void *)pool->cached_base,
++                                   pool->dma_addr, DMA_BIDIRECTIONAL);
+               gen_pool_destroy(pool->pool);
+               pool->pool = NULL;
+       }
+@@ -2317,15 +2318,15 @@ int xhci_setup_local_lowmem(struct xhci_
+       if (!pool->pool) {
+               /* minimal alloc one page */
+               pool->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(hcd->self.sysdev));
+-              if (IS_ERR(pool->pool))
+-                      return PTR_ERR(pool->pool);
++              if (!pool->pool)
++                      return -ENOMEM;
+       }
+-      buffer = dma_alloc_coherent(hcd->self.sysdev, size, &dma_addr,
+-                      GFP_KERNEL | GFP_DMA32);
++      buffer = dma_alloc_noncoherent(hcd->self.sysdev, size, &dma_addr,
++              DMA_BIDIRECTIONAL, GFP_ATOMIC);
+-      if (IS_ERR(buffer)) {
+-              err = PTR_ERR(buffer);
++      if (!buffer) {
++              err = -ENOMEM;
+               goto destroy_pool;
+       }
+@@ -2335,11 +2336,11 @@ int xhci_setup_local_lowmem(struct xhci_
+        * for it.
+        */
+       err = gen_pool_add_virt(pool->pool, (unsigned long)buffer,
+-                              dma_addr, size, dev_to_node(hcd->self.sysdev));
++              dma_addr, size, dev_to_node(hcd->self.sysdev));
+       if (err < 0) {
+               dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n",
+                       err);
+-              dma_free_coherent(hcd->self.sysdev, size, buffer, dma_addr);
++              dma_free_noncoherent(hcd->self.sysdev, size, buffer, dma_addr, DMA_BIDIRECTIONAL);
+               goto destroy_pool;
+       }
+@@ -2362,7 +2363,7 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+       unsigned int    val, val2;
+       u64             val_64;
+       u32             page_size, temp;
+-      int             i;
++      int             i, ret;
+       INIT_LIST_HEAD(&xhci->cmd_list);
+@@ -2492,9 +2493,11 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+       xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
+       if (xhci->quirks & XHCI_LOCAL_BUFFER) {
+-              if (xhci_setup_local_lowmem(xhci,
+-                      xhci->lowmem_pool.size))
+-                      goto fail;
++              ret = xhci_setup_local_lowmem(xhci, xhci->lowmem_pool.size);
++              if (ret) {
++                      xhci->quirks &= ~XHCI_LOCAL_BUFFER;
++                      xhci_warn(xhci, "WARN: Can't alloc lowmem pool\n");
++              }
+       }
+       /*
+--- a/drivers/usb/host/xhci-plat.c
++++ b/drivers/usb/host/xhci-plat.c
+@@ -255,10 +255,11 @@ int xhci_plat_probe(struct platform_devi
+               if (device_property_read_bool(tmpdev, "xhci-lowmem-pool")) {
+                       xhci->quirks |= XHCI_LOCAL_BUFFER;
+-                      if (device_property_read_u32(tmpdev, "lowmem-pool-size",
+-                              &xhci->lowmem_pool.size)) {
+-                              xhci->lowmem_pool.size = 8 << 20;
+-                      } else
++                      ret = device_property_read_u32(tmpdev, "lowmem-pool-size",
++                              &xhci->lowmem_pool.size);
++                      if (ret || xhci->lowmem_pool.size >= 4)
++                              xhci->lowmem_pool.size = 4 << 20;
++                      else
+                               xhci->lowmem_pool.size <<= 20;
+               }
+               device_property_read_u32(tmpdev, "imod-interval-ns",
diff --git a/target/linux/starfive/patches-6.6/0111-riscv-dts-starfive-Add-vf2-overlay-dtso-subdir.patch b/target/linux/starfive/patches-6.6/0111-riscv-dts-starfive-Add-vf2-overlay-dtso-subdir.patch
new file mode 100644 (file)
index 0000000..451b1c1
--- /dev/null
@@ -0,0 +1,139 @@
+From fc86405992c37b1898fd9b9bc077be673d774269 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 22 Mar 2024 09:54:28 +0800
+Subject: [PATCH 111/116] riscv: dts: starfive: Add vf2-overlay dtso subdir
+
+Create subdir vf2-overlay/ and add overlay .dtso for VF2 board.
+The code is ported from tag JH7110_VF2_6.1_v5.11.4
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/Makefile         |  1 +
+ .../boot/dts/starfive/vf2-overlay/Makefile    |  3 +
+ .../starfive/vf2-overlay/vf2-overlay-can.dtso | 23 ++++++
+ .../vf2-overlay/vf2-overlay-uart3-i2c.dtso    | 75 +++++++++++++++++++
+ 4 files changed, 102 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/starfive/vf2-overlay/Makefile
+ create mode 100644 arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso
+ create mode 100644 arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso
+
+--- a/arch/riscv/boot/dts/starfive/Makefile
++++ b/arch/riscv/boot/dts/starfive/Makefile
+@@ -9,6 +9,7 @@ DTC_FLAGS_jh7110-evb := -@
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
++subdir-y += vf2-overlay
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
+ dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb \
+                                               jh7110-starfive-visionfive-2-ac108.dtb  \
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/vf2-overlay/Makefile
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0
++dtb-$(CONFIG_ARCH_STARFIVE) += vf2-overlay-uart3-i2c.dtbo     \
++                             vf2-overlay-can.dtbo
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso
+@@ -0,0 +1,23 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //can0
++      fragment@0 {
++              target-path = "/soc/can@130d0000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      //can1
++      fragment@1 {
++              target-path = "/soc/can@130e0000";
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++};
+--- /dev/null
++++ b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso
+@@ -0,0 +1,75 @@
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/gpio/gpio.h>
++#include "../jh7110-pinfunc.h"
++/ {
++      compatible = "starfive,jh7110";
++
++      //sysgpio
++      fragment@0 {
++              target-path = "/soc/pinctrl@13040000";
++              __overlay__ {
++                      dt_uart3_pins: dt-uart3-0 {
++                              tx-pins {
++                                      pinmux = <GPIOMUX(60, GPOUT_SYS_UART3_TX,
++                                                            GPOEN_ENABLE,
++                                                            GPI_NONE)>;
++                                      bias-disable;
++                                      drive-strength = <12>;
++                                      input-disable;
++                                      input-schmitt-disable;
++                                      slew-rate = <0>;
++                              };
++
++                              rx-pins {
++                                      pinmux = <GPIOMUX(63, GPOUT_LOW,
++                                                            GPOEN_DISABLE,
++                                                            GPI_SYS_UART3_RX)>;
++                                      bias-pull-up;
++                                      drive-strength = <2>;
++                                      input-enable;
++                                      input-schmitt-enable;
++                                      slew-rate = <0>;
++                              };
++                      };
++
++                      dt_i2c1_pins: dt-i2c1-0 {
++                              i2c-pins {
++                                      pinmux = <GPIOMUX(42, GPOUT_LOW,
++                                                            GPOEN_SYS_I2C1_CLK,
++                                                            GPI_SYS_I2C1_CLK)>,
++                                               <GPIOMUX(43, GPOUT_LOW,
++                                                            GPOEN_SYS_I2C1_DATA,
++                                                            GPI_SYS_I2C1_DATA)>;
++                                      bias-pull-up;
++                                      input-enable;
++                                      input-schmitt-enable;
++                              };
++                      };
++              };
++      };
++
++      //uart3
++      fragment@1 {
++              target-path = "/soc/serial@12000000";
++              __overlay__ {
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&dt_uart3_pins>;
++                      status = "okay";
++              };
++      };
++
++      //i2c1
++      fragment@2 {
++              target-path = "/soc/i2c@10040000";
++              __overlay__ {
++                      clock-frequency = <100000>;
++                      i2c-sda-hold-time-ns = <300>;
++                      i2c-sda-falling-time-ns = <510>;
++                      i2c-scl-falling-time-ns = <510>;
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&dt_i2c1_pins>;
++                      status = "okay";
++              };
++      };
++};
diff --git a/target/linux/starfive/patches-6.6/0112-riscv-dts-starfive-visionfive-2-Add-aliases-for-i2c-.patch b/target/linux/starfive/patches-6.6/0112-riscv-dts-starfive-visionfive-2-Add-aliases-for-i2c-.patch
new file mode 100644 (file)
index 0000000..17dcf72
--- /dev/null
@@ -0,0 +1,34 @@
+From 2b098da69f9497e725683caf67fbecf79202b7fc Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Fri, 22 Mar 2024 11:33:40 +0800
+Subject: [PATCH 112/116] riscv: dts: starfive: visionfive 2: Add aliases for
+ i2c* and uart3
+
+Fix the numbers of i2c and uart3.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -15,7 +15,10 @@
+               ethernet0 = &gmac0;
+               ethernet1 = &gmac1;
+               i2c0 = &i2c0;
++              i2c1 = &i2c1;
+               i2c2 = &i2c2;
++              i2c3 = &i2c3;
++              i2c4 = &i2c4;
+               i2c5 = &i2c5;
+               i2c6 = &i2c6;
+               mmc0 = &mmc0;
+@@ -23,6 +26,7 @@
+               pcie0 = &pcie0;
+               pcie1 = &pcie1;
+               serial0 = &uart0;
++              serial3 = &uart3;
+       };
+       chosen {
diff --git a/target/linux/starfive/patches-6.6/0113-driver-bluetooth-add-aic8800-driver-support.patch b/target/linux/starfive/patches-6.6/0113-driver-bluetooth-add-aic8800-driver-support.patch
new file mode 100644 (file)
index 0000000..3651976
--- /dev/null
@@ -0,0 +1,6063 @@
+From 18b70b6f5192ee5363515b57e1f213d0698224ed Mon Sep 17 00:00:00 2001
+From: "ziv.xu" <ziv.xu@starfive.com>
+Date: Tue, 28 Nov 2023 15:37:14 +0800
+Subject: [PATCH 113/116] driver: bluetooth: add aic8800 driver support
+
+add aic8800 driver that can support sco profile
+
+Signed-off-by: ziv.xu <ziv.xu@starfive.com>
+---
+ .../configs/starfive_visionfive2_defconfig    |    3 +-
+ drivers/bluetooth/Kconfig                     |    4 +
+ drivers/bluetooth/Makefile                    |    1 +
+ drivers/bluetooth/aic_btusb/Makefile          |   80 +
+ drivers/bluetooth/aic_btusb/aic_btusb.c       | 5031 +++++++++++++++++
+ drivers/bluetooth/aic_btusb/aic_btusb.h       |  753 +++
+ .../aic_btusb/aic_btusb_external_featrue.c    |  126 +
+ .../aic_btusb/aic_btusb_external_featrue.h    |    3 +
+ 8 files changed, 5999 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/bluetooth/aic_btusb/Makefile
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb.c
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb.h
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c
+ create mode 100644 drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h
+
+--- a/arch/riscv/configs/starfive_visionfive2_defconfig
++++ b/arch/riscv/configs/starfive_visionfive2_defconfig
+@@ -87,8 +87,7 @@ CONFIG_BT_RFCOMM_TTY=y
+ CONFIG_BT_BNEP=y
+ CONFIG_BT_BNEP_MC_FILTER=y
+ CONFIG_BT_BNEP_PROTO_FILTER=y
+-CONFIG_BT_HCIBTUSB=m
+-# CONFIG_BT_HCIBTUSB_BCM is not set
++CONFIG_BT_AICUSB=y
+ CONFIG_CFG80211=y
+ CONFIG_MAC80211=y
+ CONFIG_RFKILL=y
+--- a/drivers/bluetooth/Kconfig
++++ b/drivers/bluetooth/Kconfig
+@@ -478,5 +478,9 @@ config BT_NXPUART
+         Say Y here to compile support for NXP Bluetooth UART device into
+         the kernel, or say M here to compile as a module (btnxpuart).
++config BT_AICUSB
++      tristate "AIC8800 btusb driver"
++      help
++        AIC8800 usb dongle bluetooth support driver
+ endmenu
+--- a/drivers/bluetooth/Makefile
++++ b/drivers/bluetooth/Makefile
+@@ -51,3 +51,4 @@ hci_uart-$(CONFIG_BT_HCIUART_QCA)    += hci
+ hci_uart-$(CONFIG_BT_HCIUART_AG6XX)   += hci_ag6xx.o
+ hci_uart-$(CONFIG_BT_HCIUART_MRVL)    += hci_mrvl.o
+ hci_uart-objs                         := $(hci_uart-y)
++obj-$(CONFIG_BT_AICUSB)     += aic_btusb/
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/Makefile
+@@ -0,0 +1,80 @@
++MODULE_NAME = aic_btusb
++
++CONFIG_AIC8800_BTUSB_SUPPORT = m
++CONFIG_SUPPORT_VENDOR_APCF = n
++# Need to set fw path in BOARD_KERNEL_CMDLINE
++CONFIG_USE_FW_REQUEST = n
++
++ifeq ($(CONFIG_SUPPORT_VENDOR_APCF), y)
++obj-$(CONFIG_AIC8800_BTUSB_SUPPORT) := $(MODULE_NAME).o aic_btusb_external_featrue.o
++else
++obj-$(CONFIG_AIC8800_BTUSB_SUPPORT) := $(MODULE_NAME).o
++endif
++
++ccflags-$(CONFIG_SUPPORT_VENDOR_APCF) += -DCONFIG_SUPPORT_VENDOR_APCF
++#$(MODULE_NAME)-y := aic_btusb_ioctl.o\
++#     aic_btusb.o \
++
++ccflags-$(CONFIG_USE_FW_REQUEST) += -DCONFIG_USE_FW_REQUEST
++
++
++# Platform support list
++CONFIG_PLATFORM_ROCKCHIP ?= n
++CONFIG_PLATFORM_ALLWINNER ?= n
++CONFIG_PLATFORM_AMLOGIC ?= n
++CONFIG_PLATFORM_UBUNTU ?= y
++CONFIG_PLATFORM_ING ?= n
++
++ifeq ($(CONFIG_PLATFORM_ING), y)
++ARCH := mips
++KDIR ?= /home/yaya/E/Ingenic/T31/Ingenic-SDK-T31-1.1.5-20220428/opensource/kernel
++CROSS_COMPILE := /home/yaya/E/Ingenic/T31/Ingenic-SDK-T31-1.1.5-20220428/resource/toolchain/gcc_472/mips-gcc472-glibc216-32bit/bin/mips-linux-gnu-
++endif
++
++ifeq ($(CONFIG_PLATFORM_ROCKCHIP), y)
++ccflags-$(CONFIG_PLATFORM_ROCKCHIP) += -DCONFIG_PLATFORM_ROCKCHIP
++#KDIR  := /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/kernel
++#ARCH ?= arm
++#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
++#KDIR := /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/kernel
++#ARCH ?= arm
++#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
++ARCH := arm64
++KDIR ?= /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/kernel
++CROSS_COMPILE := /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
++
++endif
++
++ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
++ccflags-$(CONFIG_PLATFORM_ALLWINNER) += -DCONFIG_PLATFORM_ALLWINNER
++KDIR  := /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/kernel/linux-4.9
++ARCH ?= arm64
++CROSS_COMPILE ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
++endif
++
++ifeq ($(CONFIG_PLATFORM_AMLOGIC), y)
++        ccflags-$(CONFIG_PLATFORM_AMLOGIC) += -DCONFIG_PLATFORM_AMLOGIC
++endif
++
++ifeq ($(CONFIG_PLATFORM_UBUNTU), y)
++ccflags-$(CONFIG_PLATFORM_UBUNTU) += -DCONFIG_PLATFORM_UBUNTU
++endif
++
++all: modules
++modules:
++      make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
++
++install:
++      mkdir -p $(MODDESTDIR)
++      install -p -m 644 $(MODULE_NAME).ko  $(MODDESTDIR)/
++      /sbin/depmod -a ${KVER}
++      echo $(MODULE_NAME) >> /etc/modules-load.d/aic_bt.conf
++
++uninstall:
++      rm -rfv $(MODDESTDIR)/$(MODULE_NAME).ko
++      /sbin/depmod -a ${KVER}
++      rm -f /etc/modules-load.d/aic_bt.conf
++
++clean:
++      rm -rf *.o *.ko *.o.* *.mod.* modules.* Module.* .a* .o* .*.o.* *.mod .tmp* .cache.mk .Module.symvers.cmd .modules.order.cmd
++
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb.c
+@@ -0,0 +1,5031 @@
++/*
++ *
++ *  AicSemi Bluetooth USB driver
++ *
++ *
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; either version 2 of the License, or
++ *  the Free Software Foundation; either version 2 of the License, or
++ *  (at your option) any later version.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/usb.h>
++
++#include <linux/ioctl.h>
++#include <linux/io.h>
++#include <linux/firmware.h>
++#include <linux/vmalloc.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++#include <linux/reboot.h>
++
++#include "aic_btusb.h"
++
++#ifdef CONFIG_USE_FW_REQUEST
++#include <linux/firmware.h>
++#endif
++
++#define AICBT_RELEASE_NAME "202012_ANDROID"
++#define VERSION "2.1.0"
++
++#define SUSPNED_DW_FW 0
++
++
++static spinlock_t queue_lock;
++static spinlock_t dlfw_lock;
++static volatile uint16_t    dlfw_dis_state = 0;
++
++/* USB Device ID */
++#define USB_VENDOR_ID_AIC                0xA69C
++#define USB_PRODUCT_ID_AIC8801                                0x8801
++#define USB_PRODUCT_ID_AIC8800DC                      0x88dc
++#define USB_PRODUCT_ID_AIC8800D80                     0x8d81
++
++enum AICWF_IC{
++      PRODUCT_ID_AIC8801      =       0,
++      PRODUCT_ID_AIC8800DC,
++      PRODUCT_ID_AIC8800DW,
++      PRODUCT_ID_AIC8800D80
++};
++
++u16 g_chipid = PRODUCT_ID_AIC8801;
++u8 chip_id = 0;
++u8 sub_chip_id = 0;
++
++struct btusb_data {
++    struct hci_dev       *hdev;
++    struct usb_device    *udev;
++    struct usb_interface *intf;
++    struct usb_interface *isoc;
++
++    spinlock_t lock;
++
++    unsigned long flags;
++
++    struct work_struct work;
++    struct work_struct waker;
++
++    struct usb_anchor tx_anchor;
++    struct usb_anchor intr_anchor;
++    struct usb_anchor bulk_anchor;
++    struct usb_anchor isoc_anchor;
++    struct usb_anchor deferred;
++    int tx_in_flight;
++    spinlock_t txlock;
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++              spinlock_t rxlock;
++              struct sk_buff *evt_skb;
++              struct sk_buff *acl_skb;
++              struct sk_buff *sco_skb;
++#endif
++#endif
++
++    struct usb_endpoint_descriptor *intr_ep;
++    struct usb_endpoint_descriptor *bulk_tx_ep;
++    struct usb_endpoint_descriptor *bulk_rx_ep;
++    struct usb_endpoint_descriptor *isoc_tx_ep;
++    struct usb_endpoint_descriptor *isoc_rx_ep;
++
++    __u8 cmdreq_type;
++
++    unsigned int sco_num;
++    int isoc_altsetting;
++    int suspend_count;
++    uint16_t sco_handle;
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++    int (*recv_bulk) (struct btusb_data * data, void *buffer, int count);
++#endif
++#endif
++
++//#ifdef CONFIG_HAS_EARLYSUSPEND
++#if 0
++    struct early_suspend early_suspend;
++#else
++    struct notifier_block pm_notifier;
++    struct notifier_block reboot_notifier;
++#endif
++    firmware_info *fw_info;
++
++#ifdef CONFIG_SCO_OVER_HCI
++    AIC_sco_card_t  *pSCOSnd;
++#endif
++};
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
++static bool reset_on_close = 0;
++#endif
++
++#ifdef CONFIG_SCO_OVER_HCI
++struct snd_sco_cap_timer {
++      struct timer_list cap_timer;
++      struct timer_list play_timer;
++      struct btusb_data snd_usb_data;
++      int snd_sco_length;
++};
++static struct snd_sco_cap_timer snd_cap_timer;
++#endif
++
++
++int bt_support = 0;
++module_param(bt_support, int, 0660);
++
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++int vendor_apcf_sent_done = 0;
++#endif
++
++static inline int check_set_dlfw_state_value(uint16_t change_value)
++{
++    spin_lock(&dlfw_lock);
++    if(!dlfw_dis_state) {
++        dlfw_dis_state = change_value;
++    }
++    spin_unlock(&dlfw_lock);
++    return dlfw_dis_state;
++}
++
++static inline void set_dlfw_state_value(uint16_t change_value)
++{
++    spin_lock(&dlfw_lock);
++    dlfw_dis_state = change_value;
++    spin_unlock(&dlfw_lock);
++}
++
++
++
++
++static void aic_free( struct btusb_data *data)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1)
++    kfree(data);
++#endif
++    return;
++}
++
++static struct btusb_data *aic_alloc(struct usb_interface *intf)
++{
++    struct btusb_data *data;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1)
++    data = kzalloc(sizeof(*data), GFP_KERNEL);
++#else
++    data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
++#endif
++    return data;
++}
++
++static void print_acl(struct sk_buff *skb, int direction)
++{
++#if PRINT_ACL_DATA
++    //uint wlength = skb->len;
++    u16 *handle = (u16 *)(skb->data);
++    u16 len = *(handle+1);
++    //u8 *acl_data = (u8 *)(skb->data);
++
++    AICBT_INFO("aic %s: direction %d, handle %04x, len %d",
++            __func__, direction, *handle, len);
++#endif
++}
++
++static void print_sco(struct sk_buff *skb, int direction)
++{
++#if PRINT_SCO_DATA
++    uint wlength = skb->len;
++    u16 *handle = (u16 *)(skb->data);
++    u8 len = *(u8 *)(handle+1);
++    //u8 *sco_data =(u8 *)(skb->data);
++
++    AICBT_INFO("aic %s: direction %d, handle %04x, len %d,wlength %d",
++            __func__, direction, *handle, len,wlength);
++#endif
++}
++
++static void print_error_command(struct sk_buff *skb)
++{
++    u16 *opcode = (u16*)(skb->data);
++    u8 *cmd_data = (u8*)(skb->data);
++    u8 len = *(cmd_data+2);
++
++      printk(" 0x%04x,len:%d,", *opcode, len);
++#if CONFIG_BLUEDROID
++    switch (*opcode) {
++    case HCI_OP_INQUIRY:
++        printk("HCI_OP_INQUIRY");
++        break;
++    case HCI_OP_INQUIRY_CANCEL:
++        printk("HCI_OP_INQUIRY_CANCEL");
++        break;
++    case HCI_OP_EXIT_PERIODIC_INQ:
++        printk("HCI_OP_EXIT_PERIODIC_INQ");
++        break;
++    case HCI_OP_CREATE_CONN:
++        printk("HCI_OP_CREATE_CONN");
++        break;
++    case HCI_OP_DISCONNECT:
++        printk("HCI_OP_DISCONNECT");
++        break;
++    case HCI_OP_CREATE_CONN_CANCEL:
++        printk("HCI_OP_CREATE_CONN_CANCEL");
++        break;
++    case HCI_OP_ACCEPT_CONN_REQ:
++        printk("HCI_OP_ACCEPT_CONN_REQ");
++        break;
++    case HCI_OP_REJECT_CONN_REQ:
++        printk("HCI_OP_REJECT_CONN_REQ");
++        break;
++    case HCI_OP_AUTH_REQUESTED:
++        printk("HCI_OP_AUTH_REQUESTED");
++        break;
++    case HCI_OP_SET_CONN_ENCRYPT:
++        printk("HCI_OP_SET_CONN_ENCRYPT");
++        break;
++    case HCI_OP_REMOTE_NAME_REQ:
++        printk("HCI_OP_REMOTE_NAME_REQ");
++        break;
++    case HCI_OP_READ_REMOTE_FEATURES:
++        printk("HCI_OP_READ_REMOTE_FEATURES");
++        break;
++    case HCI_OP_SNIFF_MODE:
++        printk("HCI_OP_SNIFF_MODE");
++        break;
++    case HCI_OP_EXIT_SNIFF_MODE:
++        printk("HCI_OP_EXIT_SNIFF_MODE");
++        break;
++    case HCI_OP_SWITCH_ROLE:
++        printk("HCI_OP_SWITCH_ROLE");
++        break;
++    case HCI_OP_SNIFF_SUBRATE:
++        printk("HCI_OP_SNIFF_SUBRATE");
++        break;
++    case HCI_OP_RESET:
++        printk("HCI_OP_RESET");
++        break;
++    case HCI_OP_Write_Extended_Inquiry_Response:
++        printk("HCI_Write_Extended_Inquiry_Response");
++        break;
++      case HCI_OP_Write_Simple_Pairing_Mode:
++              printk("HCI_OP_Write_Simple_Pairing_Mode");
++              break;
++      case HCI_OP_Read_Buffer_Size:
++              printk("HCI_OP_Read_Buffer_Size");
++              break;
++      case HCI_OP_Host_Buffer_Size:
++              printk("HCI_OP_Host_Buffer_Size");
++              break;
++      case HCI_OP_Read_Local_Version_Information:
++              printk("HCI_OP_Read_Local_Version_Information");
++              break;
++      case HCI_OP_Read_BD_ADDR:
++              printk("HCI_OP_Read_BD_ADDR");
++              break;
++      case HCI_OP_Read_Local_Supported_Commands:
++              printk("HCI_OP_Read_Local_Supported_Commands");
++              break;
++      case HCI_OP_Write_Scan_Enable:
++              printk("HCI_OP_Write_Scan_Enable");
++              break;
++      case HCI_OP_Write_Current_IAC_LAP:
++              printk("HCI_OP_Write_Current_IAC_LAP");
++              break;
++      case HCI_OP_Write_Inquiry_Scan_Activity:
++              printk("HCI_OP_Write_Inquiry_Scan_Activity");
++              break;
++      case HCI_OP_Write_Class_of_Device:
++              printk("HCI_OP_Write_Class_of_Device");
++              break;
++      case HCI_OP_LE_Rand:
++              printk("HCI_OP_LE_Rand");
++              break;
++      case HCI_OP_LE_Set_Random_Address:
++              printk("HCI_OP_LE_Set_Random_Address");
++              break;
++      case HCI_OP_LE_Set_Extended_Scan_Enable:
++              printk("HCI_OP_LE_Set_Extended_Scan_Enable");
++              break;
++      case HCI_OP_LE_Set_Extended_Scan_Parameters:
++              printk("HCI_OP_LE_Set_Extended_Scan_Parameters");
++              break;  
++      case HCI_OP_Set_Event_Filter:
++              printk("HCI_OP_Set_Event_Filter");
++              break;
++      case HCI_OP_Write_Voice_Setting:
++              printk("HCI_OP_Write_Voice_Setting");
++              break;
++      case HCI_OP_Change_Local_Name:
++              printk("HCI_OP_Change_Local_Name");
++              break;
++      case HCI_OP_Read_Local_Name:
++              printk("HCI_OP_Read_Local_Name");
++              break;
++      case HCI_OP_Wirte_Page_Timeout:
++              printk("HCI_OP_Wirte_Page_Timeout");
++              break;
++      case HCI_OP_LE_Clear_Resolving_List:
++              printk("HCI_OP_LE_Clear_Resolving_List");
++              break;
++      case HCI_OP_LE_Set_Addres_Resolution_Enable_Command:
++              printk("HCI_OP_LE_Set_Addres_Resolution_Enable_Command");
++              break;
++      case HCI_OP_Write_Inquiry_mode:
++              printk("HCI_OP_Write_Inquiry_mode");
++              break;
++      case HCI_OP_Write_Page_Scan_Type:
++              printk("HCI_OP_Write_Page_Scan_Type");
++              break;
++      case HCI_OP_Write_Inquiry_Scan_Type:
++              printk("HCI_OP_Write_Inquiry_Scan_Type");
++              break;
++      case HCI_OP_Delete_Stored_Link_Key:
++              printk("HCI_OP_Delete_Stored_Link_Key");
++              break;
++      case HCI_OP_LE_Read_Local_Resolvable_Address:
++              printk("HCI_OP_LE_Read_Local_Resolvable_Address");
++              break;
++      case HCI_OP_LE_Extended_Create_Connection:
++              printk("HCI_OP_LE_Extended_Create_Connection");
++              break;
++      case HCI_OP_Read_Remote_Version_Information:
++              printk("HCI_OP_Read_Remote_Version_Information");
++              break;
++      case HCI_OP_LE_Start_Encryption:
++              printk("HCI_OP_LE_Start_Encryption");
++              break;
++      case HCI_OP_LE_Add_Device_to_Resolving_List:
++              printk("HCI_OP_LE_Add_Device_to_Resolving_List");
++              break;
++      case HCI_OP_LE_Set_Privacy_Mode:
++              printk("HCI_OP_LE_Set_Privacy_Mode");
++              break;
++      case HCI_OP_LE_Connection_Update:
++              printk("HCI_OP_LE_Connection_Update");
++              break;
++    default:
++        printk("UNKNOW_HCI_COMMAND");
++        break;
++    }
++#endif //CONFIG_BLUEDROID
++}
++
++static void print_command(struct sk_buff *skb)
++{
++#if PRINT_CMD_EVENT
++    print_error_command(skb);
++#endif
++}
++
++
++enum CODEC_TYPE{
++    CODEC_CVSD,
++    CODEC_MSBC,
++};
++
++static enum CODEC_TYPE codec_type = CODEC_CVSD;
++static void set_select_msbc(enum CODEC_TYPE type);
++static enum CODEC_TYPE check_select_msbc(void);
++
++
++#if CONFIG_BLUEDROID
++
++/* Global parameters for bt usb char driver */
++#define BT_CHAR_DEVICE_NAME "aicbt_dev"
++struct mutex btchr_mutex;
++static struct sk_buff_head btchr_readq;
++static wait_queue_head_t btchr_read_wait;
++static wait_queue_head_t bt_dlfw_wait;
++static int bt_char_dev_registered;
++static dev_t bt_devid; /* bt char device number */
++static struct cdev bt_char_dev; /* bt character device structure */
++static struct class *bt_char_class; /* device class for usb char driver */
++static int bt_reset = 0;
++
++/* HCI device & lock */
++DEFINE_RWLOCK(hci_dev_lock);
++struct hci_dev *ghdev = NULL;
++
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++static int bypass_event(struct sk_buff *skb)
++{
++      int ret = 0;
++      u8 *opcode = (u8*)(skb->data);
++      //u8 len = *(opcode+1);
++      u16 sub_opcpde;
++
++      switch(*opcode) {
++              case HCI_EV_CMD_COMPLETE:
++                      sub_opcpde = ((u16)opcode[3]|(u16)(opcode[4])<<8);
++                      if(sub_opcpde == 0xfd57){
++                              if(vendor_apcf_sent_done){
++                                      vendor_apcf_sent_done--;
++                                      printk("apcf bypass\r\n");
++                                      ret = 1;
++                              }
++                      }
++                      break;
++              default:
++                      break;
++      }
++      return ret;
++}
++#endif//CONFIG_SUPPORT_VENDOR_APCF
++static void print_event(struct sk_buff *skb)
++{
++#if PRINT_CMD_EVENT
++    //uint wlength = skb->len;
++    //uint icount = 0;
++    u8 *opcode = (u8*)(skb->data);
++    //u8 len = *(opcode+1);
++
++    printk("aic %s ", __func__);
++    switch (*opcode) {
++    case HCI_EV_INQUIRY_COMPLETE:
++        printk("HCI_EV_INQUIRY_COMPLETE");
++        break;
++    case HCI_EV_INQUIRY_RESULT:
++        printk("HCI_EV_INQUIRY_RESULT");
++        break;
++    case HCI_EV_CONN_COMPLETE:
++        printk("HCI_EV_CONN_COMPLETE");
++        break;
++    case HCI_EV_CONN_REQUEST:
++        printk("HCI_EV_CONN_REQUEST");
++        break;
++    case HCI_EV_DISCONN_COMPLETE:
++        printk("HCI_EV_DISCONN_COMPLETE");
++        break;
++    case HCI_EV_AUTH_COMPLETE:
++        printk("HCI_EV_AUTH_COMPLETE");
++        break;
++    case HCI_EV_REMOTE_NAME:
++        printk("HCI_EV_REMOTE_NAME");
++        break;
++    case HCI_EV_ENCRYPT_CHANGE:
++        printk("HCI_EV_ENCRYPT_CHANGE");
++        break;
++    case HCI_EV_CHANGE_LINK_KEY_COMPLETE:
++        printk("HCI_EV_CHANGE_LINK_KEY_COMPLETE");
++        break;
++    case HCI_EV_REMOTE_FEATURES:
++        printk("HCI_EV_REMOTE_FEATURES");
++        break;
++    case HCI_EV_REMOTE_VERSION:
++        printk("HCI_EV_REMOTE_VERSION");
++        break;
++    case HCI_EV_QOS_SETUP_COMPLETE:
++        printk("HCI_EV_QOS_SETUP_COMPLETE");
++        break;
++    case HCI_EV_CMD_COMPLETE:
++        printk("HCI_EV_CMD_COMPLETE");
++        break;
++    case HCI_EV_CMD_STATUS:
++        printk("HCI_EV_CMD_STATUS");
++        break;
++    case HCI_EV_ROLE_CHANGE:
++        printk("HCI_EV_ROLE_CHANGE");
++        break;
++    case HCI_EV_NUM_COMP_PKTS:
++        printk("HCI_EV_NUM_COMP_PKTS");
++        break;
++    case HCI_EV_MODE_CHANGE:
++        printk("HCI_EV_MODE_CHANGE");
++        break;
++    case HCI_EV_PIN_CODE_REQ:
++        printk("HCI_EV_PIN_CODE_REQ");
++        break;
++    case HCI_EV_LINK_KEY_REQ:
++        printk("HCI_EV_LINK_KEY_REQ");
++        break;
++    case HCI_EV_LINK_KEY_NOTIFY:
++        printk("HCI_EV_LINK_KEY_NOTIFY");
++        break;
++    case HCI_EV_CLOCK_OFFSET:
++        printk("HCI_EV_CLOCK_OFFSET");
++        break;
++    case HCI_EV_PKT_TYPE_CHANGE:
++        printk("HCI_EV_PKT_TYPE_CHANGE");
++        break;
++    case HCI_EV_PSCAN_REP_MODE:
++        printk("HCI_EV_PSCAN_REP_MODE");
++        break;
++    case HCI_EV_INQUIRY_RESULT_WITH_RSSI:
++        printk("HCI_EV_INQUIRY_RESULT_WITH_RSSI");
++        break;
++    case HCI_EV_REMOTE_EXT_FEATURES:
++        printk("HCI_EV_REMOTE_EXT_FEATURES");
++        break;
++    case HCI_EV_SYNC_CONN_COMPLETE:
++        printk("HCI_EV_SYNC_CONN_COMPLETE");
++        break;
++    case HCI_EV_SYNC_CONN_CHANGED:
++        printk("HCI_EV_SYNC_CONN_CHANGED");
++        break;
++    case HCI_EV_SNIFF_SUBRATE:
++        printk("HCI_EV_SNIFF_SUBRATE");
++        break;
++    case HCI_EV_EXTENDED_INQUIRY_RESULT:
++        printk("HCI_EV_EXTENDED_INQUIRY_RESULT");
++        break;
++    case HCI_EV_IO_CAPA_REQUEST:
++        printk("HCI_EV_IO_CAPA_REQUEST");
++        break;
++    case HCI_EV_SIMPLE_PAIR_COMPLETE:
++        printk("HCI_EV_SIMPLE_PAIR_COMPLETE");
++        break;
++    case HCI_EV_REMOTE_HOST_FEATURES:
++        printk("HCI_EV_REMOTE_HOST_FEATURES");
++        break;
++    default:
++        printk("unknow event");
++        break;
++    }
++      printk("\n");
++#if 0
++    printk("%02x,len:%d,", *opcode,len);
++    for (icount = 2; (icount < wlength) && (icount < 24); icount++)
++        printk("%02x ", *(opcode+icount));
++    printk("\n");
++#endif
++#endif
++}
++
++static inline ssize_t usb_put_user(struct sk_buff *skb,
++        char __user *buf, int count)
++{
++    char __user *ptr = buf;
++    int len = min_t(unsigned int, skb->len, count);
++
++    if (copy_to_user(ptr, skb->data, len))
++        return -EFAULT;
++
++    return len;
++}
++
++static struct sk_buff *aic_skb_queue[QUEUE_SIZE];
++static int aic_skb_queue_front = 0;
++static int aic_skb_queue_rear = 0;
++
++static void aic_enqueue(struct sk_buff *skb)
++{
++    spin_lock(&queue_lock);
++    if (aic_skb_queue_front == (aic_skb_queue_rear + 1) % QUEUE_SIZE) {
++        /*
++         * If queue is full, current solution is to drop
++         * the following entries.
++         */
++        AICBT_WARN("%s: Queue is full, entry will be dropped", __func__);
++    } else {
++        aic_skb_queue[aic_skb_queue_rear] = skb;
++
++        aic_skb_queue_rear++;
++        aic_skb_queue_rear %= QUEUE_SIZE;
++
++    }
++    spin_unlock(&queue_lock);
++}
++
++static struct sk_buff *aic_dequeue_try(unsigned int deq_len)
++{
++    struct sk_buff *skb;
++    struct sk_buff *skb_copy;
++
++    if (aic_skb_queue_front == aic_skb_queue_rear) {
++        AICBT_WARN("%s: Queue is empty", __func__);
++        return NULL;
++    }
++
++    skb = aic_skb_queue[aic_skb_queue_front];
++    if (deq_len >= skb->len) {
++
++        aic_skb_queue_front++;
++        aic_skb_queue_front %= QUEUE_SIZE;
++
++        /*
++         * Return skb addr to be dequeued, and the caller
++         * should free the skb eventually.
++         */
++        return skb;
++    } else {
++        skb_copy = pskb_copy(skb, GFP_ATOMIC);
++        skb_pull(skb, deq_len);
++        /* Return its copy to be freed */
++        return skb_copy;
++    }
++}
++
++static inline int is_queue_empty(void)
++{
++    return (aic_skb_queue_front == aic_skb_queue_rear) ? 1 : 0;
++}
++
++static void aic_clear_queue(void)
++{
++    struct sk_buff *skb;
++    spin_lock(&queue_lock);
++    while(!is_queue_empty()) {
++        skb = aic_skb_queue[aic_skb_queue_front];
++        aic_skb_queue[aic_skb_queue_front] = NULL;
++        aic_skb_queue_front++;
++        aic_skb_queue_front %= QUEUE_SIZE;
++        if (skb) {
++            kfree_skb(skb);
++        }
++    }
++    spin_unlock(&queue_lock);
++}
++
++/*
++ * AicSemi - Integrate from hci_core.c
++ */
++
++/* Get HCI device by index.
++ * Device is held on return. */
++static struct hci_dev *hci_dev_get(int index)
++{
++    if (index != 0)
++        return NULL;
++
++    return ghdev;
++}
++
++/* ---- HCI ioctl helpers ---- */
++static int hci_dev_open(__u16 dev)
++{
++    struct hci_dev *hdev;
++    int ret = 0;
++
++    AICBT_DBG("%s: dev %d", __func__, dev);
++
++    hdev = hci_dev_get(dev);
++    if (!hdev) {
++        AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
++        return -ENODEV;
++    }
++
++    if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) {
++        ret = -ENODEV;
++        goto done;
++    }
++
++    if (test_bit(HCI_UP, &hdev->flags)) {
++        ret = -EALREADY;
++        goto done;
++    }
++
++done:
++    return ret;
++}
++
++static int hci_dev_do_close(struct hci_dev *hdev)
++{
++    if (hdev->flush)
++        hdev->flush(hdev);
++    /* After this point our queues are empty
++     * and no tasks are scheduled. */
++    hdev->close(hdev);
++    /* Clear flags */
++    hdev->flags = 0;
++    return 0;
++}
++
++static int hci_dev_close(__u16 dev)
++{
++    struct hci_dev *hdev;
++    int err;
++    hdev = hci_dev_get(dev);
++    if (!hdev) {
++        AICBT_ERR("%s: failed to get hci dev[Null]", __func__);
++        return -ENODEV;
++    }
++
++    err = hci_dev_do_close(hdev);
++
++    return err;
++}
++
++#ifdef CONFIG_SCO_OVER_HCI
++/* copy data from the URB buffer into the ALSA ring buffer */
++static bool aic_copy_capture_data_to_alsa(struct btusb_data *data, uint8_t* p_data, unsigned int frames)
++{
++      struct snd_pcm_runtime *runtime;
++      unsigned int frame_bytes, frames1;
++      u8 *dest;
++    AIC_sco_card_t  *pSCOSnd = data->pSCOSnd;
++
++    runtime = pSCOSnd->capture.substream->runtime;
++    frame_bytes = 2;
++
++        dest = runtime->dma_area + pSCOSnd->capture.buffer_pos * frame_bytes;
++    if (pSCOSnd->capture.buffer_pos + frames <= runtime->buffer_size) {
++              memcpy(dest, p_data, frames * frame_bytes);
++      } else {
++              /* wrap around at end of ring buffer */
++        frames1 = runtime->buffer_size - pSCOSnd->capture.buffer_pos;
++              memcpy(dest, p_data, frames1 * frame_bytes);
++              memcpy(runtime->dma_area,
++                     p_data + frames1 * frame_bytes,
++                     (frames - frames1) * frame_bytes);
++      }
++
++    pSCOSnd->capture.buffer_pos += frames;
++    if (pSCOSnd->capture.buffer_pos >= runtime->buffer_size) {
++        pSCOSnd->capture.buffer_pos -= runtime->buffer_size;
++    }
++
++    if((pSCOSnd->capture.buffer_pos%runtime->period_size) == 0) {
++        snd_pcm_period_elapsed(pSCOSnd->capture.substream);
++    }
++
++    return false;
++}
++
++
++static void hci_send_to_alsa_ringbuffer(struct hci_dev *hdev, struct sk_buff *skb)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    AIC_sco_card_t  *pSCOSnd = data->pSCOSnd;
++    uint8_t* p_data;
++    int sco_length = skb->len - HCI_SCO_HDR_SIZE;
++    u16 *handle = (u16 *) (skb->data);
++    //u8 errflg = (u8)((*handle & 0x3000) >> 12);
++
++    pSCOSnd->usb_data->sco_handle = (*handle & 0x0fff);
++
++    AICBT_DBG("%s, %x, %x %x\n", __func__,pSCOSnd->usb_data->sco_handle, *handle, errflg);
++
++    if (!hdev) {
++        AICBT_INFO("%s: Frame for unknown HCI device", __func__);
++        return;
++    }
++
++    if (!test_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states)) {
++        AICBT_INFO("%s: ALSA is not running", __func__);
++        return;
++    }
++      snd_cap_timer.snd_sco_length = sco_length;
++    p_data = (uint8_t *)skb->data + HCI_SCO_HDR_SIZE;
++    aic_copy_capture_data_to_alsa(data, p_data, sco_length/2);
++}
++
++#endif
++
++#if CONFIG_BLUEDROID
++static struct hci_dev *hci_alloc_dev(void)
++{
++    struct hci_dev *hdev;
++
++    hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL);
++    if (!hdev)
++        return NULL;
++
++    return hdev;
++}
++
++/* Free HCI device */
++static void hci_free_dev(struct hci_dev *hdev)
++{
++    kfree(hdev);
++}
++
++/* Register HCI device */
++static int hci_register_dev(struct hci_dev *hdev)
++{
++    int i, id;
++
++    AICBT_DBG("%s: %p name %s bus %d", __func__, hdev, hdev->name, hdev->bus);
++    /* Do not allow HCI_AMP devices to register at index 0,
++     * so the index can be used as the AMP controller ID.
++     */
++    id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
++
++    write_lock(&hci_dev_lock);
++
++    sprintf(hdev->name, "hci%d", id);
++    hdev->id = id;
++    hdev->flags = 0;
++    hdev->dev_flags = 0;
++    mutex_init(&hdev->lock);
++
++    AICBT_DBG("%s: id %d, name %s", __func__, hdev->id, hdev->name);
++
++
++    for (i = 0; i < NUM_REASSEMBLY; i++)
++        hdev->reassembly[i] = NULL;
++
++    memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
++    atomic_set(&hdev->promisc, 0);
++
++    if (ghdev) {
++        write_unlock(&hci_dev_lock);
++        AICBT_ERR("%s: Hci device has been registered already", __func__);
++        return -1;
++    } else
++        ghdev = hdev;
++
++    write_unlock(&hci_dev_lock);
++
++    return id;
++}
++
++/* Unregister HCI device */
++static void hci_unregister_dev(struct hci_dev *hdev)
++{
++    int i;
++
++    AICBT_DBG("%s: hdev %p name %s bus %d", __func__, hdev, hdev->name, hdev->bus);
++    set_bit(HCI_UNREGISTER, &hdev->dev_flags);
++
++    write_lock(&hci_dev_lock);
++    ghdev = NULL;
++    write_unlock(&hci_dev_lock);
++
++    hci_dev_do_close(hdev);
++    for (i = 0; i < NUM_REASSEMBLY; i++)
++        kfree_skb(hdev->reassembly[i]);
++}
++
++static void hci_send_to_stack(struct hci_dev *hdev, struct sk_buff *skb)
++{
++    struct sk_buff *aic_skb_copy = NULL;
++
++    //AICBT_DBG("%s", __func__);
++
++    if (!hdev) {
++        AICBT_ERR("%s: Frame for unknown HCI device", __func__);
++        return;
++    }
++
++    if (!test_bit(HCI_RUNNING, &hdev->flags)) {
++        AICBT_ERR("%s: HCI not running", __func__);
++        return;
++    }
++
++    aic_skb_copy = pskb_copy(skb, GFP_ATOMIC);
++    if (!aic_skb_copy) {
++        AICBT_ERR("%s: Copy skb error", __func__);
++        return;
++    }
++
++    memcpy(skb_push(aic_skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
++    aic_enqueue(aic_skb_copy);
++
++    /* Make sure bt char device existing before wakeup read queue */
++    hdev = hci_dev_get(0);
++    if (hdev) {
++        //AICBT_DBG("%s: Try to wakeup read queue", __func__);
++        AICBT_DBG("%s", __func__);
++        wake_up_interruptible(&btchr_read_wait);
++    }
++
++      
++    return;
++}
++
++/* Receive frame from HCI drivers */
++static int hci_recv_frame(struct sk_buff *skb)
++{
++    struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++    if (!hdev ||
++        (!test_bit(HCI_UP, &hdev->flags) && !test_bit(HCI_INIT, &hdev->flags))) {
++        kfree_skb(skb);
++        return -ENXIO;
++    }
++
++    /* Incomming skb */
++    bt_cb(skb)->incoming = 1;
++
++    /* Time stamp */
++    __net_timestamp(skb);
++
++    if (atomic_read(&hdev->promisc)) {
++#ifdef CONFIG_SCO_OVER_HCI
++        if(bt_cb(skb)->pkt_type == HCI_SCODATA_PKT){
++            hci_send_to_alsa_ringbuffer(hdev, skb);
++        }else{
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++              if(bt_cb(skb)->pkt_type == HCI_EVENT_PKT){
++                              if(bypass_event(skb)){
++                                      kfree_skb(skb);
++                                      return 0;
++                              }
++                      }
++#endif //CONFIG_SUPPORT_VENDOR_APCF
++                      hci_send_to_stack(hdev, skb);
++              }
++#else
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++              if(bt_cb(skb)->pkt_type == HCI_EVENT_PKT){
++                      if(bypass_event(skb)){
++                              kfree_skb(skb);
++                              return 0;
++                      }
++              }
++#endif //CONFIG_SUPPORT_VENDOR_APCF
++              /* Send copy to the sockets */
++              hci_send_to_stack(hdev, skb);
++#endif
++
++    }
++
++    kfree_skb(skb);
++    return 0;
++}
++
++
++
++static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
++                          int count, __u8 index)
++{
++    int len = 0;
++    int hlen = 0;
++    int remain = count;
++    struct sk_buff *skb;
++    struct bt_skb_cb *scb;
++
++    //AICBT_DBG("%s", __func__);
++
++    if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) ||
++            index >= NUM_REASSEMBLY)
++        return -EILSEQ;
++
++    skb = hdev->reassembly[index];
++
++    if (!skb) {
++        switch (type) {
++        case HCI_ACLDATA_PKT:
++            len = HCI_MAX_FRAME_SIZE;
++            hlen = HCI_ACL_HDR_SIZE;
++            break;
++        case HCI_EVENT_PKT:
++            len = HCI_MAX_EVENT_SIZE;
++            hlen = HCI_EVENT_HDR_SIZE;
++            break;
++        case HCI_SCODATA_PKT:
++            len = HCI_MAX_SCO_SIZE;
++            hlen = HCI_SCO_HDR_SIZE;
++            break;
++        }
++
++        skb = bt_skb_alloc(len, GFP_ATOMIC);
++        if (!skb)
++            return -ENOMEM;
++
++        scb = (void *) skb->cb;
++        scb->expect = hlen;
++        scb->pkt_type = type;
++
++        skb->dev = (void *) hdev;
++        hdev->reassembly[index] = skb;
++    }
++
++    while (count) {
++        scb = (void *) skb->cb;
++        len = min_t(uint, scb->expect, count);
++
++        memcpy(skb_put(skb, len), data, len);
++
++        count -= len;
++        data += len;
++        scb->expect -= len;
++        remain = count;
++
++        switch (type) {
++        case HCI_EVENT_PKT:
++            if (skb->len == HCI_EVENT_HDR_SIZE) {
++                struct hci_event_hdr *h = hci_event_hdr(skb);
++                scb->expect = h->plen;
++
++                if (skb_tailroom(skb) < scb->expect) {
++                    kfree_skb(skb);
++                    hdev->reassembly[index] = NULL;
++                    return -ENOMEM;
++                }
++            }
++            break;
++
++        case HCI_ACLDATA_PKT:
++            if (skb->len  == HCI_ACL_HDR_SIZE) {
++                struct hci_acl_hdr *h = hci_acl_hdr(skb);
++                scb->expect = __le16_to_cpu(h->dlen);
++
++                if (skb_tailroom(skb) < scb->expect) {
++                    kfree_skb(skb);
++                    hdev->reassembly[index] = NULL;
++                    return -ENOMEM;
++                }
++            }
++            break;
++
++        case HCI_SCODATA_PKT:
++            if (skb->len == HCI_SCO_HDR_SIZE) {
++                struct hci_sco_hdr *h = hci_sco_hdr(skb);
++                scb->expect = h->dlen;
++
++                if (skb_tailroom(skb) < scb->expect) {
++                    kfree_skb(skb);
++                    hdev->reassembly[index] = NULL;
++                    return -ENOMEM;
++                }
++            }
++            break;
++        }
++
++        if (scb->expect == 0) {
++            /* Complete frame */
++            if(HCI_ACLDATA_PKT == type)
++                print_acl(skb,0);
++            if(HCI_SCODATA_PKT == type)
++                print_sco(skb,0);
++            if(HCI_EVENT_PKT == type)
++                print_event(skb);
++
++            bt_cb(skb)->pkt_type = type;
++            hci_recv_frame(skb);
++
++            hdev->reassembly[index] = NULL;
++            return remain;
++        }
++    }
++
++    return remain;
++}
++
++static int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
++{
++    int rem = 0;
++
++    if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT)
++        return -EILSEQ;
++
++    while (count) {
++        rem = hci_reassembly(hdev, type, data, count, type - 1);
++        if (rem < 0)
++            return rem;
++
++        data += (count - rem);
++        count = rem;
++    }
++
++    return rem;
++}
++#endif //CONFIG_BLUEDROID
++
++void hci_hardware_error(void)
++{
++    struct sk_buff *aic_skb_copy = NULL;
++    int len = 3;
++    uint8_t hardware_err_pkt[4] = {HCI_EVENT_PKT, 0x10, 0x01, HCI_VENDOR_USB_DISC_HARDWARE_ERROR};
++
++    aic_skb_copy = alloc_skb(len, GFP_ATOMIC);
++    if (!aic_skb_copy) {
++        AICBT_ERR("%s: Failed to allocate mem", __func__);
++        return;
++    }
++
++    memcpy(skb_put(aic_skb_copy, len), hardware_err_pkt, len);
++    aic_enqueue(aic_skb_copy);
++
++    wake_up_interruptible(&btchr_read_wait);
++}
++
++static int btchr_open(struct inode *inode_p, struct file  *file_p)
++{
++    struct btusb_data *data;
++    struct hci_dev *hdev;
++
++    AICBT_DBG("%s: BT usb char device is opening", __func__);
++    /* Not open unless wanna tracing log */
++    /* trace_printk("%s: open....\n", __func__); */
++
++    hdev = hci_dev_get(0);
++    if (!hdev) {
++        AICBT_DBG("%s: Failed to get hci dev[NULL]", __func__);
++        return -ENODEV;
++    }
++    data = GET_DRV_DATA(hdev);
++
++    atomic_inc(&hdev->promisc);
++    /*
++     * As bt device is not re-opened when hotplugged out, we cannot
++     * trust on file's private data(may be null) when other file ops
++     * are invoked.
++     */
++    file_p->private_data = data;
++
++    mutex_lock(&btchr_mutex);
++    hci_dev_open(0);
++    mutex_unlock(&btchr_mutex);
++
++    aic_clear_queue();
++    return nonseekable_open(inode_p, file_p);
++}
++
++static int btchr_close(struct inode  *inode_p, struct file   *file_p)
++{
++    struct btusb_data *data;
++    struct hci_dev *hdev;
++
++    AICBT_INFO("%s: BT usb char device is closing", __func__);
++    /* Not open unless wanna tracing log */
++    /* trace_printk("%s: close....\n", __func__); */
++
++    data = file_p->private_data;
++    file_p->private_data = NULL;
++
++#if CONFIG_BLUEDROID
++    /*
++     * If the upper layer closes bt char interfaces, no reset
++     * action required even bt device hotplugged out.
++     */
++    bt_reset = 0;
++#endif
++
++    hdev = hci_dev_get(0);
++    if (hdev) {
++        atomic_set(&hdev->promisc, 0);
++        mutex_lock(&btchr_mutex);
++        hci_dev_close(0);
++        mutex_unlock(&btchr_mutex);
++    }
++
++    return 0;
++}
++
++static ssize_t btchr_read(struct file *file_p,
++        char __user *buf_p,
++        size_t count,
++        loff_t *pos_p)
++{
++    struct hci_dev *hdev;
++    struct sk_buff *skb;
++    ssize_t ret = 0;
++
++    while (count) {
++        hdev = hci_dev_get(0);
++        if (!hdev) {
++            /*
++             * Note: Only when BT device hotplugged out, we wil get
++             * into such situation. In order to keep the upper layer
++             * stack alive (blocking the read), we should never return
++             * EFAULT or break the loop.
++             */
++            AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
++        }
++
++        ret = wait_event_interruptible(btchr_read_wait, !is_queue_empty());
++        if (ret < 0) {
++            AICBT_ERR("%s: wait event is signaled %d", __func__, (int)ret);
++            break;
++        }
++
++        skb = aic_dequeue_try(count);
++        if (skb) {
++            ret = usb_put_user(skb, buf_p, count);
++            if (ret < 0)
++                AICBT_ERR("%s: Failed to put data to user space", __func__);
++            kfree_skb(skb);
++            break;
++        }
++    }
++
++    return ret;
++}
++
++#ifdef CONFIG_SUPPORT_VENDOR_APCF
++void btchr_external_write(char* buff, int len){
++      struct hci_dev *hdev;
++      struct sk_buff *skb;
++      int i;
++      struct btusb_data *data;
++
++      AICBT_INFO("%s \r\n", __func__);
++      for(i=0;i<len;i++){
++              printk("0x%x ",(u8)buff[i]);
++      }
++      printk("\r\n");
++      hdev = hci_dev_get(0);
++      if (!hdev) {
++              AICBT_WARN("%s: Failed to get hci dev[Null]", __func__);
++              return;
++      }
++    /* Never trust on btusb_data, as bt device may be hotplugged out */
++    data = GET_DRV_DATA(hdev);
++    if (!data) {
++        AICBT_WARN("%s: Failed to get bt usb driver data[Null]", __func__);
++        return;
++    }
++    vendor_apcf_sent_done++;
++
++      skb = bt_skb_alloc(len, GFP_ATOMIC);
++    if (!skb)
++        return;
++    skb_reserve(skb, -1); // Add this line
++    skb->dev = (void *)hdev;
++      memcpy((__u8 *)skb->data,(__u8 *)buff,len);
++      skb_put(skb, len);
++    bt_cb(skb)->pkt_type = *((__u8 *)skb->data);
++    skb_pull(skb, 1);
++    data->hdev->send(skb);
++}
++
++EXPORT_SYMBOL(btchr_external_write);
++#endif //CONFIG_SUPPORT_VENDOR_APCF
++
++static ssize_t btchr_write(struct file *file_p,
++        const char __user *buf_p,
++        size_t count,
++        loff_t *pos_p)
++{
++    struct btusb_data *data = file_p->private_data;
++    struct hci_dev *hdev;
++    struct sk_buff *skb;
++
++    //AICBT_DBG("%s: BT usb char device is writing", __func__);
++    AICBT_DBG("%s", __func__);
++
++    hdev = hci_dev_get(0);
++    if (!hdev) {
++        AICBT_WARN("%s: Failed to get hci dev[Null]", __func__);
++        /*
++         * Note: we bypass the data from the upper layer if bt device
++         * is hotplugged out. Fortunatelly, H4 or H5 HCI stack does
++         * NOT check btchr_write's return value. However, returning
++         * count instead of EFAULT is preferable.
++         */
++        /* return -EFAULT; */
++        return count;
++    }
++
++    /* Never trust on btusb_data, as bt device may be hotplugged out */
++    data = GET_DRV_DATA(hdev);
++    if (!data) {
++        AICBT_WARN("%s: Failed to get bt usb driver data[Null]", __func__);
++        return count;
++    }
++
++    if (count > HCI_MAX_FRAME_SIZE)
++        return -EINVAL;
++
++    skb = bt_skb_alloc(count, GFP_ATOMIC);
++    if (!skb)
++        return -ENOMEM;
++    skb_reserve(skb, -1); // Add this line
++
++    if (copy_from_user(skb_put(skb, count), buf_p, count)) {
++        AICBT_ERR("%s: Failed to get data from user space", __func__);
++        kfree_skb(skb);
++        return -EFAULT;
++    }
++
++    skb->dev = (void *)hdev;
++    bt_cb(skb)->pkt_type = *((__u8 *)skb->data);
++    skb_pull(skb, 1);
++    data->hdev->send(skb);
++
++    return count;
++}
++
++static unsigned int btchr_poll(struct file *file_p, poll_table *wait)
++{
++    struct btusb_data *data = file_p->private_data;
++    struct hci_dev *hdev;
++
++    //AICBT_DBG("%s: BT usb char device is polling", __func__);
++
++    if(!bt_char_dev_registered) {
++        AICBT_ERR("%s: char device has not registered!", __func__);
++        return POLLERR | POLLHUP;
++    }
++
++    poll_wait(file_p, &btchr_read_wait, wait);
++
++    hdev = hci_dev_get(0);
++    if (!hdev) {
++        AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
++        mdelay(URB_CANCELING_DELAY_MS);
++        return POLLERR | POLLHUP;
++        return POLLOUT | POLLWRNORM;
++    }
++
++    /* Never trust on btusb_data, as bt device may be hotplugged out */
++    data = GET_DRV_DATA(hdev);
++    if (!data) {
++        /*
++         * When bt device is hotplugged out, btusb_data will
++         * be freed in disconnect.
++         */
++        AICBT_ERR("%s: Failed to get bt usb driver data[Null]", __func__);
++        mdelay(URB_CANCELING_DELAY_MS);
++        return POLLOUT | POLLWRNORM;
++    }
++
++    if (!is_queue_empty())
++        return POLLIN | POLLRDNORM;
++
++    return POLLOUT | POLLWRNORM;
++}
++static long btchr_ioctl(struct file *file_p,unsigned int cmd, unsigned long arg)
++{
++    int ret = 0;
++    struct hci_dev *hdev;
++    struct btusb_data *data;
++    firmware_info *fw_info;
++
++    if(!bt_char_dev_registered) {
++        return -ENODEV;
++    }
++
++    if(check_set_dlfw_state_value(1) != 1) {
++        AICBT_ERR("%s bt controller is disconnecting!", __func__);
++        return 0;
++    }
++
++    hdev = hci_dev_get(0);
++    if(!hdev) {
++        AICBT_ERR("%s device is NULL!", __func__);
++        set_dlfw_state_value(0);
++        return 0;
++    }
++    data = GET_DRV_DATA(hdev);
++    fw_info = data->fw_info;
++
++    AICBT_INFO(" btchr_ioctl DOWN_FW_CFG with Cmd:%d",cmd);
++    switch (cmd) {
++        case DOWN_FW_CFG:
++            AICBT_INFO(" btchr_ioctl DOWN_FW_CFG");
++            ret = usb_autopm_get_interface(data->intf);
++            if (ret < 0){
++                goto failed;
++            }
++
++            //ret = download_patch(fw_info,1);
++            usb_autopm_put_interface(data->intf);
++            if(ret < 0){
++                AICBT_ERR("%s:Failed in download_patch with ret:%d",__func__,ret);
++                goto failed;
++            }
++
++            ret = hdev->open(hdev);
++            if(ret < 0){
++                AICBT_ERR("%s:Failed in hdev->open(hdev):%d",__func__,ret);
++                goto failed;
++            }
++            set_bit(HCI_UP, &hdev->flags);
++            set_dlfw_state_value(0);
++            wake_up_interruptible(&bt_dlfw_wait);
++            return 1;
++        case DWFW_CMPLT:
++            AICBT_INFO(" btchr_ioctl DWFW_CMPLT");
++#if 1
++      case SET_ISO_CFG:
++            AICBT_INFO("btchr_ioctl SET_ISO_CFG");
++              if(copy_from_user(&(hdev->voice_setting), (__u16*)arg, sizeof(__u16))){
++                      AICBT_INFO(" voice settings err");
++              }
++              //hdev->voice_setting = *(uint16_t*)arg;
++              AICBT_INFO(" voice settings = %d", hdev->voice_setting);
++              //return 1;
++#endif
++        case GET_USB_INFO:
++                      //ret = download_patch(fw_info,1);
++            AICBT_INFO(" btchr_ioctl GET_USB_INFO");
++            ret = hdev->open(hdev);
++            if(ret < 0){
++                AICBT_ERR("%s:Failed in hdev->open(hdev):%d",__func__,ret);
++                //goto done;
++            }
++            set_bit(HCI_UP, &hdev->flags);
++            set_dlfw_state_value(0);
++            wake_up_interruptible(&bt_dlfw_wait);
++            return 1;
++        case RESET_CONTROLLER:
++            AICBT_INFO(" btchr_ioctl RESET_CONTROLLER");
++            //reset_controller(fw_info);
++            return 1;
++        default:
++            AICBT_ERR("%s:Failed with wrong Cmd:%d",__func__,cmd);
++            goto failed;
++        }
++    failed:
++        set_dlfw_state_value(0);
++        wake_up_interruptible(&bt_dlfw_wait);
++        return ret;
++
++}
++
++#ifdef CONFIG_PLATFORM_UBUNTU//AIDEN
++typedef u32           compat_uptr_t;
++static inline void __user *compat_ptr(compat_uptr_t uptr)
++{
++      return (void __user *)(unsigned long)uptr;
++}
++#endif
++
++#ifdef CONFIG_COMPAT
++static long compat_btchr_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
++{
++    AICBT_DBG("%s: enter",__func__);
++    return btchr_ioctl(filp, cmd, (unsigned long) compat_ptr(arg));
++}
++#endif
++static struct file_operations bt_chrdev_ops  = {
++    open    :    btchr_open,
++    release    :    btchr_close,
++    read    :    btchr_read,
++    write    :    btchr_write,
++    poll    :    btchr_poll,
++    unlocked_ioctl   :   btchr_ioctl,
++#ifdef CONFIG_COMPAT
++    compat_ioctl :  compat_btchr_ioctl,
++#endif
++};
++
++static int btchr_init(void)
++{
++    int res = 0;
++    struct device *dev;
++
++    AICBT_INFO("Register usb char device interface for BT driver");
++    /*
++     * btchr mutex is used to sync between
++     * 1) downloading patch and opening bt char driver
++     * 2) the file operations of bt char driver
++     */
++    mutex_init(&btchr_mutex);
++
++    skb_queue_head_init(&btchr_readq);
++    init_waitqueue_head(&btchr_read_wait);
++    init_waitqueue_head(&bt_dlfw_wait);
++
++    bt_char_class = class_create(THIS_MODULE, BT_CHAR_DEVICE_NAME);
++    if (IS_ERR(bt_char_class)) {
++        AICBT_ERR("Failed to create bt char class");
++        return PTR_ERR(bt_char_class);
++    }
++
++    res = alloc_chrdev_region(&bt_devid, 0, 1, BT_CHAR_DEVICE_NAME);
++    if (res < 0) {
++        AICBT_ERR("Failed to allocate bt char device");
++        goto err_alloc;
++    }
++
++    dev = device_create(bt_char_class, NULL, bt_devid, NULL, BT_CHAR_DEVICE_NAME);
++    if (IS_ERR(dev)) {
++        AICBT_ERR("Failed to create bt char device");
++        res = PTR_ERR(dev);
++        goto err_create;
++    }
++
++    cdev_init(&bt_char_dev, &bt_chrdev_ops);
++    res = cdev_add(&bt_char_dev, bt_devid, 1);
++    if (res < 0) {
++        AICBT_ERR("Failed to add bt char device");
++        goto err_add;
++    }
++
++    return 0;
++
++err_add:
++    device_destroy(bt_char_class, bt_devid);
++err_create:
++    unregister_chrdev_region(bt_devid, 1);
++err_alloc:
++    class_destroy(bt_char_class);
++    return res;
++}
++
++static void btchr_exit(void)
++{
++    AICBT_INFO("Unregister usb char device interface for BT driver");
++
++    device_destroy(bt_char_class, bt_devid);
++    cdev_del(&bt_char_dev);
++    unregister_chrdev_region(bt_devid, 1);
++    class_destroy(bt_char_class);
++
++    return;
++}
++#endif
++
++int send_hci_cmd(firmware_info *fw_info)
++{
++
++    int len = 0;
++    int ret_val = -1;
++      int i = 0;
++
++      if(g_chipid == PRODUCT_ID_AIC8801 || g_chipid == PRODUCT_ID_AIC8800D80){
++          ret_val = usb_bulk_msg(fw_info->udev, fw_info->pipe_out, fw_info->send_pkt, fw_info->pkt_len,
++                  &len, 3000);
++          if (ret_val || (len != fw_info->pkt_len)) {
++              AICBT_INFO("Error in send hci cmd = %d,"
++                  "len = %d, size = %d", ret_val, len, fw_info->pkt_len);
++          }
++      }else if(g_chipid == PRODUCT_ID_AIC8800DC){
++              while((ret_val<0)&&(i++<3))
++              {
++                      ret_val = usb_control_msg(
++                         fw_info->udev, fw_info->pipe_out,
++                         0, USB_TYPE_CLASS, 0, 0,
++                         (void *)(fw_info->send_pkt),
++                         fw_info->pkt_len, MSG_TO);
++              }
++
++      }
++    return ret_val;
++
++}
++
++int rcv_hci_evt(firmware_info *fw_info)
++{
++    int ret_len = 0, ret_val = 0;
++    int i;
++
++    while (1) {
++        for(i = 0; i < 5; i++) {
++        ret_val = usb_interrupt_msg(
++            fw_info->udev, fw_info->pipe_in,
++            (void *)(fw_info->rcv_pkt), RCV_PKT_LEN,
++            &ret_len, MSG_TO);
++            if (ret_val >= 0)
++                break;
++        }
++
++        if (ret_val < 0)
++            return ret_val;
++
++        if (CMD_CMP_EVT == fw_info->evt_hdr->evt) {
++            if (fw_info->cmd_hdr->opcode == fw_info->cmd_cmp->opcode)
++                return ret_len;
++        }
++    }
++}
++
++int set_bt_onoff(firmware_info *fw_info, uint8_t onoff)
++{
++    int ret_val;
++
++    AICBT_INFO("%s: %s", __func__, onoff != 0 ? "on" : "off");
++
++    fw_info->cmd_hdr->opcode = cpu_to_le16(BTOFF_OPCODE);
++    fw_info->cmd_hdr->plen = 1;
++    fw_info->pkt_len = CMD_HDR_LEN + 1;
++    fw_info->send_pkt[CMD_HDR_LEN] = onoff;
++
++    ret_val = send_hci_cmd(fw_info);
++    if (ret_val < 0) {
++        AICBT_ERR("%s: Failed to send bt %s cmd, errno %d",
++                __func__, onoff != 0 ? "on" : "off", ret_val);
++        return ret_val;
++    }
++
++    ret_val = rcv_hci_evt(fw_info);
++    if (ret_val < 0) {
++        AICBT_ERR("%s: Failed to receive bt %s event, errno %d",
++                __func__, onoff != 0 ? "on" : "off", ret_val);
++        return ret_val;
++    }
++
++    return ret_val;
++}
++
++//for 8800DC start
++u32 fwcfg_tbl[][2] = {
++    {0x40200028, 0x0021047e},
++    {0x40200024, 0x0000011d},
++};
++
++int fw_config(firmware_info* fw_info)
++{
++    int ret_val = -1;
++    struct hci_dbg_rd_mem_cmd *rd_cmd;
++    struct hci_dbg_rd_mem_cmd_evt *evt_para;
++    int len = 0, i = 0;
++    struct fw_status *evt_status;
++
++    rd_cmd = (struct hci_dbg_rd_mem_cmd *)(fw_info->req_para);
++    if (!rd_cmd)
++        return -ENOMEM;
++
++    rd_cmd->start_addr = 0x40200024;
++    rd_cmd->type = 32;
++    rd_cmd->length = 4;
++    fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
++    fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
++    fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
++
++    ret_val = send_hci_cmd(fw_info);
++    if (ret_val < 0) {
++        printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++                __func__, fw_info->cmd_hdr->opcode, ret_val);
++        return ret_val;
++    }
++
++    ret_val = rcv_hci_evt(fw_info);
++    if (ret_val < 0) {
++        printk("%s: Failed to receive hci event, errno %d",
++                __func__, ret_val);
++        return ret_val;
++    }
++
++    evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
++
++    printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
++            __func__, evt_para->status, evt_para->length,
++            evt_para->data[0],
++            evt_para->data[1],
++            evt_para->data[2],
++            evt_para->data[3]);
++
++    ret_val = evt_para->status;
++    if (evt_para->status == 0) {
++        uint16_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8));
++        printk("%s rd_data is %x\n", __func__, rd_data);
++        if (rd_data == 0x119) {
++            struct aicbt_patch_table_cmd *patch_table_cmd = (struct aicbt_patch_table_cmd *)(fw_info->req_para);
++            len = sizeof(fwcfg_tbl) / sizeof(u32) / 2;
++            patch_table_cmd->patch_num = len;
++            for (i = 0; i < len; i++) {
++                memcpy(&patch_table_cmd->patch_table_addr[i], &fwcfg_tbl[i][0], sizeof(uint32_t));
++                memcpy(&patch_table_cmd->patch_table_data[i], &fwcfg_tbl[i][1], sizeof(uint32_t));
++                printk("%s [%d] data: %08x %08x\n", __func__, i, patch_table_cmd->patch_table_addr[i],patch_table_cmd->patch_table_data[i]);
++            }
++            fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_UPDATE_PT_CMD);
++            fw_info->cmd_hdr->plen = HCI_VSC_UPDATE_PT_SIZE;
++            fw_info->pkt_len = fw_info->cmd_hdr->plen + 3;
++            ret_val = send_hci_cmd(fw_info);
++            if (ret_val < 0) {
++                AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++                return ret_val;
++            }
++            ret_val = rcv_hci_evt(fw_info);
++            if (ret_val < 0) {
++                printk("%s: Failed to receive hci event, errno %d",
++                        __func__, ret_val);
++                return ret_val;
++            }
++            evt_status = (struct fw_status *)fw_info->rsp_para;
++            ret_val = evt_status->status;
++            if (0 != evt_status->status) {
++                ret_val = -1;
++            } else {
++                ret_val = 0;
++            }
++
++        }
++    }
++    return ret_val;
++}
++
++int system_config(firmware_info *fw_info)
++{
++    int ret_val = -1;
++    struct hci_dbg_rd_mem_cmd *rd_cmd;
++    struct hci_dbg_rd_mem_cmd_evt *evt_para;
++    //int len = 0, i = 0;
++    //struct fw_status *evt_status;
++
++    rd_cmd = (struct hci_dbg_rd_mem_cmd *)(fw_info->req_para);
++    if (!rd_cmd)
++        return -ENOMEM;
++
++    rd_cmd->start_addr = 0x40500000;
++    rd_cmd->type = 32;
++    rd_cmd->length = 4;
++    fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
++    fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
++    fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
++
++    ret_val = send_hci_cmd(fw_info);
++    if (ret_val < 0)
++    {
++        printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++               __func__, fw_info->cmd_hdr->opcode, ret_val);
++        return ret_val;
++    }
++
++    ret_val = rcv_hci_evt(fw_info);
++    if (ret_val < 0)
++    {
++        printk("%s: Failed to receive hci event, errno %d",
++               __func__, ret_val);
++        return ret_val;
++    }
++
++    evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
++
++    printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
++           __func__, evt_para->status, evt_para->length,
++           evt_para->data[0],
++           evt_para->data[1],
++           evt_para->data[2],
++           evt_para->data[3]);
++
++    ret_val = evt_para->status;
++    if (evt_para->status == 0)
++    {
++        uint32_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8) | (evt_para->data[2] << 16) | (evt_para->data[3] << 24));
++        //printk("%s 0x40500000 rd_data is %x\n", __func__, rd_data);
++        chip_id = (u8) (rd_data >> 16);
++    }
++
++    rd_cmd->start_addr = 0x20;
++    rd_cmd->type = 32;
++    rd_cmd->length = 4;
++    fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
++    fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
++    fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
++
++    ret_val = send_hci_cmd(fw_info);
++    if (ret_val < 0)
++    {
++        printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++               __func__, fw_info->cmd_hdr->opcode, ret_val);
++        return ret_val;
++    }
++
++    ret_val = rcv_hci_evt(fw_info);
++    if (ret_val < 0)
++    {
++        printk("%s: Failed to receive hci event, errno %d",
++               __func__, ret_val);
++        return ret_val;
++    }
++
++    evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
++
++    printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
++           __func__, evt_para->status, evt_para->length,
++           evt_para->data[0],
++           evt_para->data[1],
++           evt_para->data[2],
++           evt_para->data[3]);
++
++    ret_val = evt_para->status;
++    if (evt_para->status == 0)
++    {
++        uint32_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8) | (evt_para->data[2] << 16) | (evt_para->data[3] << 24));
++        //printk("%s 0x02 rd_data is %x\n", __func__, rd_data);
++        sub_chip_id = (u8) (rd_data);
++    }
++    printk("chip_id = %x, sub_chip_id = %x\n", chip_id, sub_chip_id);
++    return ret_val;
++}
++
++int check_fw_status(firmware_info* fw_info)
++{
++    struct fw_status *read_ver_rsp;
++    int ret_val = -1;
++
++    fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_FW_STATUS_GET_CMD);
++    fw_info->cmd_hdr->plen = 0;
++    fw_info->pkt_len = CMD_HDR_LEN;
++
++    ret_val = send_hci_cmd(fw_info);
++    if (ret_val < 0) {
++        printk("%s: Failed to send hci cmd 0x%04x, errno %d",
++                __func__, fw_info->cmd_hdr->opcode, ret_val);
++        return ret_val;
++    }
++
++    ret_val = rcv_hci_evt(fw_info);
++    if (ret_val < 0) {
++        printk("%s: Failed to receive hci event, errno %d",
++                __func__, ret_val);
++        return ret_val;
++    }
++
++    read_ver_rsp = (struct fw_status *)(fw_info->rsp_para);
++
++    printk("%s: fw status = 0x%04x",
++            __func__, read_ver_rsp->status);
++    return read_ver_rsp->status;
++}
++
++int download_data(firmware_info *fw_info, u32 fw_addr, char *filename)
++{
++    unsigned int i=0;
++    int size;
++    u8 *dst=NULL;
++    int err=0;
++    struct hci_dbg_wr_mem_cmd *dl_cmd;
++    int hdr_len = sizeof(__le32) + sizeof(__u8) + sizeof(__u8);
++    int data_len = HCI_VSC_MEM_WR_SIZE;
++    int frag_len = data_len + hdr_len;
++    int ret_val;
++    int ncmd = 1;
++    struct fw_status *evt_para;
++
++    /* load aic firmware */
++    size = aic_load_firmware(&dst, filename, NULL);
++    if(size <= 0){
++            printk("wrong size of firmware file\n");
++            vfree(dst);
++            dst = NULL;
++            return -1;
++    }
++
++    dl_cmd = (struct hci_dbg_wr_mem_cmd *)(fw_info->req_para);
++    if (!dl_cmd)
++        return -ENOMEM;
++    evt_para = (struct fw_status *)fw_info->rsp_para;
++
++    /* Copy the file on the Embedded side */
++    printk("### Upload %s firmware, @ = %x  size=%d\n", filename, fw_addr, size);
++
++    if (size > HCI_VSC_MEM_WR_SIZE) {// > 1KB data
++        for (i = 0; i < (size - HCI_VSC_MEM_WR_SIZE); i += HCI_VSC_MEM_WR_SIZE) {//each time write 240 bytes
++            data_len = HCI_VSC_MEM_WR_SIZE;
++            frag_len = data_len + hdr_len;
++            memcpy(dl_cmd->data, dst + i, data_len);
++            dl_cmd->length = data_len;
++            dl_cmd->type = 32;
++            dl_cmd->start_addr = fw_addr + i;
++            fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
++            fw_info->cmd_hdr->plen = frag_len;
++            fw_info->pkt_len = frag_len + 3;
++            #if 0
++            printk("[%d] data_len %d, src %x, dst %x\n", i, data_len, dst + i, fw_addr + i);
++            printk("%p , %d\n", dl_cmd, fw_info->pkt_len);
++            print_hex_dump(KERN_ERR,"payload:",DUMP_PREFIX_NONE,16,1,dl_cmd->data,32,false);
++            /* Send download command */
++            print_hex_dump(KERN_ERR,"data:",DUMP_PREFIX_NONE,16,1,fw_info->send_pkt,32,false);
++            #endif
++            ret_val = send_hci_cmd(fw_info);
++
++            while (ncmd > 0) {
++                ret_val = rcv_hci_evt(fw_info);
++                printk("rcv_hci_evt %d\n", ret_val);
++                if (ret_val < 0) {
++                    AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++                    goto out;
++                } else {
++                    AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
++                    ncmd--;
++                }
++                if (0 != evt_para->status) {
++                    AICBT_ERR("%s: Receive acked frag num %d, err status %d",
++                            __func__, ret_val, evt_para->status);
++                    ret_val = -1;
++                    goto out;
++                } else {
++                    ret_val = 0;
++                }
++            }
++            ncmd = 1;
++        }
++    }
++
++    if (!err && (i < size)) {// <1KB data
++        data_len = size - i;
++        frag_len = data_len + hdr_len;
++        memcpy(dl_cmd->data, dst + i, data_len);
++        dl_cmd->length = data_len;
++        dl_cmd->type = 32;
++        dl_cmd->start_addr = fw_addr + i;
++        fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
++        fw_info->cmd_hdr->plen = frag_len;
++        fw_info->pkt_len = frag_len + 3;
++        ret_val = send_hci_cmd(fw_info);
++        //printk("(%d) data_len %d, src %x, dst %x\n", i, data_len, (dst + i), fw_addr + i);
++        //printk("%p , %d\n", dl_cmd, fw_info->pkt_len);
++        while (ncmd > 0) {
++            ret_val = rcv_hci_evt(fw_info);
++            if (ret_val < 0) {
++                AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++                goto out;
++            } else {
++                AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
++                ncmd--;
++            }
++            if (0 != evt_para->status) {
++                AICBT_ERR("%s: Receive acked frag num %d, err status %d",
++                        __func__, ret_val, evt_para->status);
++                ret_val = -1;
++                goto out;
++            } else {
++                ret_val = 0;
++            }
++        }
++        ncmd = 0;
++    }
++
++out:
++    if (dst) {
++        vfree(dst);
++        dst = NULL;
++    }
++
++    printk("fw download complete\n\n");
++    return ret_val;
++
++}
++
++
++struct aicbt_info_t {
++    uint32_t btmode;
++    uint32_t btport;
++    uint32_t uart_baud;
++    uint32_t uart_flowctrl;
++    uint32_t lpm_enable;
++    uint32_t txpwr_lvl;
++};
++
++struct aicbsp_info_t {
++    int hwinfo;
++    uint32_t cpmode;
++};
++
++enum aicbsp_cpmode_type {
++    AICBSP_CPMODE_WORK,
++    AICBSP_CPMODE_TEST,
++};
++
++/*  btmode
++ * used for force bt mode,if not AICBSP_MODE_NULL
++ * efuse valid and vendor_info will be invalid, even has beed set valid
++*/
++enum aicbt_btmode_type {
++    AICBT_BTMODE_BT_ONLY_SW = 0x0,    // bt only mode with switch
++    AICBT_BTMODE_BT_WIFI_COMBO,       // wifi/bt combo mode
++    AICBT_BTMODE_BT_ONLY,             // bt only mode without switch
++    AICBT_BTMODE_BT_ONLY_TEST,        // bt only test mode
++    AICBT_BTMODE_BT_WIFI_COMBO_TEST,  // wifi/bt combo test mode
++    AICBT_MODE_NULL = 0xFF,           // invalid value
++};
++
++enum aicbt_btport_type {
++    AICBT_BTPORT_NULL,
++    AICBT_BTPORT_MB,
++    AICBT_BTPORT_UART,
++};
++
++enum aicbt_uart_baud_type {
++    AICBT_UART_BAUD_115200     = 115200,
++    AICBT_UART_BAUD_921600     = 921600,
++    AICBT_UART_BAUD_1_5M       = 1500000,
++    AICBT_UART_BAUD_3_25M      = 3250000,
++};
++
++enum aicbt_uart_flowctrl_type {
++    AICBT_UART_FLOWCTRL_DISABLE = 0x0,    // uart without flow ctrl
++    AICBT_UART_FLOWCTRL_ENABLE,           // uart with flow ctrl
++};
++
++#define AICBSP_HWINFO_DEFAULT       (-1)
++#define AICBSP_CPMODE_DEFAULT       AICBSP_CPMODE_WORK
++#define AICBT_TXPWR_DFT                0x6F2F
++
++
++#define AICBT_BTMODE_DEFAULT        AICBT_BTMODE_BT_WIFI_COMBO
++#define AICBT_BTPORT_DEFAULT        AICBT_BTPORT_MB
++#define AICBT_UART_BAUD_DEFAULT     AICBT_UART_BAUD_1_5M
++#define AICBT_UART_FC_DEFAULT       AICBT_UART_FLOWCTRL_ENABLE
++#define AICBT_LPM_ENABLE_DEFAULT    0
++#define AICBT_TXPWR_LVL_DEFAULT     AICBT_TXPWR_DFT
++
++struct aicbsp_info_t aicbsp_info = {
++    .hwinfo   = AICBSP_HWINFO_DEFAULT,
++    .cpmode   = AICBSP_CPMODE_DEFAULT,
++};
++
++#ifndef CONFIG_USE_FW_REQUEST
++#define FW_PATH_MAX 200
++
++char aic_fw_path[FW_PATH_MAX];
++#if (CONFIG_BLUEDROID == 0)
++static const char* aic_default_fw_path = "/lib/firmware/aic8800DC";
++#else
++static const char* aic_default_fw_path = "/vendor/etc/firmware";
++#endif
++#endif //CONFIG_USE_FW_REQUEST
++
++static struct aicbt_info_t aicbt_info = {
++    .btmode        = AICBT_BTMODE_DEFAULT,
++    .btport        = AICBT_BTPORT_DEFAULT,
++    .uart_baud     = AICBT_UART_BAUD_DEFAULT,
++    .uart_flowctrl = AICBT_UART_FC_DEFAULT,
++    .lpm_enable    = AICBT_LPM_ENABLE_DEFAULT,
++    .txpwr_lvl     = AICBT_TXPWR_LVL_DEFAULT,
++};
++
++int patch_table_load(firmware_info *fw_info, struct aicbt_patch_table *_head)
++{
++    struct aicbt_patch_table *head, *p;
++    int i;
++    uint32_t *data = NULL;
++    struct aicbt_patch_table_cmd *patch_table_cmd = (struct aicbt_patch_table_cmd *)(fw_info->req_para);
++    struct fw_status *evt_para;
++    int ret_val = 0;
++    int ncmd = 1;
++    uint32_t len = 0;
++    uint32_t tot_len = 0;
++    head = _head;
++    for (p = head; p != NULL; p = p->next) {
++        data = p->data;
++        if(AICBT_PT_BTMODE == p->type){
++            *(data + 1)  = aicbsp_info.hwinfo < 0;
++            *(data + 3) = aicbsp_info.hwinfo;
++            *(data + 5)  = aicbsp_info.cpmode;
++
++            *(data + 7) = aicbt_info.btmode;
++            *(data + 9) = aicbt_info.btport;
++            *(data + 11) = aicbt_info.uart_baud;
++            *(data + 13) = aicbt_info.uart_flowctrl;
++            *(data + 15) = aicbt_info.lpm_enable;
++            *(data + 17) = aicbt_info.txpwr_lvl;
++
++        }
++        if (p->type == AICBT_PT_NULL || p->type == AICBT_PT_PWRON) {
++            continue;
++        }
++        if (p->type == AICBT_PT_VER) {
++            char *data_s = (char *)p->data;
++            printk("patch version %s\n", data_s);
++            continue;
++        }
++        if (p->len == 0) {
++            printk("len is 0\n");
++            continue;
++        }
++        tot_len = p->len;
++        while (tot_len) {
++            if (tot_len > HCI_PT_MAX_LEN) {
++                len = HCI_PT_MAX_LEN;
++            } else {
++                len = tot_len;
++            }
++            for (i = 0; i < len; i++) {
++                patch_table_cmd->patch_num = len;
++                memcpy(&patch_table_cmd->patch_table_addr[i], data, sizeof(uint32_t));
++                memcpy(&patch_table_cmd->patch_table_data[i], data + 1, sizeof(uint32_t));
++                printk("[%d] data: %08x %08x\n", i, patch_table_cmd->patch_table_addr[i],patch_table_cmd->patch_table_data[i]);
++                data += 2;
++            }
++            tot_len -= len;
++            evt_para = (struct fw_status *)fw_info->rsp_para;
++            //print_hex_dump(KERN_ERR,"data0:",DUMP_PREFIX_NONE,16,1,patch_table_cmd,sizeof(struct aicbt_patch_table_cmd),false);
++
++            //printk("patch num %x %d\n", patch_table_cmd->patch_num, sizeof(struct aicbt_patch_table_cmd));
++            fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_UPDATE_PT_CMD);
++            fw_info->cmd_hdr->plen = HCI_VSC_UPDATE_PT_SIZE;
++            fw_info->pkt_len = fw_info->cmd_hdr->plen + 3;
++            AICBT_DBG("patch num 0x%x, plen 0x%x\n", patch_table_cmd->patch_num, fw_info->cmd_hdr->plen );
++            //print_hex_dump(KERN_ERR,"patch table:",DUMP_PREFIX_NONE,16,1,fw_info->send_pkt,32,false);
++            ret_val = send_hci_cmd(fw_info);
++            while (ncmd > 0) {
++                ret_val = rcv_hci_evt(fw_info);
++                if (ret_val < 0) {
++                    AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
++                    goto out;
++                } else {
++                    AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
++                    ncmd--;
++                }
++                if (0 != evt_para->status) {
++                    AICBT_ERR("%s: Receive acked frag num %d, err status %d",
++                            __func__, ret_val, evt_para->status);
++                    ret_val = -1;
++                    goto out;
++                }
++            }
++            ncmd = 1;
++        }
++    }
++out:
++    aicbt_patch_table_free(&head);
++    return ret_val;
++}
++
++int aic_load_firmware(u8 ** fw_buf, const char *name, struct device *device)
++{
++
++#ifdef CONFIG_USE_FW_REQUEST
++      const struct firmware *fw = NULL;
++      u32 *dst = NULL;
++      void *buffer=NULL;
++      int size = 0;
++      int ret = 0;
++
++      printk("%s: request firmware = %s \n", __func__ ,name);
++
++
++      ret = request_firmware(&fw, name, NULL);
++
++      if (ret < 0) {
++              printk("Load %s fail\n", name);
++              release_firmware(fw);
++              return -1;
++      }
++
++      size = fw->size;
++      dst = (u32 *)fw->data;
++
++      if (size <= 0) {
++              printk("wrong size of firmware file\n");
++              release_firmware(fw);
++              return -1;
++      }
++
++
++      buffer = vmalloc(size);
++      memset(buffer, 0, size);
++      memcpy(buffer, dst, size);
++
++      *fw_buf = buffer;
++
++      release_firmware(fw);
++
++      return size;
++
++#else
++    u8 *buffer=NULL;
++    char *path=NULL;
++    struct file *fp=NULL;
++    int size = 0, len=0;
++    ssize_t rdlen=0;
++
++    /* get the firmware path */
++    path = __getname();
++    if (!path){
++            *fw_buf=NULL;
++            return -1;
++    }
++
++    if (strlen(aic_fw_path) > 0) {
++        printk("%s: use customer define fw_path\n", __func__);
++        len = snprintf(path, FW_PATH_MAX, "%s/%s", aic_fw_path, name);
++    } else {
++        len = snprintf(path, FW_PATH_MAX, "%s/%s",aic_default_fw_path, name);
++    }
++
++    if (len >= FW_PATH_MAX) {
++        printk("%s: %s file's path too long\n", __func__, name);
++        *fw_buf=NULL;
++        __putname(path);
++        return -1;
++    }
++
++    printk("%s :firmware path = %s  \n", __func__ ,path);
++
++
++    /* open the firmware file */
++    fp=filp_open(path, O_RDONLY, 0);
++    if(IS_ERR(fp) || (!fp)){
++            printk("%s: %s file failed to open\n", __func__, name);
++            if(IS_ERR(fp))
++        printk("is_Err\n");
++    if((!fp))
++        printk("null\n");
++    *fw_buf=NULL;
++            __putname(path);
++            fp=NULL;
++            return -1;
++    }
++
++    size = i_size_read(file_inode(fp));
++    if(size<=0){
++            printk("%s: %s file size invalid %d\n", __func__, name, size);
++            *fw_buf=NULL;
++            __putname(path);
++            filp_close(fp,NULL);
++            fp=NULL;
++            return -1;
++}
++
++    /* start to read from firmware file */
++    buffer = vmalloc(size);
++    memset(buffer, 0, size);
++    if(!buffer){
++            *fw_buf=NULL;
++            __putname(path);
++            filp_close(fp,NULL);
++            fp=NULL;
++            return -1;
++    }
++
++
++    #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
++    rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
++    #else
++    rdlen = kernel_read(fp, fp->f_pos, buffer, size);
++    #endif
++
++    if(size != rdlen){
++            printk("%s: %s file rdlen invalid %d %d\n", __func__, name, (int)rdlen, size);
++            *fw_buf=NULL;
++            __putname(path);
++            filp_close(fp,NULL);
++            fp=NULL;
++            vfree(buffer);
++            buffer=NULL;
++            return -1;
++    }
++    if(rdlen > 0){
++            fp->f_pos += rdlen;
++            //printk("f_pos=%d\n", (int)fp->f_pos);
++    }
++    *fw_buf = buffer;
++
++#if 0
++    MD5Init(&md5);
++    MD5Update(&md5, (unsigned char *)dst, size);
++    MD5Final(&md5, decrypt);
++
++    printk(MD5PINRT, MD5(decrypt));
++
++#endif
++    return size;
++#endif
++}
++
++int aicbt_patch_table_free(struct aicbt_patch_table **head)
++{
++    struct aicbt_patch_table *p = *head, *n = NULL;
++    while (p) {
++        n = p->next;
++        kfree(p->name);
++        kfree(p->data);
++        kfree(p);
++        p = n;
++    }
++    *head = NULL;
++    return 0;
++}
++
++int get_patch_addr_from_patch_table(firmware_info *fw_info, char *filename, uint32_t *fw_patch_base_addr)
++{
++    int size;
++    int ret = 0;
++    uint8_t *rawdata=NULL;
++    uint8_t *p = NULL;
++    uint32_t *data = NULL;
++    uint32_t type = 0, len = 0;
++    int j;
++
++    /* load aic firmware */
++    size = aic_load_firmware((u8 **)&rawdata, filename, NULL);
++
++    /* Copy the file on the Embedded side */
++    printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
++
++    if (size <= 0) {
++        printk("wrong size of firmware file\n");
++        ret = -1;
++        goto err;
++    }
++
++    p = rawdata;
++
++    if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
++        printk("TAG err\n");
++        ret = -1;
++        goto err;
++    }
++    p += 16;
++
++    while (p - rawdata < size) {
++        printk("size = %d  p - rawdata = 0x%0lx \r\n", size, p - rawdata);
++        p += 16;
++
++        type = *(uint32_t *)p;
++        p += 4;
++
++        len = *(uint32_t *)p;
++        p += 4;
++        printk("cur->type %x, len %d\n", type, len);
++
++        if(type >= 1000 ) {//Temp Workaround
++            len = 0;
++        }else{
++            data = (uint32_t *)p;
++            if (type == AICBT_PT_NULL) {
++                *(fw_patch_base_addr) = *(data + 3);
++                printk("addr found %x\n", *(fw_patch_base_addr));
++                for (j = 0; j < len; j++) {
++                    printk("addr %x\n", *(data+j));
++                }
++                break;
++            }
++            p += len * 8;
++        }
++    }
++
++    vfree(rawdata);
++    return ret;
++err:
++    //aicbt_patch_table_free(&head);
++
++    if (rawdata){
++        vfree(rawdata);
++    }
++    return ret;
++}
++
++
++
++int patch_table_download(firmware_info *fw_info, char *filename)
++{
++    struct aicbt_patch_table *head = NULL;
++    struct aicbt_patch_table *new = NULL;
++    struct aicbt_patch_table *cur = NULL;
++        int size;
++    int ret = 0;
++       uint8_t *rawdata=NULL;
++    uint8_t *p = NULL;
++
++    /* load aic firmware */
++    size = aic_load_firmware((u8 **)&rawdata, filename, NULL);
++
++    /* Copy the file on the Embedded side */
++    printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
++
++    if (size <= 0) {
++        printk("wrong size of firmware file\n");
++        ret = -1;
++        goto err;
++    }
++
++    p = rawdata;
++
++    if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
++        printk("TAG err\n");
++        ret = -1;
++        goto err;
++    }
++    p += 16;
++
++    while (p - rawdata < size) {
++        printk("size = %d  p - rawdata = 0x%0lx \r\n", size, p - rawdata);
++        new = (struct aicbt_patch_table *)kmalloc(sizeof(struct aicbt_patch_table), GFP_KERNEL);
++        memset(new, 0, sizeof(struct aicbt_patch_table));
++        if (head == NULL) {
++            head = new;
++            cur  = new;
++        } else {
++            cur->next = new;
++            cur = cur->next;
++        }
++
++        cur->name = (char *)kmalloc(sizeof(char) * 16, GFP_KERNEL);
++        memset(cur->name, 0, sizeof(char) * 16);
++        memcpy(cur->name, p, 16);
++        p += 16;
++
++        cur->type = *(uint32_t *)p;
++        p += 4;
++
++        cur->len = *(uint32_t *)p;
++        p += 4;
++        printk("cur->type %x, len %d\n", cur->type, cur->len);
++
++        if((cur->type )  >= 1000 ) {//Temp Workaround
++            cur->len = 0;
++        }else{
++            cur->data = (uint32_t *)kmalloc(sizeof(uint8_t) * cur->len * 8, GFP_KERNEL);
++            memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8);
++            memcpy(cur->data, p, cur->len * 8);
++            p += cur->len * 8;
++        }
++    }
++
++    vfree(rawdata);
++    patch_table_load(fw_info, head);
++    printk("fw_patch_table download complete\n\n");
++
++    return ret;
++err:
++    //aicbt_patch_table_free(&head);
++
++    if (rawdata){
++        vfree(rawdata);
++    }
++    return ret;
++}
++
++
++int download_patch(firmware_info *fw_info, int cached)
++{
++    int ret_val = 0;
++
++    printk("%s: Download fw patch start, cached %d", __func__, cached);
++
++    if (!fw_info) {
++        printk("%s: No patch entry exists(fw_info %p)", __func__, fw_info);
++        ret_val = -1;
++        goto end;
++    }
++
++    ret_val = fw_config(fw_info);
++    if (ret_val) {
++        printk("%s: fw config failed %d", __func__, ret_val);
++        goto free;
++    }
++
++    ret_val = system_config(fw_info);
++    if (ret_val)
++    {
++        printk("%s: system config failed %d", __func__, ret_val);
++        goto free;
++    }
++
++    /*
++     * step1: check firmware statis
++     * step2: download firmware if updated
++     */
++
++
++    ret_val = check_fw_status(fw_info);
++
++
++    if (ret_val) {
++        #if 0
++        ret_val = download_data(fw_info, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME);
++        if (ret_val) {
++            printk("aic load adid fail %d\n", ret_val);
++            goto free;
++        }
++        #endif
++        if (sub_chip_id == 0) {
++            ret_val= download_data(fw_info, FW_RAM_PATCH_BASE_ADDR, FW_PATCH_BASE_NAME);
++            if (ret_val) {
++                printk("aic load patch fail %d\n", ret_val);
++                goto free;
++            }
++
++            ret_val= patch_table_download(fw_info, FW_PATCH_TABLE_NAME);
++            if (ret_val) {
++                printk("aic load patch ftable ail %d\n", ret_val);
++                goto free;
++            }
++        } else if (sub_chip_id == 1) {
++            uint32_t fw_ram_patch_base_addr = FW_RAM_PATCH_BASE_ADDR;
++
++            ret_val = get_patch_addr_from_patch_table(fw_info, FW_PATCH_TABLE_NAME_U02, &fw_ram_patch_base_addr);
++            if (ret_val)
++            {
++                printk("aic get patch addr fail %d\n", ret_val);
++                goto free;
++            }
++            printk("%s %x\n", __func__, fw_ram_patch_base_addr);
++            ret_val = download_data(fw_info, fw_ram_patch_base_addr, FW_PATCH_BASE_NAME_U02);
++            if (ret_val)
++            {
++                printk("aic load patch fail %d\n", ret_val);
++                goto free;
++            }
++
++            ret_val = patch_table_download(fw_info, FW_PATCH_TABLE_NAME_U02);
++            if (ret_val)
++            {
++                printk("aic load patch ftable ail %d\n", ret_val);
++                goto free;
++            }
++        } else if (sub_chip_id == 2) {
++            uint32_t fw_ram_patch_base_addr = FW_RAM_PATCH_BASE_ADDR;
++
++            ret_val = get_patch_addr_from_patch_table(fw_info, FW_PATCH_TABLE_NAME_U02H, &fw_ram_patch_base_addr);
++            if (ret_val)
++            {
++                printk("aic get patch addr fail %d\n", ret_val);
++                goto free;
++            }
++            printk("U02H %s %x\n", __func__, fw_ram_patch_base_addr);
++            ret_val = download_data(fw_info, fw_ram_patch_base_addr, FW_PATCH_BASE_NAME_U02H);
++            if (ret_val)
++            {
++                printk("aic load patch fail %d\n", ret_val);
++                goto free;
++            }
++
++            ret_val = patch_table_download(fw_info, FW_PATCH_TABLE_NAME_U02H);
++            if (ret_val)
++            {
++                printk("aic load patch ftable ail %d\n", ret_val);
++                goto free;
++            }
++      } else {
++            printk("%s unsupported sub_chip_id %x\n", __func__, sub_chip_id);
++        }
++    }
++
++free:
++    /* Free fw data after download finished */
++    kfree(fw_info->fw_data);
++    fw_info->fw_data = NULL;
++
++end:
++    return ret_val;
++}
++
++//for 8800dc end
++
++firmware_info *firmware_info_init(struct usb_interface *intf)
++{
++    struct usb_device *udev = interface_to_usbdev(intf);
++    firmware_info *fw_info;
++
++    AICBT_DBG("%s: start", __func__);
++
++    fw_info = kzalloc(sizeof(*fw_info), GFP_KERNEL);
++    if (!fw_info)
++        return NULL;
++
++    fw_info->send_pkt = kzalloc(SEND_PKT_LEN, GFP_KERNEL);
++    if (!fw_info->send_pkt) {
++        kfree(fw_info);
++        return NULL;
++    }
++
++    fw_info->rcv_pkt = kzalloc(RCV_PKT_LEN, GFP_KERNEL);
++    if (!fw_info->rcv_pkt) {
++        kfree(fw_info->send_pkt);
++        kfree(fw_info);
++        return NULL;
++    }
++
++    fw_info->intf = intf;
++    fw_info->udev = udev;
++if(g_chipid == PRODUCT_ID_AIC8801 || g_chipid == PRODUCT_ID_AIC8800D80){
++    fw_info->pipe_in = usb_rcvbulkpipe(fw_info->udev, BULK_EP);
++      fw_info->pipe_out = usb_rcvbulkpipe(fw_info->udev, CTRL_EP);
++}else if(g_chipid == PRODUCT_ID_AIC8800DC){
++    fw_info->pipe_in = usb_rcvintpipe(fw_info->udev, INTR_EP);
++    fw_info->pipe_out = usb_sndctrlpipe(fw_info->udev, CTRL_EP);
++}
++    fw_info->cmd_hdr = (struct hci_command_hdr *)(fw_info->send_pkt);
++    fw_info->evt_hdr = (struct hci_event_hdr *)(fw_info->rcv_pkt);
++    fw_info->cmd_cmp = (struct hci_ev_cmd_complete *)(fw_info->rcv_pkt + EVT_HDR_LEN);
++    fw_info->req_para = fw_info->send_pkt + CMD_HDR_LEN;
++    fw_info->rsp_para = fw_info->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN;
++
++#if BTUSB_RPM
++    AICBT_INFO("%s: Auto suspend is enabled", __func__);
++    usb_enable_autosuspend(udev);
++    pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
++#else
++    AICBT_INFO("%s: Auto suspend is disabled", __func__);
++    usb_disable_autosuspend(udev);
++#endif
++
++#if BTUSB_WAKEUP_HOST
++    device_wakeup_enable(&udev->dev);
++#endif
++
++    return fw_info;
++}
++
++
++void firmware_info_destroy(struct usb_interface *intf)
++{
++    firmware_info *fw_info;
++    struct usb_device *udev;
++    struct btusb_data *data;
++
++    udev = interface_to_usbdev(intf);
++    data = usb_get_intfdata(intf);
++
++    fw_info = data->fw_info;
++    if (!fw_info)
++        return;
++
++#if BTUSB_RPM
++    usb_disable_autosuspend(udev);
++#endif
++
++    /*
++     * In order to reclaim fw data mem, we free fw_data immediately
++     * after download patch finished instead of here.
++     */
++    kfree(fw_info->rcv_pkt);
++    kfree(fw_info->send_pkt);
++    kfree(fw_info);
++
++
++}
++
++static struct usb_driver btusb_driver;
++
++static struct usb_device_id btusb_table[] = {
++    #if 0
++    { .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
++                     USB_DEVICE_ID_MATCH_INT_INFO,
++      .idVendor = 0xa69d,
++      .bInterfaceClass = 0xe0,
++      .bInterfaceSubClass = 0x01,
++      .bInterfaceProtocol = 0x01 },
++    #endif
++    {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8801, 0xe0, 0x01,0x01)},
++    {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800D80, 0xe0, 0x01,0x01)},
++    {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800DC, 0xe0, 0x01,0x01)},
++    {}
++};
++
++MODULE_DEVICE_TABLE(usb, btusb_table);
++
++static int inc_tx(struct btusb_data *data)
++{
++    unsigned long flags;
++    int rv;
++
++    spin_lock_irqsave(&data->txlock, flags);
++    rv = test_bit(BTUSB_SUSPENDING, &data->flags);
++    if (!rv)
++        data->tx_in_flight++;
++    spin_unlock_irqrestore(&data->txlock, flags);
++
++    return rv;
++}
++
++void check_sco_event(struct urb *urb)
++{
++    u8* opcode = (u8*)(urb->transfer_buffer);
++    u8 status;
++    static uint16_t sco_handle = 0;
++    uint16_t handle;
++    u8 air_mode = 0;
++    struct hci_dev *hdev = urb->context;
++#ifdef CONFIG_SCO_OVER_HCI
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    AIC_sco_card_t  *pSCOSnd = data->pSCOSnd;
++#endif
++
++    switch (*opcode) {
++    case HCI_EV_SYNC_CONN_COMPLETE:
++        AICBT_INFO("%s: HCI_EV_SYNC_CONN_COMPLETE(0x%02x)", __func__, *opcode);
++        status = *(opcode + 2);
++        sco_handle = *(opcode + 3) | *(opcode + 4) << 8;
++        air_mode = *(opcode + 18);
++              printk("%s status:%d,air_mode:%d \r\n", __func__, status,air_mode);
++        if (status == 0) {
++            hdev->conn_hash.sco_num++;
++                      hdev->notify(hdev, 0);
++            //schedule_work(&data->work);
++            if (air_mode == 0x03) {
++                set_select_msbc(CODEC_MSBC);
++            }
++        }
++        break;
++    case HCI_EV_DISCONN_COMPLETE:
++        AICBT_INFO("%s: HCI_EV_DISCONN_COMPLETE(0x%02x)", __func__, *opcode);
++        status = *(opcode + 2);
++        handle = *(opcode + 3) | *(opcode + 4) << 8;
++        if (status == 0 && sco_handle == handle) {
++            hdev->conn_hash.sco_num--;
++                      hdev->notify(hdev, 0);
++            set_select_msbc(CODEC_CVSD);
++            //schedule_work(&data->work);
++#ifdef CONFIG_SCO_OVER_HCI
++                      if (test_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states)) {
++                              mod_timer(&snd_cap_timer.cap_timer,jiffies + msecs_to_jiffies(3));
++                      }
++#endif
++        }
++        break;
++    default:
++        AICBT_DBG("%s: event 0x%02x", __func__, *opcode);
++        break;
++    }
++}
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++static inline void btusb_free_frags(struct btusb_data *data)
++{
++    unsigned long flags;
++
++    spin_lock_irqsave(&data->rxlock, flags);
++
++    kfree_skb(data->evt_skb);
++    data->evt_skb = NULL;
++
++    kfree_skb(data->acl_skb);
++    data->acl_skb = NULL;
++
++    kfree_skb(data->sco_skb);
++    data->sco_skb = NULL;
++
++    spin_unlock_irqrestore(&data->rxlock, flags);
++}
++
++static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
++{
++    struct sk_buff *skb;
++    int err = 0;
++
++    spin_lock(&data->rxlock);
++    skb = data->evt_skb;
++    //printk("%s count %d\n", __func__, count);
++
++#if 1
++    while (count) {
++        int len;
++
++        if (!skb) {
++            skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
++            if (!skb) {
++                err = -ENOMEM;
++                break;
++            }
++
++            bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
++            bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
++        }
++
++        len = min_t(uint, bt_cb(skb)->expect, count);
++        memcpy(skb_put(skb, len), buffer, len);
++
++        count -= len;
++        buffer += len;
++        bt_cb(skb)->expect -= len;
++
++        if (skb->len == HCI_EVENT_HDR_SIZE) {
++            /* Complete event header */
++            bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
++
++            if (skb_tailroom(skb) < bt_cb(skb)->expect) {
++                kfree_skb(skb);
++                skb = NULL;
++
++                err = -EILSEQ;
++                break;
++            }
++        }
++
++        if (bt_cb(skb)->expect == 0) {
++            /* Complete frame */
++            hci_recv_frame(data->hdev, skb);
++            skb = NULL;
++        }
++    }
++#endif
++
++    data->evt_skb = skb;
++    spin_unlock(&data->rxlock);
++
++    return err;
++}
++
++static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
++{
++    struct sk_buff *skb;
++    int err = 0;
++
++    spin_lock(&data->rxlock);
++    skb = data->acl_skb;
++
++    while (count) {
++        int len;
++
++        if (!skb) {
++            skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
++            if (!skb) {
++                err = -ENOMEM;
++                break;
++            }
++
++            bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
++            bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
++        }
++
++        len = min_t(uint, bt_cb(skb)->expect, count);
++        memcpy(skb_put(skb, len), buffer, len);
++
++        count -= len;
++        buffer += len;
++        bt_cb(skb)->expect -= len;
++
++        if (skb->len == HCI_ACL_HDR_SIZE) {
++            __le16 dlen = hci_acl_hdr(skb)->dlen;
++
++            /* Complete ACL header */
++            bt_cb(skb)->expect = __le16_to_cpu(dlen);
++
++            if (skb_tailroom(skb) < bt_cb(skb)->expect) {
++                kfree_skb(skb);
++                skb = NULL;
++
++                err = -EILSEQ;
++                break;
++            }
++        }
++
++        if (bt_cb(skb)->expect == 0) {
++            /* Complete frame */
++            hci_recv_frame(data->hdev, skb);
++            skb = NULL;
++        }
++    }
++
++    data->acl_skb = skb;
++    spin_unlock(&data->rxlock);
++
++    return err;
++}
++
++static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
++{
++    struct sk_buff *skb;
++    int err = 0;
++
++    spin_lock(&data->rxlock);
++    skb = data->sco_skb;
++
++    while (count) {
++        int len;
++
++        if (!skb) {
++            skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
++            if (!skb) {
++                err = -ENOMEM;
++                break;
++            }
++
++            bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
++            bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
++        }
++
++        len = min_t(uint, bt_cb(skb)->expect, count);
++        memcpy(skb_put(skb, len), buffer, len);
++
++        count -= len;
++        buffer += len;
++        bt_cb(skb)->expect -= len;
++
++        if (skb->len == HCI_SCO_HDR_SIZE) {
++            /* Complete SCO header */
++            bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
++
++            if (skb_tailroom(skb) < bt_cb(skb)->expect) {
++                kfree_skb(skb);
++                skb = NULL;
++
++                err = -EILSEQ;
++                break;
++            }
++        }
++
++        if (bt_cb(skb)->expect == 0) {
++            /* Complete frame */
++            hci_recv_frame(data->hdev, skb);
++            skb = NULL;
++        }
++    }
++
++    data->sco_skb = skb;
++    spin_unlock(&data->rxlock);
++
++    return err;
++}
++#endif
++#endif // (CONFIG_BLUEDROID == 0)
++
++
++static void btusb_intr_complete(struct urb *urb)
++{
++    struct hci_dev *hdev = urb->context;
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    int err;
++
++    AICBT_DBG("%s: urb %p status %d count %d ", __func__,
++            urb, urb->status, urb->actual_length);
++
++    if (!test_bit(HCI_RUNNING, &hdev->flags)) {
++        printk("%s return \n", __func__);
++        return;
++    }
++    if (urb->status == 0) {
++        hdev->stat.byte_rx += urb->actual_length;
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
++              if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
++                                              urb->transfer_buffer,
++                                              urb->actual_length) < 0) {
++                      AICBT_ERR("%s: Corrupted event packet", __func__);
++                      hdev->stat.err_rx++;
++              }
++#else
++              if (btusb_recv_intr(data, urb->transfer_buffer,
++                                      urb->actual_length) < 0) {
++                      AICBT_ERR("%s corrupted event packet", hdev->name);
++                      hdev->stat.err_rx++;
++              }
++#endif
++
++#ifdef CONFIG_SCO_OVER_HCI
++              check_sco_event(urb);
++#endif
++#ifdef CONFIG_USB_AIC_UART_SCO_DRIVER
++              check_sco_event(urb);
++#endif
++
++    }
++    /* Avoid suspend failed when usb_kill_urb */
++    else if(urb->status == -ENOENT)    {
++        return;
++    }
++
++
++    if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
++        return;
++
++    usb_mark_last_busy(data->udev);
++    usb_anchor_urb(urb, &data->intr_anchor);
++
++    err = usb_submit_urb(urb, GFP_ATOMIC);
++    if (err < 0) {
++        if (err != -EPERM && err != -ENODEV)
++            AICBT_ERR("%s: Failed to re-submit urb %p, err %d",
++                    __func__, urb, err);
++        usb_unanchor_urb(urb);
++    }
++}
++
++static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    struct urb *urb;
++    unsigned char *buf;
++    unsigned int pipe;
++    int err, size;
++
++    if (!data->intr_ep)
++        return -ENODEV;
++
++    urb = usb_alloc_urb(0, mem_flags);
++    if (!urb)
++        return -ENOMEM;
++
++    size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
++
++    buf = kmalloc(size, mem_flags);
++    if (!buf) {
++        usb_free_urb(urb);
++        return -ENOMEM;
++    }
++
++    AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
++            __func__, size, data->intr_ep->bEndpointAddress);
++
++    pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
++
++    usb_fill_int_urb(urb, data->udev, pipe, buf, size,
++                        btusb_intr_complete, hdev,
++                        data->intr_ep->bInterval);
++
++    urb->transfer_flags |= URB_FREE_BUFFER;
++
++    usb_anchor_urb(urb, &data->intr_anchor);
++
++    err = usb_submit_urb(urb, mem_flags);
++    if (err < 0) {
++        AICBT_ERR("%s: Failed to submit urb %p, err %d",
++                __func__, urb, err);
++        usb_unanchor_urb(urb);
++    }
++
++    usb_free_urb(urb);
++
++    return err;
++}
++
++static void btusb_bulk_complete(struct urb *urb)
++{
++    struct hci_dev *hdev = urb->context;
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    int err;
++
++    AICBT_DBG("%s: urb %p status %d count %d",
++            __func__, urb, urb->status, urb->actual_length);
++
++    if (!test_bit(HCI_RUNNING, &hdev->flags)) {
++        printk("%s HCI_RUNNING\n", __func__);
++        return;
++    }
++    if (urb->status == 0) {
++        hdev->stat.byte_rx += urb->actual_length;
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
++              if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
++                        urb->transfer_buffer,
++                        urb->actual_length) < 0) {
++                              AICBT_ERR("%s: Corrupted ACL packet", __func__);
++                              hdev->stat.err_rx++;
++                      }
++#else
++              if (data->recv_bulk(data, urb->transfer_buffer,
++                              urb->actual_length) < 0) {
++                              AICBT_ERR("%s Corrupted ACL packet", hdev->name);
++                              hdev->stat.err_rx++;
++                      }
++#endif
++
++    }
++    /* Avoid suspend failed when usb_kill_urb */
++    else if(urb->status == -ENOENT)    {
++        printk("%s ENOENT\n", __func__);
++        return;
++    }
++    AICBT_DBG("%s: OUT", __func__);
++
++    if (!test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
++        printk("%s BTUSB_BULK_RUNNING\n", __func__);
++        return;
++    }
++    usb_anchor_urb(urb, &data->bulk_anchor);
++    usb_mark_last_busy(data->udev);
++
++    //printk("LIULI bulk submit\n");
++    err = usb_submit_urb(urb, GFP_ATOMIC);
++    if (err < 0) {
++        /* -EPERM: urb is being killed;
++         * -ENODEV: device got disconnected */
++        if (err != -EPERM && err != -ENODEV)
++            AICBT_ERR("btusb_bulk_complete %s urb %p failed to resubmit (%d)",
++                        hdev->name, urb, -err);
++        usb_unanchor_urb(urb);
++    }
++}
++
++static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    struct urb *urb;
++    unsigned char *buf;
++    unsigned int pipe;
++    int err, size = HCI_MAX_FRAME_SIZE;
++
++    AICBT_DBG("%s: hdev name %s", __func__, hdev->name);
++    AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
++            __func__, size, data->bulk_rx_ep->bEndpointAddress);
++
++    if (!data->bulk_rx_ep)
++        return -ENODEV;
++
++    urb = usb_alloc_urb(0, mem_flags);
++    if (!urb)
++        return -ENOMEM;
++
++    buf = kmalloc(size, mem_flags);
++    if (!buf) {
++        usb_free_urb(urb);
++        return -ENOMEM;
++    }
++
++    pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
++
++    usb_fill_bulk_urb(urb, data->udev, pipe,
++                    buf, size, btusb_bulk_complete, hdev);
++
++    urb->transfer_flags |= URB_FREE_BUFFER;
++
++    usb_mark_last_busy(data->udev);
++    usb_anchor_urb(urb, &data->bulk_anchor);
++
++    err = usb_submit_urb(urb, mem_flags);
++    if (err < 0) {
++        AICBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, err);
++        usb_unanchor_urb(urb);
++    }
++
++    usb_free_urb(urb);
++
++    return err;
++}
++
++static void btusb_isoc_complete(struct urb *urb)
++{
++    struct hci_dev *hdev = urb->context;
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    int i, err;
++      unsigned int total_length = 0;
++
++    AICBT_DBG("%s: urb %p status %d count %d",
++            __func__, urb, urb->status, urb->actual_length);
++
++    if (!test_bit(HCI_RUNNING, &hdev->flags))
++        return;
++
++    if (urb->status == 0) {
++        for (i = 0; i < urb->number_of_packets; i++) {
++            unsigned int offset = urb->iso_frame_desc[i].offset;
++            unsigned int length = urb->iso_frame_desc[i].actual_length;
++            //u8 *data = (u8 *)(urb->transfer_buffer + offset);
++            //AICBT_DBG("%d,%d ,%x,%x,%x  s %d.",
++            //offset, length, data[0], data[1],data[2],urb->iso_frame_desc[i].status);
++
++            if(total_length >= urb->actual_length){
++                AICBT_ERR("total_len >= actual_length ,return");
++                break;
++            }
++            total_length += length;
++
++            if (urb->iso_frame_desc[i].status)
++                continue;
++
++            hdev->stat.byte_rx += length;
++            if(length){
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
++                              if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
++                                        urb->transfer_buffer + offset,
++                                        length) < 0) {
++                                              AICBT_ERR("%s: Corrupted SCO packet", __func__);
++                                                      hdev->stat.err_rx++;
++                                      }
++#else
++                              if (btusb_recv_isoc(data, urb->transfer_buffer + offset,
++                                      length) < 0) {
++                                              AICBT_ERR("%s corrupted SCO packet",
++                                                        hdev->name);
++                                              hdev->stat.err_rx++;
++                              }
++#endif
++
++            }
++        }
++    }
++    /* Avoid suspend failed when usb_kill_urb */
++    else if(urb->status == -ENOENT) {
++        return;
++    }
++
++
++    if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
++        return;
++
++    usb_anchor_urb(urb, &data->isoc_anchor);
++    i = 0;
++retry:
++    err = usb_submit_urb(urb, GFP_ATOMIC);
++    if (err < 0) {
++        /* -EPERM: urb is being killed;
++         * -ENODEV: device got disconnected */
++        if (err != -EPERM && err != -ENODEV)
++            AICBT_ERR("%s: Failed to re-sumbit urb %p, retry %d, err %d",
++                    __func__, urb, i, err);
++        if (i < 10) {
++            i++;
++            mdelay(1);
++            goto retry;
++        }
++
++        usb_unanchor_urb(urb);
++    }
++}
++
++static inline void fill_isoc_descriptor(struct urb *urb, int len, int mtu)
++{
++    int i, offset = 0;
++
++    AICBT_DBG("%s: len %d mtu %d", __func__, len, mtu);
++
++    for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
++                    i++, offset += mtu, len -= mtu) {
++        urb->iso_frame_desc[i].offset = offset;
++        urb->iso_frame_desc[i].length = mtu;
++    }
++
++    if (len && i < BTUSB_MAX_ISOC_FRAMES) {
++        urb->iso_frame_desc[i].offset = offset;
++        urb->iso_frame_desc[i].length = len;
++        i++;
++    }
++
++    urb->number_of_packets = i;
++}
++
++static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    struct urb *urb;
++    unsigned char *buf;
++    unsigned int pipe;
++    int err, size;
++      int interval;
++
++    if (!data->isoc_rx_ep)
++        return -ENODEV;
++    AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
++            __func__, size, data->isoc_rx_ep->bEndpointAddress);
++
++    urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
++    if (!urb)
++        return -ENOMEM;
++
++    size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
++                        BTUSB_MAX_ISOC_FRAMES;
++
++    buf = kmalloc(size, mem_flags);
++    if (!buf) {
++        usb_free_urb(urb);
++        return -ENOMEM;
++    }
++
++    pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
++
++    urb->dev      = data->udev;
++    urb->pipe     = pipe;
++    urb->context  = hdev;
++    urb->complete = btusb_isoc_complete;
++      if (urb->dev->speed == USB_SPEED_HIGH || urb->dev->speed >= USB_SPEED_SUPER) {  
++              /* make sure interval is within allowed range */  
++              interval = clamp((int)data->isoc_rx_ep->bInterval, 1, 16);  
++              urb->interval = 1 << (interval - 1); 
++      } else {  
++              urb->interval = data->isoc_rx_ep->bInterval; 
++      }
++
++      AICBT_INFO("urb->interval %d \r\n", urb->interval);
++
++    urb->transfer_flags  = URB_FREE_BUFFER | URB_ISO_ASAP;
++    urb->transfer_buffer = buf;
++    urb->transfer_buffer_length = size;
++
++    fill_isoc_descriptor(urb, size,
++            le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
++
++    usb_anchor_urb(urb, &data->isoc_anchor);
++
++    err = usb_submit_urb(urb, mem_flags);
++    if (err < 0) {
++        AICBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, err);
++        usb_unanchor_urb(urb);
++    }
++
++    usb_free_urb(urb);
++
++    return err;
++}
++
++static void btusb_tx_complete(struct urb *urb)
++{
++    struct sk_buff *skb = urb->context;
++    struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++
++    if (!test_bit(HCI_RUNNING, &hdev->flags))
++        goto done;
++
++    if (!urb->status)
++        hdev->stat.byte_tx += urb->transfer_buffer_length;
++    else
++        hdev->stat.err_tx++;
++
++done:
++    spin_lock(&data->txlock);
++    data->tx_in_flight--;
++    spin_unlock(&data->txlock);
++
++    kfree(urb->setup_packet);
++
++    kfree_skb(skb);
++}
++
++static void btusb_isoc_tx_complete(struct urb *urb)
++{
++    struct sk_buff *skb = urb->context;
++    struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++    AICBT_DBG("%s: urb %p status %d count %d",
++            __func__, urb, urb->status, urb->actual_length);
++
++    if (skb && hdev) {
++        if (!test_bit(HCI_RUNNING, &hdev->flags))
++            goto done;
++
++        if (!urb->status)
++            hdev->stat.byte_tx += urb->transfer_buffer_length;
++        else
++            hdev->stat.err_tx++;
++    } else
++        AICBT_ERR("%s: skb 0x%p hdev 0x%p", __func__, skb, hdev);
++
++done:
++    kfree(urb->setup_packet);
++
++    kfree_skb(skb);
++}
++
++#if (CONFIG_BLUEDROID == 0)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 9)
++static int btusb_shutdown(struct hci_dev *hdev)
++{
++      struct sk_buff *skb;
++    printk("aic %s\n", __func__);
++
++      skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
++      if (IS_ERR(skb)) {
++              printk("HCI reset during shutdown failed\n");
++              return PTR_ERR(skb);
++      }
++      kfree_skb(skb);
++
++    return 0;
++}
++#endif
++#endif //(CONFIG_BLUEDROID == 0)
++
++static int btusb_open(struct hci_dev *hdev)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    int err = 0;
++
++    AICBT_INFO("%s: Start", __func__);
++
++    err = usb_autopm_get_interface(data->intf);
++    if (err < 0)
++        return err;
++
++    data->intf->needs_remote_wakeup = 1;
++
++#if (CONFIG_BLUEDROID == 0)
++              //err = download_patch(data->fw_info,1);
++              printk(" download_patch %d", err);
++              if (err < 0) {
++                      goto failed;
++              }
++#endif
++
++
++    if (test_and_set_bit(HCI_RUNNING, &hdev->flags)){
++        goto done;
++    }
++
++    if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)){
++        goto done;
++    }
++
++    err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
++    if (err < 0)
++        goto failed;
++
++    err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
++    if (err < 0) {
++        mdelay(URB_CANCELING_DELAY_MS);
++        usb_kill_anchored_urbs(&data->intr_anchor);
++        goto failed;
++    }
++
++    set_bit(BTUSB_BULK_RUNNING, &data->flags);
++    btusb_submit_bulk_urb(hdev, GFP_KERNEL);
++
++done:
++    usb_autopm_put_interface(data->intf);
++    AICBT_INFO("%s: End", __func__);
++    return 0;
++
++failed:
++    clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++    clear_bit(HCI_RUNNING, &hdev->flags);
++    usb_autopm_put_interface(data->intf);
++    AICBT_ERR("%s: Failed", __func__);
++    return err;
++}
++
++static void btusb_stop_traffic(struct btusb_data *data)
++{
++    mdelay(URB_CANCELING_DELAY_MS);
++    usb_kill_anchored_urbs(&data->intr_anchor);
++    usb_kill_anchored_urbs(&data->bulk_anchor);
++    usb_kill_anchored_urbs(&data->isoc_anchor);
++}
++
++static int btusb_close(struct hci_dev *hdev)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
++    int i;
++#endif
++      int err;
++
++    AICBT_INFO("%s: hci running %lu", __func__, hdev->flags & HCI_RUNNING);
++
++    if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)){
++        return 0;
++    }
++      
++      if (!test_and_clear_bit(BTUSB_INTR_RUNNING, &data->flags)){
++        return 0;
++      }
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
++      for (i = 0; i < NUM_REASSEMBLY; i++) {
++              if (hdev->reassembly[i]) {
++                      AICBT_DBG("%s: free ressembly[%d]", __func__, i);
++                      kfree_skb(hdev->reassembly[i]);
++                      hdev->reassembly[i] = NULL;
++              }
++      }
++#endif
++
++    cancel_work_sync(&data->work);
++    cancel_work_sync(&data->waker);
++
++    clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++    clear_bit(BTUSB_BULK_RUNNING, &data->flags);
++    clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++
++    btusb_stop_traffic(data);
++    err = usb_autopm_get_interface(data->intf);
++    if (err < 0)
++        goto failed;
++
++    data->intf->needs_remote_wakeup = 0;
++    usb_autopm_put_interface(data->intf);
++
++failed:
++    mdelay(URB_CANCELING_DELAY_MS);
++    usb_scuttle_anchored_urbs(&data->deferred);
++    return 0;
++}
++
++static int btusb_flush(struct hci_dev *hdev)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++
++    AICBT_DBG("%s", __func__);
++
++    mdelay(URB_CANCELING_DELAY_MS);
++    usb_kill_anchored_urbs(&data->tx_anchor);
++
++    return 0;
++}
++
++#ifdef CONFIG_SCO_OVER_HCI
++static void btusb_isoc_snd_tx_complete(struct urb *urb);
++
++static int snd_send_sco_frame(struct sk_buff *skb)
++{
++    struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    //struct usb_ctrlrequest *dr;
++    struct urb *urb;
++    unsigned int pipe;
++    int err;
++
++    AICBT_DBG("%s:pkt type %d, packet_len : %d",
++            __func__,bt_cb(skb)->pkt_type, skb->len);
++
++    if (!hdev && !test_bit(HCI_RUNNING, &hdev->flags))
++        return -EBUSY;
++
++    if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1) {
++        kfree(skb);
++        return -ENODEV;
++    }
++
++    urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
++    if (!urb) {
++        AICBT_ERR("%s: Failed to allocate mem for sco pkts", __func__);
++        kfree(skb);
++        return -ENOMEM;
++    }
++
++    pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
++
++    usb_fill_int_urb(urb, data->udev, pipe,
++            skb->data, skb->len, btusb_isoc_snd_tx_complete,
++            skb, data->isoc_tx_ep->bInterval);
++
++    urb->transfer_flags  = URB_ISO_ASAP;
++
++    fill_isoc_descriptor(urb, skb->len,
++            le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
++
++    hdev->stat.sco_tx++;
++
++    usb_anchor_urb(urb, &data->tx_anchor);
++
++    err = usb_submit_urb(urb, GFP_ATOMIC);
++    if (err < 0) {
++        AICBT_ERR("%s: Failed to submit urb %p, pkt type %d, err %d",
++                __func__, urb, bt_cb(skb)->pkt_type, err);
++        kfree(urb->setup_packet);
++        usb_unanchor_urb(urb);
++    } else
++        usb_mark_last_busy(data->udev);
++    usb_free_urb(urb);
++
++    return err;
++
++}
++
++static bool snd_copy_send_sco_data( AIC_sco_card_t *pSCOSnd)
++{
++    struct snd_pcm_runtime *runtime = pSCOSnd->playback.substream->runtime;
++      unsigned int frame_bytes = 2, frames1;
++    const u8 *source;
++
++    snd_pcm_uframes_t period_size = runtime->period_size;
++    int i, count;
++    u8 buffer[period_size * 3];
++    int sco_packet_bytes = pSCOSnd->playback.sco_packet_bytes;
++    struct sk_buff *skb;
++
++    count = frames_to_bytes(runtime, period_size)/sco_packet_bytes;
++    skb = bt_skb_alloc(((sco_packet_bytes + HCI_SCO_HDR_SIZE) * count), GFP_ATOMIC);
++    skb->dev = (void *)hci_dev_get(0);
++    bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
++    skb_put(skb, ((sco_packet_bytes + HCI_SCO_HDR_SIZE) * count));
++    if(!skb)
++        return false;
++
++    AICBT_DBG("%s, buffer_pos:%d sco_handle:%d sco_packet_bytes:%d count:%d", __FUNCTION__, pSCOSnd->playback.buffer_pos, pSCOSnd->usb_data->sco_handle,
++    sco_packet_bytes, count);
++
++    source = runtime->dma_area + pSCOSnd->playback.buffer_pos * frame_bytes;
++
++    if (pSCOSnd->playback.buffer_pos + period_size <= runtime->buffer_size) {
++      memcpy(buffer, source, period_size * frame_bytes);
++    } else {
++      /* wrap around at end of ring buffer */
++      frames1 = runtime->buffer_size - pSCOSnd->playback.buffer_pos;
++      memcpy(buffer, source, frames1 * frame_bytes);
++      memcpy(&buffer[frames1 * frame_bytes],
++             runtime->dma_area, (period_size - frames1) * frame_bytes);
++    }
++
++    pSCOSnd->playback.buffer_pos += period_size;
++    if ( pSCOSnd->playback.buffer_pos >= runtime->buffer_size)
++       pSCOSnd->playback.buffer_pos -= runtime->buffer_size;
++
++    for(i = 0; i < count; i++) {
++        *((__u16 *)(skb->data + i * (sco_packet_bytes + HCI_SCO_HDR_SIZE))) = pSCOSnd->usb_data->sco_handle;
++        *((__u8 *)(skb->data + i*(sco_packet_bytes + HCI_SCO_HDR_SIZE) + 2)) = sco_packet_bytes;
++        memcpy((skb->data + i * (sco_packet_bytes + HCI_SCO_HDR_SIZE) + HCI_SCO_HDR_SIZE),
++          &buffer[sco_packet_bytes * i], sco_packet_bytes);
++    }
++
++    if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++        snd_pcm_period_elapsed(pSCOSnd->playback.substream);
++    }
++    snd_send_sco_frame(skb);
++    return true;
++}
++
++static void btusb_isoc_snd_tx_complete(struct urb *urb)
++{
++    struct sk_buff *skb = urb->context;
++    struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    AIC_sco_card_t  *pSCOSnd = data->pSCOSnd;
++
++    AICBT_DBG("%s: status %d count %d",
++            __func__,urb->status, urb->actual_length);
++
++    if (skb && hdev) {
++        if (!test_bit(HCI_RUNNING, &hdev->flags))
++            goto done;
++
++        if (!urb->status)
++            hdev->stat.byte_tx += urb->transfer_buffer_length;
++        else
++            hdev->stat.err_tx++;
++    } else
++        AICBT_ERR("%s: skb 0x%p hdev 0x%p", __func__, skb, hdev);
++
++done:
++    kfree(urb->setup_packet);
++    kfree_skb(skb);
++    if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)){
++        snd_copy_send_sco_data(pSCOSnd);
++        //schedule_work(&pSCOSnd->send_sco_work);
++    }
++}
++
++static void playback_work(struct work_struct *work)
++{
++    AIC_sco_card_t *pSCOSnd = container_of(work, AIC_sco_card_t, send_sco_work);
++
++    snd_copy_send_sco_data(pSCOSnd);
++}
++
++#endif
++
++#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0))
++int btusb_send_frame(struct sk_buff *skb)
++{
++    struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++#else
++int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
++{
++#endif
++    //struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    struct usb_ctrlrequest *dr;
++    struct urb *urb;
++    unsigned int pipe;
++    int err = 0;
++    int retries = 0;
++    u16 *opcode = NULL;
++
++    AICBT_DBG("%s: hdev %p, btusb data %p, pkt type %d",
++            __func__, hdev, data, bt_cb(skb)->pkt_type);
++
++    //printk("aic %d %d\r\n", bt_cb(skb)->pkt_type, skb->len);
++    if (!test_bit(HCI_RUNNING, &hdev->flags))
++        return -EBUSY;
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
++      skb->dev = (void *)hdev;
++#endif
++#endif
++
++    switch (bt_cb(skb)->pkt_type) {
++    case HCI_COMMAND_PKT:
++        print_command(skb);
++        urb = usb_alloc_urb(0, GFP_ATOMIC);
++        if (!urb)
++            return -ENOMEM;
++
++        dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
++        if (!dr) {
++            usb_free_urb(urb);
++            return -ENOMEM;
++        }
++
++        dr->bRequestType = data->cmdreq_type;
++        dr->bRequest     = 0;
++        dr->wIndex       = 0;
++        dr->wValue       = 0;
++        dr->wLength      = __cpu_to_le16(skb->len);
++
++        pipe = usb_sndctrlpipe(data->udev, 0x00);
++
++        usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
++                skb->data, skb->len, btusb_tx_complete, skb);
++
++        hdev->stat.cmd_tx++;
++        break;
++
++    case HCI_ACLDATA_PKT:
++        if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
++            print_command(skb);
++            opcode = (u16*)(skb->data);
++            printk("aic cmd:0x%04x", *opcode);
++        } else {
++            print_acl(skb, 1);
++        }
++        if (!data->bulk_tx_ep)
++            return -ENODEV;
++
++        urb = usb_alloc_urb(0, GFP_ATOMIC);
++        if (!urb)
++            return -ENOMEM;
++
++        pipe = usb_sndbulkpipe(data->udev,
++                    data->bulk_tx_ep->bEndpointAddress);
++
++              usb_fill_bulk_urb(urb, data->udev, pipe,
++                      skb->data, skb->len, btusb_tx_complete, skb);
++
++        hdev->stat.acl_tx++;
++        break;
++
++    case HCI_SCODATA_PKT:
++        print_sco(skb, 1);
++        if (!data->isoc_tx_ep || SCO_NUM < 1) {
++            kfree(skb);
++            return -ENODEV;
++        }
++
++        urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
++        if (!urb) {
++            AICBT_ERR("%s: Failed to allocate mem for sco pkts", __func__);
++            kfree(skb);
++            return -ENOMEM;
++        }
++
++        pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
++
++        usb_fill_int_urb(urb, data->udev, pipe,
++                skb->data, skb->len, btusb_isoc_tx_complete,
++                skb, data->isoc_tx_ep->bInterval);
++
++        urb->transfer_flags  = URB_ISO_ASAP;
++
++        fill_isoc_descriptor(urb, skb->len,
++                le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
++
++        hdev->stat.sco_tx++;
++        goto skip_waking;
++
++    default:
++        return -EILSEQ;
++    }
++
++    err = inc_tx(data);
++    if (err) {
++        usb_anchor_urb(urb, &data->deferred);
++        schedule_work(&data->waker);
++        err = 0;
++        goto done;
++    }
++
++skip_waking:
++    usb_anchor_urb(urb, &data->tx_anchor);
++retry:
++    err = usb_submit_urb(urb, GFP_ATOMIC);
++    if (err < 0) {
++        AICBT_ERR("%s: Failed to submit urb %p, pkt type %d, err %d, retries %d",
++                __func__, urb, bt_cb(skb)->pkt_type, err, retries);
++        if ((bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) && (retries < 10)) {
++            mdelay(1);
++
++            if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)
++                print_error_command(skb);
++            retries++;
++            goto retry;
++        }
++        kfree(urb->setup_packet);
++        usb_unanchor_urb(urb);
++    } else
++        usb_mark_last_busy(data->udev);
++    usb_free_urb(urb);
++
++done:
++    return err;
++}
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++static void btusb_destruct(struct hci_dev *hdev)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++
++    AICBT_DBG("%s: name %s", __func__, hdev->name);
++
++    kfree(data);
++}
++#endif
++
++static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++
++    AICBT_DBG("%s: name %s, evt %d", __func__, hdev->name, evt);
++
++    if (SCO_NUM != data->sco_num) {
++        data->sco_num = SCO_NUM;
++        schedule_work(&data->work);
++    }
++}
++
++static inline int set_isoc_interface(struct hci_dev *hdev, int altsetting)
++{
++    struct btusb_data *data = GET_DRV_DATA(hdev);
++    struct usb_interface *intf = data->isoc;
++    struct usb_endpoint_descriptor *ep_desc;
++    int i, err;
++
++    if (!data->isoc)
++        return -ENODEV;
++
++    err = usb_set_interface(data->udev, 1, altsetting);
++    if (err < 0) {
++        AICBT_ERR("%s: Failed to set interface, altsetting %d, err %d",
++                __func__, altsetting, err);
++        return err;
++    }
++
++    data->isoc_altsetting = altsetting;
++
++    data->isoc_tx_ep = NULL;
++    data->isoc_rx_ep = NULL;
++
++    for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
++        ep_desc = &intf->cur_altsetting->endpoint[i].desc;
++
++        if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
++            data->isoc_tx_ep = ep_desc;
++            continue;
++        }
++
++        if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
++            data->isoc_rx_ep = ep_desc;
++            continue;
++        }
++    }
++
++    if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
++        AICBT_ERR("%s: Invalid SCO descriptors", __func__);
++        return -ENODEV;
++    }
++
++      AICBT_ERR("%s: hdev->reassembly implemant\r\n",
++                      __func__);
++
++#if CONFIG_BLUEDROID
++    if(hdev->reassembly[HCI_SCODATA_PKT - 1]) {
++        kfree_skb(hdev->reassembly[HCI_SCODATA_PKT - 1]);
++        hdev->reassembly[HCI_SCODATA_PKT - 1] = NULL;
++    }
++#endif
++    return 0;
++}
++
++static void set_select_msbc(enum CODEC_TYPE type)
++{
++    printk("%s codec type = %d", __func__, (int)type);
++    codec_type = type;
++}
++
++static enum CODEC_TYPE check_select_msbc(void)
++{
++    return codec_type;
++}
++
++#ifdef CONFIG_SCO_OVER_HCI
++static int check_controller_support_msbc( struct usb_device *udev)
++{
++    //fix this in the future,when new card support msbc decode and encode
++    AICBT_INFO("%s:pid = 0x%02x, vid = 0x%02x",__func__,udev->descriptor.idProduct, udev->descriptor.idVendor);
++    switch (udev->descriptor.idProduct) {
++
++        default:
++          return 0;
++    }
++    return 0;
++}
++#endif
++static void btusb_work(struct work_struct *work)
++{
++    struct btusb_data *data = container_of(work, struct btusb_data, work);
++    struct hci_dev *hdev = data->hdev;
++    int err;
++    int new_alts;
++#ifdef CONFIG_SCO_OVER_HCI
++    AIC_sco_card_t  *pSCOSnd = data->pSCOSnd;
++#endif
++      printk("%s data->sco_num:%d \r\n", __func__, data->sco_num);
++      
++    if (data->sco_num > 0) {
++        if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
++            err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
++            if (err < 0) {
++                clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++                mdelay(URB_CANCELING_DELAY_MS);
++                usb_kill_anchored_urbs(&data->isoc_anchor);
++                              printk("%s usb_kill_anchored_urbs after \r\n", __func__);
++                return;
++            }
++
++            set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
++        }
++
++      hdev->voice_setting = 93;
++        AICBT_INFO("%s voice settings = 0x%04x", __func__, hdev->voice_setting);
++        if (!(hdev->voice_setting & 0x0003)) {
++            if(data->sco_num == 1)
++                if(check_select_msbc()) {
++                    new_alts = 1;
++                } else {
++                    new_alts = 2;
++                }
++            else {
++              AICBT_INFO("%s: we don't support mutiple sco link for cvsd", __func__);
++              return;
++            }
++        } else{
++            if(check_select_msbc()) {
++                if(data->sco_num == 1)
++                    new_alts = 1;
++                else {
++                    AICBT_INFO("%s: we don't support mutiple sco link for msbc", __func__);
++                    return;
++                }
++            } else {
++                new_alts = 2;
++            }
++        }
++        if (data->isoc_altsetting != new_alts) {
++
++            clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++            mdelay(URB_CANCELING_DELAY_MS);
++            usb_kill_anchored_urbs(&data->isoc_anchor);
++
++                      printk("%s set_isoc_interface in \r\n", __func__);
++            if (set_isoc_interface(hdev, new_alts) < 0)
++                return;
++                      
++        }
++              
++              printk("%s set_isoc_interface out \r\n", __func__);
++
++        if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
++                      printk("%s btusb_submit_isoc_urb\r\n", __func__);
++            if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
++                clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++            else
++                btusb_submit_isoc_urb(hdev, GFP_KERNEL);
++        }
++#ifdef CONFIG_SCO_OVER_HCI
++        if(test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
++            set_bit(USB_CAPTURE_RUNNING, &data->pSCOSnd->states);
++            set_bit(USB_PLAYBACK_RUNNING, &data->pSCOSnd->states);
++        }
++        if (test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++            schedule_work(&pSCOSnd->send_sco_work);
++            AICBT_INFO("%s: play_timer restart", __func__);
++        }
++#endif
++    } else {
++        clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++#ifdef CONFIG_SCO_OVER_HCI
++        clear_bit(USB_CAPTURE_RUNNING, &data->pSCOSnd->states);
++        clear_bit(USB_PLAYBACK_RUNNING, &data->pSCOSnd->states);
++              //AIC_sco_card_t        *pSCOSnd = data->pSCOSnd;
++              if (test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++                      mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(30));
++                      AICBT_INFO("%s: play_timer start", __func__);
++              }
++#endif
++        mdelay(URB_CANCELING_DELAY_MS);
++        usb_kill_anchored_urbs(&data->isoc_anchor);
++
++        set_isoc_interface(hdev, 0);
++        if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
++            usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
++    }
++}
++
++static void btusb_waker(struct work_struct *work)
++{
++    struct btusb_data *data = container_of(work, struct btusb_data, waker);
++    int err;
++
++    AICBT_DBG("%s", __func__);
++
++    err = usb_autopm_get_interface(data->intf);
++    if (err < 0)
++        return;
++
++    usb_autopm_put_interface(data->intf);
++}
++
++int bt_pm_notify(struct notifier_block *notifier, ulong pm_event, void *unused)
++{
++    struct btusb_data *data;
++    firmware_info *fw_info;
++    struct usb_device *udev;
++
++    AICBT_INFO("%s: pm event %ld", __func__, pm_event);
++
++    data = container_of(notifier, struct btusb_data, pm_notifier);
++    fw_info = data->fw_info;
++    udev = fw_info->udev;
++
++    switch (pm_event) {
++    case PM_SUSPEND_PREPARE:
++    case PM_HIBERNATION_PREPARE:
++#if 0
++        patch_entry->fw_len = load_firmware(fw_info, &patch_entry->fw_cache);
++        if (patch_entry->fw_len <= 0) {
++        /* We may encount failure in loading firmware, just give a warning */
++            AICBT_WARN("%s: Failed to load firmware", __func__);
++        }
++#endif
++        if (!device_may_wakeup(&udev->dev)) {
++#if (CONFIG_RESET_RESUME || CONFIG_BLUEDROID)
++            AICBT_INFO("%s:remote wakeup not supported, reset resume supported", __func__);
++#else
++            fw_info->intf->needs_binding = 1;
++            AICBT_INFO("%s:remote wakeup not supported, binding needed", __func__);
++#endif
++        }
++        break;
++
++    case PM_POST_SUSPEND:
++    case PM_POST_HIBERNATION:
++    case PM_POST_RESTORE:
++#if 0
++        /* Reclaim fw buffer when bt usb resumed */
++        if (patch_entry->fw_len > 0) {
++            kfree(patch_entry->fw_cache);
++            patch_entry->fw_cache = NULL;
++            patch_entry->fw_len = 0;
++        }
++#endif
++
++#if BTUSB_RPM
++        usb_disable_autosuspend(udev);
++        usb_enable_autosuspend(udev);
++        pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
++#endif
++        break;
++
++    default:
++        break;
++    }
++
++    return NOTIFY_DONE;
++}
++
++int bt_reboot_notify(struct notifier_block *notifier, ulong pm_event, void *unused)
++{
++    struct btusb_data *data;
++    firmware_info *fw_info;
++    struct usb_device *udev;
++
++    AICBT_INFO("%s: pm event %ld", __func__, pm_event);
++
++    data = container_of(notifier, struct btusb_data, reboot_notifier);
++    fw_info = data->fw_info;
++    udev = fw_info->udev;
++
++    switch (pm_event) {
++    case SYS_DOWN:
++        AICBT_DBG("%s:system down or restart", __func__);
++    break;
++
++    case SYS_HALT:
++    case SYS_POWER_OFF:
++#if SUSPNED_DW_FW
++        cancel_work_sync(&data->work);
++
++        btusb_stop_traffic(data);
++        mdelay(URB_CANCELING_DELAY_MS);
++        usb_kill_anchored_urbs(&data->tx_anchor);
++
++
++        if(fw_info_4_suspend) {
++            download_suspend_patch(fw_info_4_suspend,1);
++        }
++          else
++                  AICBT_ERR("%s: Failed to download suspend fw", __func__);
++#endif
++
++#ifdef SET_WAKEUP_DEVICE
++        set_wakeup_device_from_conf(fw_info_4_suspend);
++#endif
++        AICBT_DBG("%s:system halt or power off", __func__);
++    break;
++
++    default:
++        break;
++    }
++
++    return NOTIFY_DONE;
++}
++
++
++#ifdef CONFIG_SCO_OVER_HCI
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++void aic_snd_capture_timeout(ulong data)
++#else
++void aic_snd_capture_timeout(struct timer_list *t)
++#endif
++{
++      uint8_t null_data[255];
++      struct btusb_data *usb_data;
++      
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++    usb_data = (struct btusb_data *)data;
++#else
++    usb_data = &snd_cap_timer.snd_usb_data;
++#endif
++    aic_copy_capture_data_to_alsa(usb_data, null_data, snd_cap_timer.snd_sco_length/2);
++      //printk("%s enter\r\n", __func__);
++    mod_timer(&snd_cap_timer.cap_timer,jiffies + msecs_to_jiffies(3));
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++void aic_snd_play_timeout(ulong data)
++#else
++void aic_snd_play_timeout(struct timer_list *t)
++#endif
++{
++      AIC_sco_card_t *pSCOSnd;
++      struct snd_pcm_runtime *runtime;
++      snd_pcm_uframes_t period_size;
++    int count;
++      struct btusb_data *usb_data;
++      int sco_packet_bytes;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++    usb_data = (struct btusb_data *)data;
++#else
++    usb_data = &snd_cap_timer.snd_usb_data;
++#endif
++      pSCOSnd = usb_data->pSCOSnd;
++
++      if(test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++              return;
++      }
++
++      if(!test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++              return;
++      }
++
++      runtime = pSCOSnd->playback.substream->runtime;
++      period_size = runtime->period_size;
++    sco_packet_bytes = pSCOSnd->playback.sco_packet_bytes;
++    count = frames_to_bytes(runtime, period_size)/sco_packet_bytes;
++
++    pSCOSnd->playback.buffer_pos += period_size;
++    if ( pSCOSnd->playback.buffer_pos >= runtime->buffer_size)
++       pSCOSnd->playback.buffer_pos -= runtime->buffer_size;
++
++    if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++        snd_pcm_period_elapsed(pSCOSnd->playback.substream);
++    }
++    //AICBT_DBG("%s,play_timer restart buffer_pos:%d sco_handle:%d sco_packet_bytes:%d count:%d", __FUNCTION__, pSCOSnd->playback.buffer_pos, pSCOSnd->usb_data->sco_handle,
++    //sco_packet_bytes, count);
++    mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(3*count));
++}
++
++static const struct snd_pcm_hardware snd_card_sco_capture_default =
++{
++    .info               = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_NONINTERLEAVED |
++                            SNDRV_PCM_ACCESS_RW_INTERLEAVED | SNDRV_PCM_INFO_FIFO_IN_FRAMES),
++    .formats            = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
++    .rates              = (SNDRV_PCM_RATE_8000),
++    .rate_min           = 8000,
++    .rate_max           = 8000,
++    .channels_min       = 1,
++    .channels_max       = 1,
++    .buffer_bytes_max   = 8 * 768,
++    .period_bytes_min   = 48,
++    .period_bytes_max   = 768,
++    .periods_min        = 1,
++    .periods_max        = 8,
++    .fifo_size          = 8,
++
++};
++
++static int snd_sco_capture_pcm_open(struct snd_pcm_substream * substream)
++{
++    AIC_sco_card_t  *pSCOSnd = substream->private_data;
++
++    AICBT_INFO("%s", __FUNCTION__);
++    pSCOSnd->capture.substream = substream;
++
++    memcpy(&substream->runtime->hw, &snd_card_sco_capture_default, sizeof(struct snd_pcm_hardware));
++      pSCOSnd->capture.buffer_pos = 0;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++      init_timer(&snd_cap_timer.cap_timer);
++      snd_cap_timer.cap_timer.data = (unsigned long)pSCOSnd->usb_data;
++      snd_cap_timer.cap_timer.function = aic_snd_capture_timeout;
++#else
++      timer_setup(&snd_cap_timer.cap_timer, aic_snd_capture_timeout, 0);
++      snd_cap_timer.snd_usb_data = *(pSCOSnd->usb_data);
++#endif
++
++    if(check_controller_support_msbc(pSCOSnd->dev)) {
++        substream->runtime->hw.rates |= SNDRV_PCM_RATE_16000;
++        substream->runtime->hw.rate_max = 16000;
++        substream->runtime->hw.period_bytes_min = 96;
++        substream->runtime->hw.period_bytes_max = 16 * 96;
++        substream->runtime->hw.buffer_bytes_max = 8 * 16 * 96;
++    }
++    set_bit(ALSA_CAPTURE_OPEN, &pSCOSnd->states);
++    return 0;
++}
++
++static int snd_sco_capture_pcm_close(struct snd_pcm_substream *substream)
++{
++      AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++      del_timer(&snd_cap_timer.cap_timer);
++      clear_bit(ALSA_CAPTURE_OPEN, &pSCOSnd->states);
++      return 0;
++}
++
++static int snd_sco_capture_ioctl(struct snd_pcm_substream *substream,  unsigned int cmd, void *arg)
++{
++    AICBT_DBG("%s, cmd = %d", __FUNCTION__, cmd);
++    switch (cmd)
++    {
++        default:
++            return snd_pcm_lib_ioctl(substream, cmd, arg);
++    }
++    return 0;
++}
++
++static int snd_sco_capture_pcm_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params)
++{
++
++    int err;
++    struct snd_pcm_runtime *runtime = substream->runtime;
++    err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
++    AICBT_INFO("%s,err : %d,  runtime state : %d", __FUNCTION__, err, runtime->status->state);
++    return err;
++}
++
++static int snd_sco_capture_pcm_hw_free(struct snd_pcm_substream * substream)
++{
++    AICBT_DBG("%s", __FUNCTION__);
++    return snd_pcm_lib_free_vmalloc_buffer(substream);;
++}
++
++static int snd_sco_capture_pcm_prepare(struct snd_pcm_substream *substream)
++{
++    AIC_sco_card_t *pSCOSnd = substream->private_data;
++    struct snd_pcm_runtime *runtime = substream->runtime;
++
++    AICBT_INFO("%s %d\n", __FUNCTION__, (int)runtime->period_size);
++    if (test_bit(DISCONNECTED, &pSCOSnd->states))
++                  return -ENODEV;
++        if (!test_bit(USB_CAPTURE_RUNNING, &pSCOSnd->states))
++                  return -EIO;
++
++    if(runtime->rate == 8000) {
++        if(pSCOSnd->usb_data->isoc_altsetting != 2)
++            return -ENOEXEC;
++        pSCOSnd->capture.sco_packet_bytes = 48;
++    }
++    else if(runtime->rate == 16000 && check_controller_support_msbc(pSCOSnd->dev)) {
++        if(pSCOSnd->usb_data->isoc_altsetting != 4)
++            return -ENOEXEC;
++        pSCOSnd->capture.sco_packet_bytes = 96;
++    }
++    else if(pSCOSnd->usb_data->isoc_altsetting == 2) {
++        pSCOSnd->capture.sco_packet_bytes = 48;
++    }
++    else if(pSCOSnd->usb_data->isoc_altsetting == 1) {
++        pSCOSnd->capture.sco_packet_bytes = 24;
++    }
++    return 0;
++}
++
++static int snd_sco_capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++        AIC_sco_card_t *pSCOSnd = substream->private_data;
++    AICBT_INFO("%s, cmd : %d", __FUNCTION__, cmd);
++
++        switch (cmd) {
++          case SNDRV_PCM_TRIGGER_START:
++                    if (!test_bit(USB_CAPTURE_RUNNING, &pSCOSnd->states))
++                            return -EIO;
++                    set_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states);
++                    return 0;
++          case SNDRV_PCM_TRIGGER_STOP:
++                    clear_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states);
++                    return 0;
++          default:
++                    return -EINVAL;
++        }
++}
++
++static snd_pcm_uframes_t snd_sco_capture_pcm_pointer(struct snd_pcm_substream *substream)
++{
++        AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++        return pSCOSnd->capture.buffer_pos;
++}
++
++
++static struct snd_pcm_ops snd_sco_capture_pcm_ops = {
++      .open =         snd_sco_capture_pcm_open,
++      .close =        snd_sco_capture_pcm_close,
++      .ioctl =        snd_sco_capture_ioctl,
++      .hw_params =    snd_sco_capture_pcm_hw_params,
++      .hw_free =      snd_sco_capture_pcm_hw_free,
++      .prepare =      snd_sco_capture_pcm_prepare,
++      .trigger =      snd_sco_capture_pcm_trigger,
++      .pointer =      snd_sco_capture_pcm_pointer,
++};
++
++
++static const struct snd_pcm_hardware snd_card_sco_playback_default =
++{
++    .info               = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_NONINTERLEAVED |
++                            SNDRV_PCM_ACCESS_RW_INTERLEAVED | SNDRV_PCM_INFO_FIFO_IN_FRAMES),
++    .formats            = SNDRV_PCM_FMTBIT_S16_LE,
++    .rates              = (SNDRV_PCM_RATE_8000),
++    .rate_min           = 8000,
++    .rate_max           = 8000,
++    .channels_min       = 1,
++    .channels_max       = 1,
++    .buffer_bytes_max   = 8 * 768,
++    .period_bytes_min   = 48,
++    .period_bytes_max   = 768,
++    .periods_min        = 1,
++    .periods_max        = 8,
++    .fifo_size          = 8,
++};
++
++static int snd_sco_playback_pcm_open(struct snd_pcm_substream * substream)
++{
++    AIC_sco_card_t *pSCOSnd = substream->private_data;
++    int err = 0;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
++      init_timer(&snd_cap_timer.play_timer);
++      snd_cap_timer.play_timer.data = (unsigned long)pSCOSnd->usb_data;
++      snd_cap_timer.play_timer.function = aic_snd_play_timeout;
++#else
++      timer_setup(&snd_cap_timer.play_timer, aic_snd_play_timeout, 0);
++      snd_cap_timer.snd_usb_data = *(pSCOSnd->usb_data);
++#endif
++      pSCOSnd->playback.buffer_pos = 0;
++
++    AICBT_INFO("%s, rate : %d", __FUNCTION__, substream->runtime->rate);
++    memcpy(&substream->runtime->hw, &snd_card_sco_playback_default, sizeof(struct snd_pcm_hardware));
++    if(check_controller_support_msbc(pSCOSnd->dev)) {
++        substream->runtime->hw.rates |= SNDRV_PCM_RATE_16000;
++        substream->runtime->hw.rate_max = 16000;
++        substream->runtime->hw.period_bytes_min = 96;
++        substream->runtime->hw.period_bytes_max = 16 * 96;
++        substream->runtime->hw.buffer_bytes_max = 8 * 16 * 96;
++    }
++    pSCOSnd->playback.substream = substream;
++    set_bit(ALSA_PLAYBACK_OPEN, &pSCOSnd->states);
++
++    return err;
++}
++
++static int snd_sco_playback_pcm_close(struct snd_pcm_substream *substream)
++{
++    AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++      del_timer(&snd_cap_timer.play_timer);
++      AICBT_INFO("%s: play_timer delete", __func__);
++      clear_bit(ALSA_PLAYBACK_OPEN, &pSCOSnd->states);
++    cancel_work_sync(&pSCOSnd->send_sco_work);
++        return 0;
++}
++
++static int snd_sco_playback_ioctl(struct snd_pcm_substream *substream,  unsigned int cmd, void *arg)
++{
++    AICBT_DBG("%s, cmd : %d", __FUNCTION__, cmd);
++    switch (cmd)
++    {
++        default:
++            return snd_pcm_lib_ioctl(substream, cmd, arg);
++            break;
++    }
++    return 0;
++}
++
++static int snd_sco_playback_pcm_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params)
++{
++    int err;
++    err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
++    return err;
++}
++
++static int snd_sco_palyback_pcm_hw_free(struct snd_pcm_substream * substream)
++{
++    AICBT_DBG("%s", __FUNCTION__);
++    return snd_pcm_lib_free_vmalloc_buffer(substream);
++}
++
++static int snd_sco_playback_pcm_prepare(struct snd_pcm_substream *substream)
++{
++        AIC_sco_card_t *pSCOSnd = substream->private_data;
++    struct snd_pcm_runtime *runtime = substream->runtime;
++
++    AICBT_INFO("%s, bound_rate = %d", __FUNCTION__, runtime->rate);
++
++        if (test_bit(DISCONNECTED, &pSCOSnd->states))
++                  return -ENODEV;
++        if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states))
++                  return -EIO;
++
++    if(runtime->rate == 8000) {
++        if(pSCOSnd->usb_data->isoc_altsetting != 2)
++            return -ENOEXEC;
++        pSCOSnd->playback.sco_packet_bytes = 48;
++    }
++    else if(runtime->rate == 16000) {
++        if(pSCOSnd->usb_data->isoc_altsetting != 4)
++            return -ENOEXEC;
++        pSCOSnd->playback.sco_packet_bytes = 96;
++    }
++
++      return 0;
++}
++
++static int snd_sco_playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++      AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++    AICBT_INFO("%s, cmd = %d", __FUNCTION__, cmd);
++      switch (cmd) {
++              case SNDRV_PCM_TRIGGER_START:
++                      if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states))
++                              return -EIO;
++                      set_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states);
++          schedule_work(&pSCOSnd->send_sco_work);
++#ifdef CONFIG_SCO_OVER_HCI
++                if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states)) {
++                        AICBT_INFO("%s: play_timer cmd 1 start ", __func__);
++                        mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(3));
++                }
++#endif
++                      return 0;
++              case SNDRV_PCM_TRIGGER_STOP:
++                      clear_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states);
++                      return 0;
++              default:
++                      return -EINVAL;
++      }
++}
++
++static snd_pcm_uframes_t snd_sco_playback_pcm_pointer(struct snd_pcm_substream *substream)
++{
++      AIC_sco_card_t *pSCOSnd = substream->private_data;
++
++      return pSCOSnd->playback.buffer_pos;
++}
++
++
++static struct snd_pcm_ops snd_sco_playback_pcm_ops = {
++      .open =         snd_sco_playback_pcm_open,
++      .close =        snd_sco_playback_pcm_close,
++      .ioctl =        snd_sco_playback_ioctl,
++      .hw_params =    snd_sco_playback_pcm_hw_params,
++      .hw_free =      snd_sco_palyback_pcm_hw_free,
++      .prepare =      snd_sco_playback_pcm_prepare,
++      .trigger =      snd_sco_playback_pcm_trigger,
++      .pointer =      snd_sco_playback_pcm_pointer,
++};
++
++
++static AIC_sco_card_t* btusb_snd_init(struct usb_interface *intf, const struct usb_device_id *id, struct btusb_data *data)
++{
++    struct snd_card *card;
++    AIC_sco_card_t  *pSCOSnd;
++    int err=0;
++    AICBT_INFO("%s", __func__);
++    err = snd_card_new(&intf->dev,
++     -1, AIC_SCO_ID, THIS_MODULE,
++     sizeof(AIC_sco_card_t), &card);
++    if (err < 0) {
++        AICBT_ERR("%s: sco snd card create fail", __func__);
++        return NULL;
++    }
++    // private data
++    pSCOSnd = (AIC_sco_card_t *)card->private_data;
++    pSCOSnd->card = card;
++    pSCOSnd->dev = interface_to_usbdev(intf);
++    pSCOSnd->usb_data = data;
++
++    strcpy(card->driver, AIC_SCO_ID);
++    strcpy(card->shortname, "Aicsemi sco snd");
++    sprintf(card->longname, "Aicsemi sco over hci: VID:0x%04x, PID:0x%04x",
++        id->idVendor, pSCOSnd->dev->descriptor.idProduct);
++
++    err = snd_pcm_new(card, AIC_SCO_ID, 0, 1, 1, &pSCOSnd->pcm);
++    if (err < 0) {
++        AICBT_ERR("%s: sco snd card new pcm fail", __func__);
++        return NULL;
++    }
++    pSCOSnd->pcm->private_data = pSCOSnd;
++    sprintf(pSCOSnd->pcm->name, "sco_pcm:VID:0x%04x, PID:0x%04x",
++      id->idVendor, pSCOSnd->dev->descriptor.idProduct);
++
++    snd_pcm_set_ops(pSCOSnd->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sco_playback_pcm_ops);
++    snd_pcm_set_ops(pSCOSnd->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sco_capture_pcm_ops);
++
++    err = snd_card_register(card);
++    if (err < 0) {
++        AICBT_ERR("%s: sco snd card register card fail", __func__);
++        return NULL;
++    }
++
++    spin_lock_init(&pSCOSnd->capture_lock);
++    spin_lock_init(&pSCOSnd->playback_lock);
++    INIT_WORK(&pSCOSnd->send_sco_work, playback_work);
++    return pSCOSnd;
++}
++#endif
++
++static int aicwf_usb_chipmatch(u16 vid, u16 pid){
++
++      if(pid == USB_PRODUCT_ID_AIC8801){
++              g_chipid = PRODUCT_ID_AIC8801;
++              printk("%s USE AIC8801\r\n", __func__);
++              return 0;
++      }else if(pid == USB_PRODUCT_ID_AIC8800DC){
++              g_chipid = PRODUCT_ID_AIC8800DC;
++              printk("%s USE AIC8800DC\r\n", __func__);
++              return 0;
++      }else if(pid == USB_PRODUCT_ID_AIC8800D80){
++                g_chipid = PRODUCT_ID_AIC8800D80;
++                printk("%s USE AIC8800D80\r\n", __func__);
++                return 0;
++      }else{
++              return -1;
++      }
++}
++
++
++static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
++{
++    struct usb_device *udev = interface_to_usbdev(intf);
++    struct usb_endpoint_descriptor *ep_desc;
++    u8 endpoint_num;
++    struct btusb_data *data;
++    struct hci_dev *hdev;
++    firmware_info *fw_info;
++    int i, err=0;
++
++    bt_support = 1;
++    
++    AICBT_INFO("%s: usb_interface %p, bInterfaceNumber %d, idVendor 0x%04x, "
++            "idProduct 0x%04x", __func__, intf,
++            intf->cur_altsetting->desc.bInterfaceNumber,
++            id->idVendor, id->idProduct);
++
++      aicwf_usb_chipmatch(id->idVendor, id->idProduct);
++
++    /* interface numbers are hardcoded in the spec */
++    if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++        return -ENODEV;
++
++    AICBT_DBG("%s: can wakeup = %x, may wakeup = %x", __func__,
++            device_can_wakeup(&udev->dev), device_may_wakeup(&udev->dev));
++
++    data = aic_alloc(intf);
++    if (!data)
++        return -ENOMEM;
++
++    for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
++        ep_desc = &intf->cur_altsetting->endpoint[i].desc;
++
++        endpoint_num = usb_endpoint_num(ep_desc);
++        printk("endpoint num %d\n", endpoint_num);
++
++       if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
++            data->intr_ep = ep_desc;
++            continue;
++        }
++
++        if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
++            data->bulk_tx_ep = ep_desc;
++            continue;
++        }
++
++        if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
++            data->bulk_rx_ep = ep_desc;
++            continue;
++        }
++    }
++
++    if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
++        aic_free(data);
++        return -ENODEV;
++    }
++
++    data->cmdreq_type = USB_TYPE_CLASS;
++
++    data->udev = udev;
++    data->intf = intf;
++
++    dlfw_dis_state = 0;
++    spin_lock_init(&queue_lock);
++    spin_lock_init(&dlfw_lock);
++    spin_lock_init(&data->lock);
++
++    INIT_WORK(&data->work, btusb_work);
++    INIT_WORK(&data->waker, btusb_waker);
++    spin_lock_init(&data->txlock);
++
++    init_usb_anchor(&data->tx_anchor);
++    init_usb_anchor(&data->intr_anchor);
++    init_usb_anchor(&data->bulk_anchor);
++    init_usb_anchor(&data->isoc_anchor);
++    init_usb_anchor(&data->deferred);
++
++#if (CONFIG_BLUEDROID == 0)
++#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
++              spin_lock_init(&data->rxlock);
++              data->recv_bulk = btusb_recv_bulk;
++#endif
++#endif
++
++
++    fw_info = firmware_info_init(intf);
++    if (fw_info)
++        data->fw_info = fw_info;
++    else {
++        AICBT_WARN("%s: Failed to initialize fw info", __func__);
++        /* Skip download patch */
++        goto end;
++    }
++
++    AICBT_INFO("%s: download begining...", __func__);
++
++#if CONFIG_BLUEDROID
++    mutex_lock(&btchr_mutex);
++#endif
++      if(g_chipid == PRODUCT_ID_AIC8800DC){
++              err = download_patch(data->fw_info,1);
++      }
++
++#if CONFIG_BLUEDROID
++    mutex_unlock(&btchr_mutex);
++#endif
++
++    AICBT_INFO("%s: download ending...", __func__);
++      if (err < 0) {
++              return err;
++      }
++
++
++    hdev = hci_alloc_dev();
++    if (!hdev) {
++        aic_free(data);
++        data = NULL;
++        return -ENOMEM;
++    }
++
++    HDEV_BUS = HCI_USB;
++
++    data->hdev = hdev;
++
++    SET_HCIDEV_DEV(hdev, &intf->dev);
++
++    hdev->open     = btusb_open;
++    hdev->close    = btusb_close;
++    hdev->flush    = btusb_flush;
++    hdev->send     = btusb_send_frame;
++    hdev->notify   = btusb_notify;
++#if (CONFIG_BLUEDROID == 0)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 9)
++    hdev->shutdown = btusb_shutdown;
++#endif
++#endif //(CONFIG_BLUEDROIF == 0)
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0)
++    hci_set_drvdata(hdev, data);
++#else
++    hdev->driver_data = data;
++    hdev->destruct = btusb_destruct;
++    hdev->owner = THIS_MODULE;
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
++    if (!reset_on_close){
++        /* set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); */
++        AICBT_DBG("%s: Set HCI_QUIRK_RESET_ON_CLOSE", __func__);
++    }
++#endif
++
++    /* Interface numbers are hardcoded in the specification */
++    data->isoc = usb_ifnum_to_if(data->udev, 1);
++    if (data->isoc) {
++        err = usb_driver_claim_interface(&btusb_driver,
++                            data->isoc, data);
++        if (err < 0) {
++            hci_free_dev(hdev);
++            hdev = NULL;
++            aic_free(data);
++            data = NULL;
++            return err;
++        }
++#ifdef CONFIG_SCO_OVER_HCI
++        data->pSCOSnd = btusb_snd_init(intf, id, data);
++#endif
++    }
++
++    err = hci_register_dev(hdev);
++    if (err < 0) {
++        hci_free_dev(hdev);
++        hdev = NULL;
++        aic_free(data);
++        data = NULL;
++        return err;
++    }
++
++    usb_set_intfdata(intf, data);
++
++//#ifdef CONFIG_HAS_EARLYSUSPEND
++#if 0
++    data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
++    data->early_suspend.suspend = btusb_early_suspend;
++    data->early_suspend.resume = btusb_late_resume;
++    register_early_suspend(&data->early_suspend);
++#else
++    data->pm_notifier.notifier_call = bt_pm_notify;
++    data->reboot_notifier.notifier_call = bt_reboot_notify;
++    register_pm_notifier(&data->pm_notifier);
++    register_reboot_notifier(&data->reboot_notifier);
++#endif
++
++#if CONFIG_BLUEDROID
++    AICBT_INFO("%s: Check bt reset flag %d", __func__, bt_reset);
++    /* Report hci hardware error after everthing is ready,
++     * especially hci register is completed. Or, btchr_poll
++     * will get null hci dev when hotplug in.
++     */
++    if (bt_reset == 1) {
++        hci_hardware_error();
++        bt_reset = 0;
++    } else
++        bt_reset = 0; /* Clear and reset it anyway */
++#endif
++
++end:
++    return 0;
++}
++
++static void btusb_disconnect(struct usb_interface *intf)
++{
++    struct btusb_data *data;
++    struct hci_dev *hdev = NULL;
++#if CONFIG_BLUEDROID
++    wait_event_interruptible(bt_dlfw_wait, (check_set_dlfw_state_value(2) == 2));
++#endif
++
++    bt_support = 0;
++
++    AICBT_INFO("%s: usb_interface %p, bInterfaceNumber %d",
++            __func__, intf, intf->cur_altsetting->desc.bInterfaceNumber);
++
++    data = usb_get_intfdata(intf);
++
++    if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++        return;
++
++    if (data)
++        hdev = data->hdev;
++    else {
++        AICBT_WARN("%s: Failed to get bt usb data[Null]", __func__);
++        return;
++    }
++
++#ifdef CONFIG_SCO_OVER_HCI
++    if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
++        AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
++        if(!pSCOSnd) {
++            AICBT_ERR("%s: sco private data is null", __func__);
++            return;
++        }
++        set_bit(DISCONNECTED, &pSCOSnd->states);
++        snd_card_disconnect(pSCOSnd->card);
++        snd_card_free_when_closed(pSCOSnd->card);
++    }
++#endif
++
++//#ifdef CONFIG_HAS_EARLYSUSPEND
++#if 0
++    unregister_early_suspend(&data->early_suspend);
++#else
++    unregister_pm_notifier(&data->pm_notifier);
++    unregister_reboot_notifier(&data->reboot_notifier);
++#endif
++
++    firmware_info_destroy(intf);
++
++#if CONFIG_BLUEDROID
++    if (test_bit(HCI_RUNNING, &hdev->flags)) {
++        AICBT_INFO("%s: Set BT reset flag", __func__);
++        bt_reset = 1;
++    }
++#endif
++
++    usb_set_intfdata(data->intf, NULL);
++
++    if (data->isoc)
++        usb_set_intfdata(data->isoc, NULL);
++
++    hci_unregister_dev(hdev);
++
++    if (intf == data->isoc)
++        usb_driver_release_interface(&btusb_driver, data->intf);
++    else if (data->isoc)
++        usb_driver_release_interface(&btusb_driver, data->isoc);
++
++#if !CONFIG_BLUEDROID
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++    __hci_dev_put(hdev);
++#endif
++#endif
++
++    hci_free_dev(hdev);
++    aic_free(data);
++    data = NULL;
++    set_dlfw_state_value(0);
++}
++
++#ifdef CONFIG_PM
++static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
++{
++    struct btusb_data *data = usb_get_intfdata(intf);
++    //firmware_info *fw_info = data->fw_info;
++
++    AICBT_INFO("%s: event 0x%x, suspend count %d", __func__,
++            message.event, data->suspend_count);
++
++    if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++        return 0;
++#if 0
++    if (!test_bit(HCI_RUNNING, &data->hdev->flags))
++        set_bt_onoff(fw_info, 1);
++#endif
++    if (data->suspend_count++)
++        return 0;
++
++    spin_lock_irq(&data->txlock);
++    if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
++        set_bit(BTUSB_SUSPENDING, &data->flags);
++        spin_unlock_irq(&data->txlock);
++    } else {
++        spin_unlock_irq(&data->txlock);
++        data->suspend_count--;
++        AICBT_ERR("%s: Failed to enter suspend", __func__);
++        return -EBUSY;
++    }
++
++    cancel_work_sync(&data->work);
++
++    btusb_stop_traffic(data);
++    mdelay(URB_CANCELING_DELAY_MS);
++    usb_kill_anchored_urbs(&data->tx_anchor);
++
++    return 0;
++}
++
++static void play_deferred(struct btusb_data *data)
++{
++    struct urb *urb;
++    int err;
++
++    while ((urb = usb_get_from_anchor(&data->deferred))) {
++        usb_anchor_urb(urb, &data->tx_anchor);
++        err = usb_submit_urb(urb, GFP_ATOMIC);
++        if (err < 0) {
++            AICBT_ERR("%s: Failed to submit urb %p, err %d",
++                    __func__, urb, err);
++            kfree(urb->setup_packet);
++            usb_unanchor_urb(urb);
++        } else {
++            usb_mark_last_busy(data->udev);
++        }
++        usb_free_urb(urb);
++
++        data->tx_in_flight++;
++    }
++    mdelay(URB_CANCELING_DELAY_MS);
++    usb_scuttle_anchored_urbs(&data->deferred);
++}
++
++static int btusb_resume(struct usb_interface *intf)
++{
++    struct btusb_data *data = usb_get_intfdata(intf);
++    struct hci_dev *hdev = data->hdev;
++    int err = 0;
++
++    AICBT_INFO("%s: Suspend count %d", __func__, data->suspend_count);
++
++    if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++        return 0;
++
++    if (--data->suspend_count)
++        return 0;
++
++    #if 0
++    /*check_fw_version to check the status of the BT Controller after USB Resume*/
++    err = check_fw_version(fw_info);
++    if (err !=0)
++    {
++        AICBT_INFO("%s: BT Controller Power OFF And Return hci_hardware_error:%d", __func__, err);
++        hci_hardware_error();
++    }
++    #endif
++
++    AICBT_INFO("%s g_chipid %x\n", __func__, g_chipid);
++    if(g_chipid == PRODUCT_ID_AIC8800DC){
++        if(data->fw_info){
++            err = download_patch(data->fw_info,1);
++        }else{
++            AICBT_WARN("%s: Failed to initialize fw info", __func__);
++        }
++    }
++
++    #if 1
++    if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
++        err = btusb_submit_intr_urb(hdev, GFP_NOIO);
++        if (err < 0) {
++            clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++            goto failed;
++        }
++    }
++    #endif
++
++    if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
++        err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
++        if (err < 0) {
++            clear_bit(BTUSB_BULK_RUNNING, &data->flags);
++            goto failed;
++        }
++
++        btusb_submit_bulk_urb(hdev, GFP_NOIO);
++    }
++
++    if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
++        if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
++            clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
++        else
++            btusb_submit_isoc_urb(hdev, GFP_NOIO);
++    }
++
++    spin_lock_irq(&data->txlock);
++    play_deferred(data);
++    clear_bit(BTUSB_SUSPENDING, &data->flags);
++    spin_unlock_irq(&data->txlock);
++    schedule_work(&data->work);
++
++    return 0;
++
++failed:
++    mdelay(URB_CANCELING_DELAY_MS);
++    usb_scuttle_anchored_urbs(&data->deferred);
++    spin_lock_irq(&data->txlock);
++    clear_bit(BTUSB_SUSPENDING, &data->flags);
++    spin_unlock_irq(&data->txlock);
++
++    return err;
++}
++#endif
++
++static struct usb_driver btusb_driver = {
++    .name        = "aic_btusb",
++    .probe        = btusb_probe,
++    .disconnect    = btusb_disconnect,
++#ifdef CONFIG_PM
++    .suspend    = btusb_suspend,
++    .resume        = btusb_resume,
++#if CONFIG_RESET_RESUME
++    .reset_resume    = btusb_resume,
++#endif
++#endif
++    .id_table    = btusb_table,
++    .supports_autosuspend = 1,
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1)
++    .disable_hub_initiated_lpm = 1,
++#endif
++};
++
++static int __init btusb_init(void)
++{
++    int err;
++
++    AICBT_INFO("AICBT_RELEASE_NAME: %s",AICBT_RELEASE_NAME);
++    AICBT_INFO("AicSemi Bluetooth USB driver module init, version %s", VERSION);
++      AICBT_INFO("RELEASE DATE: 2023_0506_1635 \r\n");
++#if CONFIG_BLUEDROID
++    err = btchr_init();
++    if (err < 0) {
++        /* usb register will go on, even bt char register failed */
++        AICBT_ERR("Failed to register usb char device interfaces");
++    } else
++        bt_char_dev_registered = 1;
++#endif
++    err = usb_register(&btusb_driver);
++    if (err < 0)
++        AICBT_ERR("Failed to register aic bluetooth USB driver");
++    return err;
++}
++
++static void __exit btusb_exit(void)
++{
++    AICBT_INFO("AicSemi Bluetooth USB driver module exit");
++#if CONFIG_BLUEDROID
++    if (bt_char_dev_registered > 0)
++        btchr_exit();
++#endif
++    usb_deregister(&btusb_driver);
++}
++
++module_init(btusb_init);
++module_exit(btusb_exit);
++
++
++module_param(mp_drv_mode, int, 0644);
++MODULE_PARM_DESC(mp_drv_mode, "0: NORMAL; 1: MP MODE");
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
++MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
++#endif
++
++MODULE_AUTHOR("AicSemi Corporation");
++MODULE_DESCRIPTION("AicSemi Bluetooth USB driver version");
++MODULE_VERSION(VERSION);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb.h
+@@ -0,0 +1,753 @@
++/*
++ *
++ *  Aic Bluetooth USB driver
++ *
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; either version 2 of the License, or
++ *  (at your option) any later version.
++ *
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++ *
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/skbuff.h>
++#include <linux/errno.h>
++#include <linux/usb.h>
++#include <linux/cdev.h>
++#include <linux/device.h>
++#include <linux/poll.h>
++
++#include <linux/version.h>
++#include <linux/pm_runtime.h>
++#include <linux/firmware.h>
++#include <linux/suspend.h>
++
++
++#ifdef CONFIG_PLATFORM_UBUNTU
++#define CONFIG_BLUEDROID        0 /* bleuz 0, bluedroid 1 */
++#else
++#define CONFIG_BLUEDROID        1 /* bleuz 0, bluedroid 1 */
++#endif
++
++
++//#define CONFIG_SCO_OVER_HCI
++#define CONFIG_USB_AIC_UART_SCO_DRIVER
++
++#ifdef CONFIG_SCO_OVER_HCI
++#include <linux/usb/audio.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++
++#define AIC_SCO_ID "snd_sco_aic"
++enum {
++      USB_CAPTURE_RUNNING,
++      USB_PLAYBACK_RUNNING,
++      ALSA_CAPTURE_OPEN,
++      ALSA_PLAYBACK_OPEN,
++      ALSA_CAPTURE_RUNNING,
++      ALSA_PLAYBACK_RUNNING,
++      CAPTURE_URB_COMPLETED,
++      PLAYBACK_URB_COMPLETED,
++      DISCONNECTED,
++};
++
++// AIC sound card
++typedef struct AIC_sco_card {
++    struct snd_card *card;
++    struct snd_pcm *pcm;
++    struct usb_device *dev;
++    struct btusb_data *usb_data;
++    unsigned long states;
++    struct aic_sco_stream {
++                  struct snd_pcm_substream *substream;
++                  unsigned int sco_packet_bytes;
++                  snd_pcm_uframes_t buffer_pos;
++        } capture, playback;
++    spinlock_t capture_lock;
++    spinlock_t playback_lock;
++    struct work_struct send_sco_work;
++} AIC_sco_card_t;
++#endif
++/* Some Android system may use standard Linux kernel, while
++ * standard Linux may also implement early suspend feature.
++ * So exclude earysuspend.h from CONFIG_BLUEDROID.
++ */
++#ifdef CONFIG_HAS_EARLYSUSPEND
++#include <linux/earlysuspend.h>
++#endif
++
++#if CONFIG_BLUEDROID
++#else
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/hci.h>
++#endif
++
++
++/***********************************
++** AicSemi - For aic_btusb driver **
++***********************************/
++#define URB_CANCELING_DELAY_MS          10
++/* when OS suspended, module is still powered,usb is not powered,
++ * this may set to 1, and must comply with special patch code.
++ */
++#define CONFIG_RESET_RESUME     1
++#define PRINT_CMD_EVENT         0
++#define PRINT_ACL_DATA          0
++#define PRINT_SCO_DATA          0
++
++#define AICBT_DBG_FLAG          0
++
++#if AICBT_DBG_FLAG
++#define AICBT_DBG(fmt, arg...) printk( "aic_btusb: " fmt "\n" , ## arg)
++#else
++#define AICBT_DBG(fmt, arg...)
++#endif
++
++#define AICBT_INFO(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
++#define AICBT_WARN(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
++#define AICBT_ERR(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
++
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
++#define HDEV_BUS        hdev->bus
++#define USB_RPM            1
++#else
++#define HDEV_BUS        hdev->type
++#define USB_RPM            0
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
++#define NUM_REASSEMBLY 3
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0)
++#define GET_DRV_DATA(x)        hci_get_drvdata(x)
++#else
++#define GET_DRV_DATA(x)        x->driver_data
++#endif
++
++#define SCO_NUM    hdev->conn_hash.sco_num
++
++
++#define BTUSB_RPM        (0 * USB_RPM) /* 1 SS enable; 0 SS disable */
++#define BTUSB_WAKEUP_HOST        0    /* 1  enable; 0  disable */
++#define BTUSB_MAX_ISOC_FRAMES    48
++#define BTUSB_INTR_RUNNING        0
++#define BTUSB_BULK_RUNNING        1
++#define BTUSB_ISOC_RUNNING        2
++#define BTUSB_SUSPENDING        3
++#define BTUSB_DID_ISO_RESUME    4
++
++#define HCI_VENDOR_USB_DISC_HARDWARE_ERROR   0xFF
++
++#define HCI_CMD_READ_BD_ADDR 0x1009
++#define HCI_VENDOR_READ_LMP_VERISION 0x1001
++#define HCI_VENDOR_RESET                       0x0C03
++
++#define DRV_NORMAL_MODE 0
++#define DRV_MP_MODE 1
++int mp_drv_mode = 0; /* 1 Mptool Fw; 0 Normal Fw */
++
++
++#if CONFIG_BLUEDROID
++#define QUEUE_SIZE 500
++
++/***************************************
++** AicSemi - Integrate from bluetooth.h **
++*****************************************/
++/* Reserv for core and drivers use */
++#define BT_SKB_RESERVE    8
++
++/* BD Address */
++typedef struct {
++    __u8 b[6];
++} __packed bdaddr_t;
++
++/* Skb helpers */
++struct bt_skb_cb {
++    __u8 pkt_type;
++    __u8 incoming;
++    __u16 expect;
++    __u16 tx_seq;
++    __u8 retries;
++    __u8 sar;
++    __u8 force_active;
++};
++
++#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
++
++static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
++{
++    struct sk_buff *skb;
++
++    if ((skb = alloc_skb(len + BT_SKB_RESERVE, how))) {
++        skb_reserve(skb, BT_SKB_RESERVE);
++        bt_cb(skb)->incoming  = 0;
++    }
++    return skb;
++}
++/* AicSemi - Integrate from bluetooth.h end */
++
++/***********************************
++** AicSemi - Integrate from hci.h **
++***********************************/
++#define HCI_MAX_ACL_SIZE    1024
++#define HCI_MAX_SCO_SIZE    255
++#define HCI_MAX_EVENT_SIZE    260
++#define HCI_MAX_FRAME_SIZE    (HCI_MAX_ACL_SIZE + 4)
++
++/* HCI bus types */
++#define HCI_VIRTUAL    0
++#define HCI_USB        1
++#define HCI_PCCARD    2
++#define HCI_UART    3
++#define HCI_RS232    4
++#define HCI_PCI        5
++#define HCI_SDIO    6
++
++/* HCI controller types */
++#define HCI_BREDR    0x00
++#define HCI_AMP        0x01
++
++/* HCI device flags */
++enum {
++    HCI_UP,
++    HCI_INIT,
++    HCI_RUNNING,
++
++    HCI_PSCAN,
++    HCI_ISCAN,
++    HCI_AUTH,
++    HCI_ENCRYPT,
++    HCI_INQUIRY,
++
++    HCI_RAW,
++
++    HCI_RESET,
++};
++
++/*
++ * BR/EDR and/or LE controller flags: the flags defined here should represent
++ * states from the controller.
++ */
++enum {
++    HCI_SETUP,
++    HCI_AUTO_OFF,
++    HCI_MGMT,
++    HCI_PAIRABLE,
++    HCI_SERVICE_CACHE,
++    HCI_LINK_KEYS,
++    HCI_DEBUG_KEYS,
++    HCI_UNREGISTER,
++
++    HCI_LE_SCAN,
++    HCI_SSP_ENABLED,
++    HCI_HS_ENABLED,
++    HCI_LE_ENABLED,
++    HCI_CONNECTABLE,
++    HCI_DISCOVERABLE,
++    HCI_LINK_SECURITY,
++    HCI_PENDING_CLASS,
++};
++
++/* HCI data types */
++#define HCI_COMMAND_PKT        0x01
++#define HCI_ACLDATA_PKT        0x02
++#define HCI_SCODATA_PKT        0x03
++#define HCI_EVENT_PKT        0x04
++#define HCI_VENDOR_PKT        0xff
++
++#define HCI_MAX_NAME_LENGTH        248
++#define HCI_MAX_EIR_LENGTH        240
++
++#define HCI_OP_READ_LOCAL_VERSION    0x1001
++struct hci_rp_read_local_version {
++    __u8     status;
++    __u8     hci_ver;
++    __le16   hci_rev;
++    __u8     lmp_ver;
++    __le16   manufacturer;
++    __le16   lmp_subver;
++} __packed;
++
++#define HCI_EV_CMD_COMPLETE        0x0e
++struct hci_ev_cmd_complete {
++    __u8     ncmd;
++    __le16   opcode;
++} __packed;
++
++/* ---- HCI Packet structures ---- */
++#define HCI_COMMAND_HDR_SIZE 3
++#define HCI_EVENT_HDR_SIZE   2
++#define HCI_ACL_HDR_SIZE     4
++#define HCI_SCO_HDR_SIZE     3
++
++struct hci_command_hdr {
++    __le16    opcode;        /* OCF & OGF */
++    __u8    plen;
++} __packed;
++
++struct hci_event_hdr {
++    __u8    evt;
++    __u8    plen;
++} __packed;
++
++struct hci_acl_hdr {
++    __le16    handle;        /* Handle & Flags(PB, BC) */
++    __le16    dlen;
++} __packed;
++
++struct hci_sco_hdr {
++    __le16    handle;
++    __u8    dlen;
++} __packed;
++
++static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb)
++{
++    return (struct hci_event_hdr *) skb->data;
++}
++
++static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb)
++{
++    return (struct hci_acl_hdr *) skb->data;
++}
++
++static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
++{
++    return (struct hci_sco_hdr *) skb->data;
++}
++
++/* ---- HCI Ioctl requests structures ---- */
++struct hci_dev_stats {
++    __u32 err_rx;
++    __u32 err_tx;
++    __u32 cmd_tx;
++    __u32 evt_rx;
++    __u32 acl_tx;
++    __u32 acl_rx;
++    __u32 sco_tx;
++    __u32 sco_rx;
++    __u32 byte_rx;
++    __u32 byte_tx;
++};
++/* AicSemi - Integrate from hci.h end */
++
++/*****************************************
++** AicSemi - Integrate from hci_core.h  **
++*****************************************/
++struct hci_conn_hash {
++    struct list_head list;
++    unsigned int     acl_num;
++    unsigned int     sco_num;
++    unsigned int     le_num;
++};
++
++#define HCI_MAX_SHORT_NAME_LENGTH    10
++
++#define NUM_REASSEMBLY 4
++struct hci_dev {
++    struct mutex    lock;
++
++    char        name[8];
++    unsigned long    flags;
++    __u16        id;
++    __u8        bus;
++    __u8        dev_type;
++
++    struct sk_buff        *reassembly[NUM_REASSEMBLY];
++
++    struct hci_conn_hash    conn_hash;
++
++    struct hci_dev_stats    stat;
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++    atomic_t        refcnt;
++    struct module           *owner;
++    void                    *driver_data;
++#endif
++
++    atomic_t        promisc;
++
++    struct device        *parent;
++    struct device        dev;
++
++    unsigned long        dev_flags;
++
++    int (*open)(struct hci_dev *hdev);
++    int (*close)(struct hci_dev *hdev);
++    int (*flush)(struct hci_dev *hdev);
++    int (*send)(struct sk_buff *skb);
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++    void (*destruct)(struct hci_dev *hdev);
++#endif
++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1)
++    __u16               voice_setting;
++#endif
++    void (*notify)(struct hci_dev *hdev, unsigned int evt);
++    int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
++      u8 *align_data;
++};
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
++static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d)
++{
++    atomic_inc(&d->refcnt);
++    return d;
++}
++
++static inline void __hci_dev_put(struct hci_dev *d)
++{
++    if (atomic_dec_and_test(&d->refcnt))
++        d->destruct(d);
++}
++#endif
++
++static inline void *hci_get_drvdata(struct hci_dev *hdev)
++{
++    return dev_get_drvdata(&hdev->dev);
++}
++
++static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
++{
++    dev_set_drvdata(&hdev->dev, data);
++}
++
++#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev))
++/* AicSemi - Integrate from hci_core.h end */
++
++/* -----  HCI Commands ---- */
++#define HCI_OP_INQUIRY            0x0401
++#define HCI_OP_INQUIRY_CANCEL        0x0402
++#define HCI_OP_EXIT_PERIODIC_INQ    0x0404
++#define HCI_OP_CREATE_CONN        0x0405
++#define HCI_OP_DISCONNECT                0x0406
++#define HCI_OP_ADD_SCO            0x0407
++#define HCI_OP_CREATE_CONN_CANCEL    0x0408
++#define HCI_OP_ACCEPT_CONN_REQ        0x0409
++#define HCI_OP_REJECT_CONN_REQ        0x040a
++#define HCI_OP_LINK_KEY_REPLY        0x040b
++#define HCI_OP_LINK_KEY_NEG_REPLY    0x040c
++#define HCI_OP_PIN_CODE_REPLY        0x040d
++#define HCI_OP_PIN_CODE_NEG_REPLY    0x040e
++#define HCI_OP_CHANGE_CONN_PTYPE    0x040f
++#define HCI_OP_AUTH_REQUESTED        0x0411
++#define HCI_OP_SET_CONN_ENCRYPT        0x0413
++#define HCI_OP_CHANGE_CONN_LINK_KEY    0x0415
++#define HCI_OP_REMOTE_NAME_REQ        0x0419
++#define HCI_OP_REMOTE_NAME_REQ_CANCEL    0x041a
++#define HCI_OP_READ_REMOTE_FEATURES    0x041b
++#define HCI_OP_READ_REMOTE_EXT_FEATURES    0x041c
++#define HCI_OP_READ_REMOTE_VERSION    0x041d
++#define HCI_OP_SETUP_SYNC_CONN        0x0428
++#define HCI_OP_ACCEPT_SYNC_CONN_REQ    0x0429
++#define HCI_OP_REJECT_SYNC_CONN_REQ    0x042a
++#define HCI_OP_SNIFF_MODE        0x0803
++#define HCI_OP_EXIT_SNIFF_MODE        0x0804
++#define HCI_OP_ROLE_DISCOVERY        0x0809
++#define HCI_OP_SWITCH_ROLE        0x080b
++#define HCI_OP_READ_LINK_POLICY        0x080c
++#define HCI_OP_WRITE_LINK_POLICY    0x080d
++#define HCI_OP_READ_DEF_LINK_POLICY    0x080e
++#define HCI_OP_WRITE_DEF_LINK_POLICY    0x080f
++#define HCI_OP_SNIFF_SUBRATE        0x0811
++#define HCI_OP_Write_Link_Policy_Settings 0x080d
++#define HCI_OP_SET_EVENT_MASK        0x0c01
++#define HCI_OP_RESET            0x0c03
++#define HCI_OP_SET_EVENT_FLT        0x0c05
++#define HCI_OP_Write_Extended_Inquiry_Response        0x0c52
++#define HCI_OP_Write_Simple_Pairing_Mode 0x0c56
++#define HCI_OP_Read_Buffer_Size 0x1005
++#define HCI_OP_Host_Buffer_Size 0x0c33
++#define HCI_OP_Read_Local_Version_Information 0x1001
++#define HCI_OP_Read_BD_ADDR 0x1009
++#define HCI_OP_Read_Local_Supported_Commands 0x1002
++#define HCI_OP_Write_Scan_Enable 0x0c1a
++#define HCI_OP_Write_Current_IAC_LAP 0x0c3a
++#define HCI_OP_Write_Inquiry_Scan_Activity 0x0c1e
++#define HCI_OP_Write_Class_of_Device 0x0c24
++#define HCI_OP_LE_Rand 0x2018
++#define HCI_OP_LE_Set_Random_Address 0x2005
++#define HCI_OP_LE_Set_Extended_Scan_Enable 0x2042
++#define HCI_OP_LE_Set_Extended_Scan_Parameters 0x2041
++#define HCI_OP_Set_Event_Filter 0x0c05
++#define HCI_OP_Write_Voice_Setting 0x0c26
++#define HCI_OP_Change_Local_Name 0x0c13
++#define HCI_OP_Read_Local_Name 0x0c14
++#define HCI_OP_Wirte_Page_Timeout 0x0c18
++#define HCI_OP_LE_Clear_Resolving_List 0x0c29
++#define HCI_OP_LE_Set_Addres_Resolution_Enable_Command 0x0c2e
++#define HCI_OP_Write_Inquiry_mode 0x0c45
++#define HCI_OP_Write_Page_Scan_Type 0x0c47
++#define HCI_OP_Write_Inquiry_Scan_Type 0x0c43
++
++#define HCI_OP_Delete_Stored_Link_Key 0x0c12
++#define HCI_OP_LE_Read_Local_Resolvable_Address 0x202d
++#define HCI_OP_LE_Extended_Create_Connection 0x2043
++#define HCI_OP_Read_Remote_Version_Information 0x041d
++#define HCI_OP_LE_Start_Encryption 0x2019
++#define HCI_OP_LE_Add_Device_to_Resolving_List 0x2027
++#define HCI_OP_LE_Set_Privacy_Mode 0x204e
++#define HCI_OP_LE_Connection_Update 0x2013
++
++/* -----  HCI events---- */
++#define HCI_OP_DISCONNECT        0x0406
++#define HCI_EV_INQUIRY_COMPLETE        0x01
++#define HCI_EV_INQUIRY_RESULT        0x02
++#define HCI_EV_CONN_COMPLETE        0x03
++#define HCI_EV_CONN_REQUEST            0x04
++#define HCI_EV_DISCONN_COMPLETE        0x05
++#define HCI_EV_AUTH_COMPLETE        0x06
++#define HCI_EV_REMOTE_NAME            0x07
++#define HCI_EV_ENCRYPT_CHANGE        0x08
++#define HCI_EV_CHANGE_LINK_KEY_COMPLETE    0x09
++
++#define HCI_EV_REMOTE_FEATURES        0x0b
++#define HCI_EV_REMOTE_VERSION        0x0c
++#define HCI_EV_QOS_SETUP_COMPLETE    0x0d
++#define HCI_EV_CMD_COMPLETE            0x0e
++#define HCI_EV_CMD_STATUS            0x0f
++
++#define HCI_EV_ROLE_CHANGE            0x12
++#define HCI_EV_NUM_COMP_PKTS        0x13
++#define HCI_EV_MODE_CHANGE            0x14
++#define HCI_EV_PIN_CODE_REQ            0x16
++#define HCI_EV_LINK_KEY_REQ            0x17
++#define HCI_EV_LINK_KEY_NOTIFY        0x18
++#define HCI_EV_CLOCK_OFFSET            0x1c
++#define HCI_EV_PKT_TYPE_CHANGE        0x1d
++#define HCI_EV_PSCAN_REP_MODE        0x20
++
++#define HCI_EV_INQUIRY_RESULT_WITH_RSSI    0x22
++#define HCI_EV_REMOTE_EXT_FEATURES    0x23
++#define HCI_EV_SYNC_CONN_COMPLETE    0x2c
++#define HCI_EV_SYNC_CONN_CHANGED    0x2d
++#define HCI_EV_SNIFF_SUBRATE            0x2e
++#define HCI_EV_EXTENDED_INQUIRY_RESULT    0x2f
++#define HCI_EV_IO_CAPA_REQUEST        0x31
++#define HCI_EV_SIMPLE_PAIR_COMPLETE    0x36
++#define HCI_EV_REMOTE_HOST_FEATURES    0x3d
++#define HCI_EV_LE_Meta 0x3e
++
++#define CONFIG_MAC_OFFSET_GEN_1_2       (0x3C)      //MAC's OFFSET in config/efuse for aic generation 1~2 bluetooth chip
++#define CONFIG_MAC_OFFSET_GEN_3PLUS     (0x44)      //MAC's OFFSET in config/efuse for aic generation 3+ bluetooth chip
++
++
++typedef struct {
++    uint16_t    vid;
++    uint16_t    pid;
++    uint16_t    lmp_sub_default;
++    uint16_t    lmp_sub;
++    uint16_t    eversion;
++    char        *mp_patch_name;
++    char        *patch_name;
++    char        *config_name;
++    uint8_t     *fw_cache;
++    int         fw_len;
++    uint16_t    mac_offset;
++    uint32_t    max_patch_size;
++} patch_info;
++
++//Define ioctl cmd the same as HCIDEVUP in the kernel
++#define DOWN_FW_CFG             _IOW('E', 176, int)
++//#ifdef CONFIG_SCO_OVER_HCI
++//#define SET_ISO_CFG             _IOW('H', 202, int)
++//#else
++#define SET_ISO_CFG             _IOW('E', 177, int)
++//#endif
++#define RESET_CONTROLLER        _IOW('E', 178, int)
++#define DWFW_CMPLT              _IOW('E', 179, int)
++
++#define GET_USB_INFO            _IOR('E', 180, int)
++
++/*  for altsettings*/
++#include <linux/fs.h>
++#define BDADDR_FILE "/data/misc/bluetooth/bdaddr"
++#define FACTORY_BT_BDADDR_STORAGE_LEN 17
++#if 0
++static inline int getmacaddr(uint8_t * vnd_local_bd_addr)
++{
++    struct file  *bdaddr_file;
++    mm_segment_t oldfs;
++    char buf[FACTORY_BT_BDADDR_STORAGE_LEN];
++    int32_t i = 0;
++    memset(buf, 0, FACTORY_BT_BDADDR_STORAGE_LEN);
++    bdaddr_file = filp_open(BDADDR_FILE, O_RDONLY, 0);
++    if (IS_ERR(bdaddr_file)){
++        AICBT_INFO("No Mac Config for BT\n");
++        return -1;
++    }
++    oldfs = get_fs(); 
++    set_fs(KERNEL_DS);
++    bdaddr_file->f_op->llseek(bdaddr_file, 0, 0);
++    bdaddr_file->f_op->read(bdaddr_file, buf, FACTORY_BT_BDADDR_STORAGE_LEN, &bdaddr_file->f_pos);
++    for (i = 0; i < 6; i++) {
++     if(buf[3*i]>'9')
++     {
++         if(buf[3*i]>'Z')
++              buf[3*i] -=('a'-'A'); //change  a to A
++         buf[3*i] -= ('A'-'9'-1);
++     }
++     if(buf[3*i+1]>'9')
++     {
++        if(buf[3*i+1]>'Z')
++              buf[3*i+1] -=('a'-'A'); //change  a to A
++         buf[3*i+1] -= ('A'-'9'-1);
++     }
++     vnd_local_bd_addr[5-i] = ((uint8_t)buf[3*i]-'0')*16 + ((uint8_t)buf[3*i+1]-'0');
++    }
++    set_fs(oldfs);
++    filp_close(bdaddr_file, NULL);
++    return 0;
++}
++#endif
++
++#endif /* CONFIG_BLUEDROID */
++
++
++typedef struct {
++    struct usb_interface    *intf;
++    struct usb_device        *udev;
++    int            pipe_in, pipe_out;
++    uint8_t        *send_pkt;
++    uint8_t        *rcv_pkt;
++    struct hci_command_hdr        *cmd_hdr;
++    struct hci_event_hdr        *evt_hdr;
++    struct hci_ev_cmd_complete    *cmd_cmp;
++    uint8_t        *req_para,    *rsp_para;
++    uint8_t        *fw_data;
++    int            pkt_len;
++    int            fw_len;
++} firmware_info;
++
++/*******************************
++**    Reasil patch code
++********************************/
++#define CMD_CMP_EVT        0x0e
++#define RCV_PKT_LEN            64
++#define SEND_PKT_LEN       300
++#define MSG_TO            1000
++#define PATCH_SEG_MAX    252
++#define DATA_END        0x80
++#define DOWNLOAD_OPCODE    0xfc02
++#define HCI_VSC_UPDATE_PT_CMD          0xFC75
++#define BTOFF_OPCODE    0xfc28
++#define TRUE            1
++#define FALSE            0
++#define CMD_HDR_LEN        sizeof(struct hci_command_hdr)
++#define EVT_HDR_LEN        sizeof(struct hci_event_hdr)
++#define CMD_CMP_LEN        sizeof(struct hci_ev_cmd_complete)
++#define MAX_PATCH_SIZE_24K (1024*24)
++#define MAX_PATCH_SIZE_40K (1024*40)
++
++
++#define FW_RAM_ADID_BASE_ADDR           0x101788
++#define FW_RAM_PATCH_BASE_ADDR          0x184000
++#define FW_ADID_BASE_NAME               "fw_adid_8800dc.bin"
++#define FW_PATCH_TABLE_NAME             "fw_patch_table_8800dc.bin"
++#define FW_PATCH_BASE_NAME              "fw_patch_8800dc.bin"
++#define FW_PATCH_TABLE_NAME_U02         "fw_patch_table_8800dc_u02.bin"
++#define FW_PATCH_BASE_NAME_U02          "fw_patch_8800dc_u02.bin"
++#define FW_PATCH_TABLE_NAME_U02H        "fw_patch_table_8800dc_u02h.bin"
++#define FW_PATCH_BASE_NAME_U02H         "fw_patch_8800dc_u02h.bin"
++#define AICBT_PT_TAG                    "AICBT_PT_TAG"
++
++enum aicbt_patch_table_type {
++    AICBT_PT_NULL = 0x00,
++    AICBT_PT_TRAP,
++    AICBT_PT_B4,
++    AICBT_PT_BTMODE,
++    AICBT_PT_PWRON,
++    AICBT_PT_AF,
++    AICBT_PT_VER,
++    AICBT_PT_MAX,
++};
++
++#define HCI_VSC_FW_STATUS_GET_CMD          0xFC78
++
++struct fw_status {
++    u8 status;
++} __packed;
++
++#define HCI_PATCH_DATA_MAX_LEN              240
++#define HCI_VSC_MEM_WR_SIZE                 240
++#define HCI_VSC_MEM_RD_SIZE                 128
++#define HCI_VSC_UPDATE_PT_SIZE              249
++#define HCI_PT_MAX_LEN                      31
++
++#define HCI_VSC_DBG_RD_MEM_CMD              0xFC01
++
++struct hci_dbg_rd_mem_cmd {
++    __le32 start_addr;
++    __u8 type;
++    __u8 length;
++}__attribute__ ((packed));
++
++struct hci_dbg_rd_mem_cmd_evt {
++    __u8 status;
++    __u8 length;
++    __u8 data[HCI_VSC_MEM_RD_SIZE];
++}__attribute__ ((packed));
++
++struct long_buffer_tag {
++    __u8 length;
++    __u8 data[HCI_VSC_MEM_WR_SIZE];
++};
++
++struct hci_dbg_wr_mem_cmd {
++    __le32 start_addr;
++    __u8 type;
++    __u8 length;
++    __u8 data[HCI_VSC_MEM_WR_SIZE];
++};
++
++struct aicbt_patch_table {
++    char     *name;
++    uint32_t type;
++    uint32_t *data;
++    uint32_t len;
++    struct aicbt_patch_table *next;
++};
++
++struct aicbt_patch_table_cmd {
++    uint8_t patch_num;
++    uint32_t patch_table_addr[31];
++    uint32_t patch_table_data[31];
++}__attribute__ ((packed));
++
++
++enum aic_endpoit {
++    CTRL_EP = 0,
++    INTR_EP = 3,
++    BULK_EP = 1,
++    ISOC_EP = 4
++};
++
++/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */
++#define HCI_VERSION_CODE LINUX_VERSION_CODE
++
++int aic_load_firmware(u8 ** fw_buf, const char *name, struct device *device);
++int aicbt_patch_table_free(struct aicbt_patch_table **head);
++int download_patch(firmware_info *fw_info, int cached);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
++#define NUM_REASSEMBLY 3
++#else
++#define NUM_REASSEMBLY 4
++#endif
++
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c
+@@ -0,0 +1,126 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/usb.h>
++#include <linux/poll.h>
++#include <linux/cdev.h>
++#include <linux/device.h>
++
++
++
++#define IOCTL_CHAR_DEVICE_NAME "aic_btusb_ex_dev"
++
++#define SET_APCF_PARAMETER    _IOR('E', 181, int)
++
++static dev_t ioctl_devid; /* bt char device number */
++static struct cdev ioctl_char_dev; /* bt character device structure */
++static struct class *ioctl_char_class; /* device class for usb char driver */
++
++extern struct file_operations ioctl_chrdev_ops;
++
++extern void btchr_external_write(char* data, int len);
++
++static long ioctl_ioctl(struct file *file_p,unsigned int cmd, unsigned long arg)
++{
++      char data[1024];
++      int ret = 0;
++
++      printk("%s enter\r\n", __func__);
++      memset(data, 0, 1024);
++    switch(cmd) 
++    {
++        case SET_APCF_PARAMETER:
++                      printk("set apcf parameter\r\n");
++              ret = copy_from_user(data, (int __user *)arg, 1024);
++                      btchr_external_write(&data[1], (int)data[0]);
++        break;
++
++        default:
++                      printk("unknow cmdr\r\n");
++                      break;
++    }
++    return 0;
++}
++
++
++#ifdef CONFIG_COMPAT
++static long compat_ioctlchr_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
++{
++    return ioctl_ioctl(filp, cmd, (unsigned long) compat_ptr(arg));
++}
++#endif
++
++
++struct file_operations ioctl_chrdev_ops  = {
++    unlocked_ioctl   :   ioctl_ioctl,
++#ifdef CONFIG_COMPAT
++      compat_ioctl :  compat_ioctlchr_ioctl,
++#endif
++
++};
++
++static int __init init_extenal_ioctl(void){
++      int res = 0;
++      struct device *dev;
++
++      printk("%s enter\r\n", __func__);
++      
++              ioctl_char_class = class_create(THIS_MODULE, IOCTL_CHAR_DEVICE_NAME);
++              if (IS_ERR(ioctl_char_class)) {
++                      printk("Failed to create ioctl char class");
++              }
++      
++              res = alloc_chrdev_region(&ioctl_devid, 0, 1, IOCTL_CHAR_DEVICE_NAME);
++              if (res < 0) {
++                      printk("Failed to allocate ioctl char device");
++                      goto err_alloc;
++              }
++      
++              dev = device_create(ioctl_char_class, NULL, ioctl_devid, NULL, IOCTL_CHAR_DEVICE_NAME);
++              if (IS_ERR(dev)) {
++                      printk("Failed to create ioctl char device");
++                      res = PTR_ERR(dev);
++                      goto err_create;
++              }
++      
++              cdev_init(&ioctl_char_dev, &ioctl_chrdev_ops);
++              res = cdev_add(&ioctl_char_dev, ioctl_devid, 1);
++              if (res < 0) {
++                      printk("Failed to add ioctl char device");
++                      goto err_add;
++              }
++
++              return res;
++      
++err_add:
++              device_destroy(ioctl_char_class, ioctl_devid);
++err_create:
++              unregister_chrdev_region(ioctl_devid, 1);
++err_alloc:
++              class_destroy(ioctl_char_class);
++
++              return res;
++
++}
++static void __exit deinit_extenal_ioctl(void){
++      printk("%s enter\r\n", __func__);
++    device_destroy(ioctl_char_class, ioctl_devid);
++    cdev_del(&ioctl_char_dev);
++    unregister_chrdev_region(ioctl_devid, 1);
++    class_destroy(ioctl_char_class);
++
++}
++
++module_init(init_extenal_ioctl);
++module_exit(deinit_extenal_ioctl);
++
++
++MODULE_AUTHOR("AicSemi Corporation");
++MODULE_DESCRIPTION("AicSemi Bluetooth USB driver version");
++MODULE_LICENSE("GPL");
++
+--- /dev/null
++++ b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h
+@@ -0,0 +1,3 @@
++
++void btchr_external_write(char* data, int len);
++
diff --git a/target/linux/starfive/patches-6.6/0114-riscv-dts-starfive-visionfive-2-Sync-the-sound-card-.patch b/target/linux/starfive/patches-6.6/0114-riscv-dts-starfive-visionfive-2-Sync-the-sound-card-.patch
new file mode 100644 (file)
index 0000000..c599a44
--- /dev/null
@@ -0,0 +1,67 @@
+From fdb2822982887c7048f34601688ae2406ccbcfcc Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 15 Apr 2024 11:29:17 +0800
+Subject: [PATCH 114/116] riscv: dts: starfive: visionfive 2: Sync the sound
+ card names with v5.15 and v6.1
+
+Sync the sound card names. So we can build Debian images with the
+same process.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ .../dts/starfive/jh7110-starfive-visionfive-2-tdm.dts     | 2 +-
+ .../dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts  | 2 +-
+ .../boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi   | 8 ++++----
+ 3 files changed, 6 insertions(+), 6 deletions(-)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
+@@ -8,7 +8,7 @@
+ #include "jh7110-starfive-visionfive-2-v1.3b.dts"
+ / {
+-      sound-tdm {
++      sound5: snd-card5 {
+               compatible = "simple-audio-card";
+               #address-cells = <1>;
+               #size-cells = <0>;
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
+@@ -9,7 +9,7 @@
+ / {
+       /* i2s + wm8960 */
+-      sound-wm8960 {
++      sound6: snd-card6 {
+               compatible = "simple-audio-card";
+               #address-cells = <1>;
+               #size-cells = <0>;
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -91,9 +91,9 @@
+               #sound-dai-cells = <0>;
+       };
+-      sound-pwmdac {
++      sound3: snd-card3 {
+               compatible = "simple-audio-card";
+-              simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
++              simple-audio-card,name = "Starfive-PWMDAC-Sound-Card";
+               #address-cells = <1>;
+               #size-cells = <0>;
+@@ -113,12 +113,12 @@
+               };
+       };
+-      sound-hdmi {
++      sound1: snd-card1 {
+               compatible = "simple-audio-card";
+               #address-cells = <1>;
+               #size-cells = <0>;
+-              simple-audio-card,name = "StarFive-HDMI-Sound-Card";
++              simple-audio-card,name = "Starfive-HDMI-Sound-Card";
+               simple-audio-card,dai-link@0 {
+                       reg = <0>;
+                       format = "i2s";
diff --git a/target/linux/starfive/patches-6.6/0115-clk-starfive-jh7110-Change-uart3-uart5-clk-register-.patch b/target/linux/starfive/patches-6.6/0115-clk-starfive-jh7110-Change-uart3-uart5-clk-register-.patch
new file mode 100644 (file)
index 0000000..6b34f0e
--- /dev/null
@@ -0,0 +1,53 @@
+From dac6f3021895e7678552789edff22f1dc909e274 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Mon, 15 Apr 2024 20:59:35 +0800
+Subject: [PATCH 115/116] clk: starfive: jh7110: Change uart3-uart5 clk
+ register info
+
+The core_clk division register of uart3-uart5 include fractional and
+integral parts, but now only use the integral part, so include shift
+operation. The integral part include 8 bit, so the max value can be
+configed is 255.
+
+Signed-off-by: Yanhong Wang <yanhong.wang@starfivetech.com>
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ drivers/clk/starfive/clk-starfive-jh71x0.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/clk/starfive/clk-starfive-jh71x0.c
++++ b/drivers/clk/starfive/clk-starfive-jh71x0.c
+@@ -10,6 +10,8 @@
+ #include <linux/device.h>
+ #include <linux/io.h>
++#include <dt-bindings/clock/starfive,jh7110-crg.h>
++
+ #include "clk-starfive-jh71x0.h"
+ static struct jh71x0_clk *jh71x0_clk_from(struct clk_hw *hw)
+@@ -70,6 +72,11 @@ static unsigned long jh71x0_clk_recalc_r
+       struct jh71x0_clk *clk = jh71x0_clk_from(hw);
+       u32 div = jh71x0_clk_reg_get(clk) & JH71X0_CLK_DIV_MASK;
++      if (clk->idx == JH7110_SYSCLK_UART3_CORE ||
++          clk->idx == JH7110_SYSCLK_UART4_CORE ||
++          clk->idx == JH7110_SYSCLK_UART5_CORE)
++              div >>= 8;
++
+       return div ? parent_rate / div : 0;
+ }
+@@ -110,6 +117,12 @@ static int jh71x0_clk_set_rate(struct cl
+       unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate),
+                                 1UL, (unsigned long)clk->max_div);
++      /* UART3-5: [15:8]: integer part of the divisor. [7:0] fraction part of the divisor */
++      if (clk->idx == JH7110_SYSCLK_UART3_CORE ||
++          clk->idx == JH7110_SYSCLK_UART4_CORE ||
++          clk->idx == JH7110_SYSCLK_UART5_CORE)
++              div <<= 8;
++
+       jh71x0_clk_reg_rmw(clk, JH71X0_CLK_DIV_MASK, div);
+       return 0;
+ }
diff --git a/target/linux/starfive/patches-6.6/0116-riscv-dts-starfive-visionfive-2-Quote-corresponding-.patch b/target/linux/starfive/patches-6.6/0116-riscv-dts-starfive-visionfive-2-Quote-corresponding-.patch
new file mode 100644 (file)
index 0000000..659c442
--- /dev/null
@@ -0,0 +1,32 @@
+From 1ea169bb83d3b556eb9054f1a3e8ce0411370293 Mon Sep 17 00:00:00 2001
+From: Hal Feng <hal.feng@starfivetech.com>
+Date: Wed, 24 Apr 2024 14:23:07 +0800
+Subject: [PATCH 116/116] riscv: dts: starfive: visionfive 2: Quote
+ corresponding regulators in hdmi and vin
+
+So PMIC can start to work before HDMI and VIN.
+
+Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
+---
+ arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+@@ -961,6 +961,8 @@
+       pinctrl-names = "default";
+       pinctrl-0 = <&hdmi_pins>;
+       hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
++      hdmi_0p9-supply = <&hdmi_0p9>;
++      hdmi_1p8-supply = <&hdmi_1p8>;
+       hdmi_in: port {
+               #address-cells = <1>;
+@@ -1084,6 +1086,7 @@
+ &vin_sysctl {
+       /* when use dvp open this pinctrl*/
+       status = "okay";
++      mipi_0p9-supply = <&mipi_0p9>;
+       ports {
+               #address-cells = <1>;
diff --git a/target/linux/starfive/patches-6.6/1000-serial-8250_dw-Add-starfive-jh7100-hsuart-compatible.patch b/target/linux/starfive/patches-6.6/1000-serial-8250_dw-Add-starfive-jh7100-hsuart-compatible.patch
new file mode 100644 (file)
index 0000000..463c7eb
--- /dev/null
@@ -0,0 +1,25 @@
+From b7b4fad8784d7ab4fcd929a56c3e5366046fe7fc Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Thu, 14 Oct 2021 20:56:54 +0200
+Subject: [PATCH 1000/1024] serial: 8250_dw: Add starfive,jh7100-hsuart
+ compatible
+
+This adds a compatible for the high speed UARTs on the StarFive JH7100
+RISC-V SoC. Just like the regular uarts we also need to keep the input
+clocks at their default rate and rely only on the divisor in the UART.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/tty/serial/8250/8250_dw.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/tty/serial/8250/8250_dw.c
++++ b/drivers/tty/serial/8250/8250_dw.c
+@@ -802,6 +802,7 @@ static const struct of_device_id dw8250_
+       { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
+       { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
+       { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data },
++      { .compatible = "starfive,jh7100-hsuart", .data = &dw8250_starfive_jh7100_data },
+       { .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data },
+       { /* Sentinel */ }
+ };
diff --git a/target/linux/starfive/patches-6.6/1001-RISC-V-Add-StarFive-JH7100-audio-clock-node.patch b/target/linux/starfive/patches-6.6/1001-RISC-V-Add-StarFive-JH7100-audio-clock-node.patch
new file mode 100644 (file)
index 0000000..4cfa2a1
--- /dev/null
@@ -0,0 +1,32 @@
+From 3051ab0102aaf969dd85f07b6ab731dadf97ef6c Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 17:13:22 +0100
+Subject: [PATCH 1001/1024] RISC-V: Add StarFive JH7100 audio clock node
+
+Add device tree node for the audio clocks on the StarFive JH7100 RISC-V
+SoC.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -158,6 +158,16 @@
+                       riscv,ndev = <133>;
+               };
++              audclk: clock-controller@10480000 {
++                      compatible = "starfive,jh7100-audclk";
++                      reg = <0x0 0x10480000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_AUDIO_SRC>,
++                               <&clkgen JH7100_CLK_AUDIO_12288>,
++                               <&clkgen JH7100_CLK_DOM7AHB_BUS>;
++                      clock-names = "audio_src", "audio_12288", "dom7ahb_bus";
++                      #clock-cells = <1>;
++              };
++
+               clkgen: clock-controller@11800000 {
+                       compatible = "starfive,jh7100-clkgen";
+                       reg = <0x0 0x11800000 0x0 0x10000>;
diff --git a/target/linux/starfive/patches-6.6/1002-drivers-tty-serial-8250-update-driver-for-JH7100.patch b/target/linux/starfive/patches-6.6/1002-drivers-tty-serial-8250-update-driver-for-JH7100.patch
new file mode 100644 (file)
index 0000000..73d25f5
--- /dev/null
@@ -0,0 +1,28 @@
+From 8ef30667e97363ca921416a60e50eb28bf88cc4f Mon Sep 17 00:00:00 2001
+From: Samin Guo <samin.guo@starfivetech.com>
+Date: Fri, 8 Jan 2021 03:11:04 +0800
+Subject: [PATCH 1002/1024] drivers/tty/serial/8250: update driver for JH7100
+
+---
+ drivers/tty/serial/8250/8250_port.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -72,8 +72,16 @@ static const struct serial8250_config ua
+       },
+       [PORT_16550] = {
+               .name           = "16550",
++#ifdef CONFIG_SOC_STARFIVE
++              .fifo_size      = 16,
++              .tx_loadsz      = 16,
++              .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
++              .rxtrig_bytes   = {1, 4, 8, 14},
++              .flags          = UART_CAP_FIFO,
++#else
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
++#endif
+       },
+       [PORT_16550A] = {
+               .name           = "16550A",
diff --git a/target/linux/starfive/patches-6.6/1003-dmaengine-dw-axi-dmac-Handle-xfer-start-while-non-id.patch b/target/linux/starfive/patches-6.6/1003-dmaengine-dw-axi-dmac-Handle-xfer-start-while-non-id.patch
new file mode 100644 (file)
index 0000000..d0c692e
--- /dev/null
@@ -0,0 +1,55 @@
+From 2d5b71006c6a93e26eb86b7efd37440c020bab46 Mon Sep 17 00:00:00 2001
+From: Samin Guo <samin.guo@starfivetech.com>
+Date: Wed, 17 Nov 2021 14:50:45 +0800
+Subject: [PATCH 1003/1024] dmaengine: dw-axi-dmac: Handle xfer start while
+ non-idle
+
+Signed-off-by: Samin Guo <samin.guo@starfivetech.com>
+Signed-off-by: Curry Zhang <curry.zhang@starfivetech.com>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 +++++++++++-
+ drivers/dma/dw-axi-dmac/dw-axi-dmac.h          |  1 +
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -382,11 +382,13 @@ static void axi_chan_block_xfer_start(st
+       u32 irq_mask;
+       u8 lms = 0; /* Select AXI0 master for LLI fetching */
++      chan->is_err = false;
+       if (unlikely(axi_chan_is_hw_enable(chan))) {
+               dev_err(chan2dev(chan), "%s is non-idle!\n",
+                       axi_chan_name(chan));
+-              return;
++              axi_chan_disable(chan);
++              chan->is_err = true;
+       }
+       axi_dma_enable(chan->chip);
+@@ -1028,6 +1030,14 @@ static noinline void axi_chan_handle_err
+                       axi_chan_name(chan));
+               goto out;
+       }
++      if (chan->is_err) {
++              struct axi_dma_desc *desc = vd_to_axi_desc(vd);
++
++              axi_chan_block_xfer_start(chan, desc);
++              chan->is_err = false;
++              goto out;
++      }
++
+       /* Remove the completed descriptor from issued list */
+       list_del(&vd->node);
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+@@ -50,6 +50,7 @@ struct axi_dma_chan {
+       struct dma_slave_config         config;
+       enum dma_transfer_direction     direction;
+       bool                            cyclic;
++      bool                            is_err;
+       /* these other elements are all protected by vc.lock */
+       bool                            is_paused;
+ };
diff --git a/target/linux/starfive/patches-6.6/1004-dmaengine-dw-axi-dmac-Add-StarFive-JH7100-support.patch b/target/linux/starfive/patches-6.6/1004-dmaengine-dw-axi-dmac-Add-StarFive-JH7100-support.patch
new file mode 100644 (file)
index 0000000..74a05cf
--- /dev/null
@@ -0,0 +1,64 @@
+From 933f8c33d466de20e9894ef8de5b6afe478a420d Mon Sep 17 00:00:00 2001
+From: Samin Guo <samin.guo@starfivetech.com>
+Date: Wed, 17 Nov 2021 14:50:45 +0800
+Subject: [PATCH 1004/1024] dmaengine: dw-axi-dmac: Add StarFive JH7100 support
+
+Signed-off-by: Samin Guo <samin.guo@starfivetech.com>
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 ++++++++++++
+ drivers/dma/dw-axi-dmac/dw-axi-dmac.h          |  4 ++++
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -677,8 +677,13 @@ static int dw_axi_dma_set_hw_desc(struct
+       hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1);
++#ifdef CONFIG_SOC_STARFIVE
++      ctllo |= DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_DST_MSIZE_POS |
++               DWAXIDMAC_BURST_TRANS_LEN_16 << CH_CTL_L_SRC_MSIZE_POS;
++#else
+       ctllo |= DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
+                DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS;
++#endif
+       hw_desc->lli->ctl_lo = cpu_to_le32(ctllo);
+       set_desc_src_master(hw_desc);
+@@ -1502,7 +1507,11 @@ static int dw_probe(struct platform_devi
+        * Therefore, set constraint to 1024 * 4.
+        */
+       dw->dma.dev->dma_parms = &dw->dma_parms;
++#ifdef CONFIG_SOC_STARFIVE
++      dma_set_max_seg_size(&pdev->dev, DMAC_MAX_BLK_SIZE);
++#else
+       dma_set_max_seg_size(&pdev->dev, MAX_BLOCK_SIZE);
++#endif
+       platform_set_drvdata(pdev, chip);
+       pm_runtime_enable(chip->dev);
+@@ -1587,6 +1596,9 @@ static const struct of_device_id dw_dma_
+               .compatible = "intel,kmb-axi-dma",
+               .data = (void *)AXI_DMA_FLAG_HAS_APB_REGS,
+       }, {
++              .compatible = "starfive,jh7100-axi-dma",
++              .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
++      }, {
+               .compatible = "starfive,jh7110-axi-dma",
+               .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
+       },
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+@@ -284,7 +284,11 @@ enum {
+ #define CH_CTL_L_SRC_MAST             BIT(0)
+ /* CH_CFG_H */
++#ifdef CONFIG_SOC_STARFIVE
++#define CH_CFG_H_PRIORITY_POS         15
++#else
+ #define CH_CFG_H_PRIORITY_POS         17
++#endif
+ #define CH_CFG_H_DST_PER_POS          12
+ #define CH_CFG_H_SRC_PER_POS          7
+ #define CH_CFG_H_HS_SEL_DST_POS               4
diff --git a/target/linux/starfive/patches-6.6/1005-pinctrl-starfive-Reset-pinmux-settings.patch b/target/linux/starfive/patches-6.6/1005-pinctrl-starfive-Reset-pinmux-settings.patch
new file mode 100644 (file)
index 0000000..63a7a0c
--- /dev/null
@@ -0,0 +1,127 @@
+From c5019bf73d1e178beb6055cca254e7d3c916db7c Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 17 Jul 2021 21:50:38 +0200
+Subject: [PATCH 1005/1024] pinctrl: starfive: Reset pinmux settings
+
+Current u-boot doesn't seem to take into account that some GPIOs are
+configured as inputs/outputs of certain peripherals on power-up. This
+means it ends up configuring some GPIOs as inputs to more than one
+peripheral which the documentation explicitly says is illegal. Similarly
+it also ends up configuring more than one GPIO as output of the same
+peripheral. While not explicitly mentioned by the documentation this
+also seems like a bad idea.
+
+The easiest way to remedy this mess is to just disconnect all GPIOs from
+peripherals and have our pinmux configuration set everything up
+properly. This, however, means that we'd disconnect the serial console
+from its pins for a while, so add a device tree property to keep
+certain GPIOs from being reset.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ .../pinctrl/starfive,jh7100-pinctrl.yaml      |  4 ++
+ .../starfive/pinctrl-starfive-jh7100.c        | 66 +++++++++++++++++++
+ 2 files changed, 70 insertions(+)
+
+--- a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
++++ b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
+@@ -88,6 +88,10 @@ properties:
+     $ref: /schemas/types.yaml#/definitions/uint32
+     enum: [0, 1, 2, 3, 4, 5, 6]
++  starfive,keep-gpiomux:
++    description: Keep pinmux for these GPIOs from being reset at boot.
++    $ref: /schemas/types.yaml#/definitions/uint32-array
++
+ required:
+   - compatible
+   - reg
+--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c
++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c
+@@ -203,6 +203,10 @@ static u16 starfive_drive_strength_from_
+       return (clamp(i, 14U, 63U) - 14) / 7;
+ }
++static bool keepmux;
++module_param(keepmux, bool, 0644);
++MODULE_PARM_DESC(keepmux, "Keep pinmux settings from previous boot stage");
++
+ struct starfive_pinctrl {
+       struct gpio_chip gc;
+       struct pinctrl_gpio_range gpios;
+@@ -1225,6 +1229,65 @@ static void starfive_disable_clock(void
+       clk_disable_unprepare(data);
+ }
++#define GPI_END (GPI_USB_OVER_CURRENT + 1)
++static void starfive_pinmux_reset(struct starfive_pinctrl *sfp)
++{
++      static const DECLARE_BITMAP(defaults, GPI_END) = {
++              BIT_MASK(GPI_I2C0_PAD_SCK_IN) |
++              BIT_MASK(GPI_I2C0_PAD_SDA_IN) |
++              BIT_MASK(GPI_I2C1_PAD_SCK_IN) |
++              BIT_MASK(GPI_I2C1_PAD_SDA_IN) |
++              BIT_MASK(GPI_I2C2_PAD_SCK_IN) |
++              BIT_MASK(GPI_I2C2_PAD_SDA_IN) |
++              BIT_MASK(GPI_I2C3_PAD_SCK_IN) |
++              BIT_MASK(GPI_I2C3_PAD_SDA_IN) |
++              BIT_MASK(GPI_SDIO0_PAD_CARD_DETECT_N) |
++
++              BIT_MASK(GPI_SDIO1_PAD_CARD_DETECT_N) |
++              BIT_MASK(GPI_SPI0_PAD_SS_IN_N) |
++              BIT_MASK(GPI_SPI1_PAD_SS_IN_N) |
++              BIT_MASK(GPI_SPI2_PAD_SS_IN_N) |
++              BIT_MASK(GPI_SPI2AHB_PAD_SS_N) |
++              BIT_MASK(GPI_SPI3_PAD_SS_IN_N),
++
++              BIT_MASK(GPI_UART0_PAD_SIN) |
++              BIT_MASK(GPI_UART1_PAD_SIN) |
++              BIT_MASK(GPI_UART2_PAD_SIN) |
++              BIT_MASK(GPI_UART3_PAD_SIN) |
++              BIT_MASK(GPI_USB_OVER_CURRENT)
++      };
++      DECLARE_BITMAP(keep, NR_GPIOS) = {};
++      struct device_node *np = sfp->gc.parent->of_node;
++      int len = of_property_count_u32_elems(np, "starfive,keep-gpiomux");
++      int i;
++
++      for (i = 0; i < len; i++) {
++              u32 gpio;
++
++              of_property_read_u32_index(np, "starfive,keep-gpiomux", i, &gpio);
++              if (gpio < NR_GPIOS)
++                      set_bit(gpio, keep);
++      }
++
++      for (i = 0; i < NR_GPIOS; i++) {
++              if (test_bit(i, keep))
++                      continue;
++
++              writel_relaxed(GPO_DISABLE, sfp->base + GPON_DOEN_CFG + 8 * i);
++              writel_relaxed(GPO_LOW,     sfp->base + GPON_DOUT_CFG + 8 * i);
++      }
++
++      for (i = 0; i < GPI_END; i++) {
++              void __iomem *reg = sfp->base + GPI_CFG_OFFSET + 4 * i;
++              u32 din = readl_relaxed(reg);
++
++              if (din >= 2 && din < (NR_GPIOS + 2) && test_bit(din - 2, keep))
++                      continue;
++
++              writel_relaxed(test_bit(i, defaults), reg);
++      }
++}
++
+ static int starfive_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -1286,6 +1349,9 @@ static int starfive_probe(struct platfor
+               writel(value, sfp->padctl + IO_PADSHARE_SEL);
+       }
++      if (!keepmux)
++              starfive_pinmux_reset(sfp);
++
+       value = readl(sfp->padctl + IO_PADSHARE_SEL);
+       switch (value) {
+       case 0:
diff --git a/target/linux/starfive/patches-6.6/1006-clk-starfive-Add-flags-argument-to-JH71X0__MUX-macro.patch b/target/linux/starfive/patches-6.6/1006-clk-starfive-Add-flags-argument-to-JH71X0__MUX-macro.patch
new file mode 100644 (file)
index 0000000..a6a1bb8
--- /dev/null
@@ -0,0 +1,302 @@
+From a4a2a8886c97e14643fe3e0ab8cf67a75c1bf14d Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Sat, 25 Mar 2023 22:57:06 +0100
+Subject: [PATCH 1006/1024] clk: starfive: Add flags argument to JH71X0__MUX
+ macro
+
+This flag is needed to add the CLK_SET_RATE_PARENT flag on the gmac_tx
+clock on the JH7100, which in turn is needed by the dwmac-starfive
+driver to set the clock properly for 1000, 100 and 10 Mbps links.
+
+This change was mostly made using coccinelle:
+
+@ match @
+expression idx, name, nparents;
+@@
+ JH71X0__MUX(
+-idx, name, nparents,
++idx, name, 0, nparents,
+ ...)
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+---
+ .../clk/starfive/clk-starfive-jh7100-audio.c  |  2 +-
+ drivers/clk/starfive/clk-starfive-jh7100.c    | 32 +++++++++----------
+ .../clk/starfive/clk-starfive-jh7110-aon.c    |  6 ++--
+ .../clk/starfive/clk-starfive-jh7110-isp.c    |  2 +-
+ .../clk/starfive/clk-starfive-jh7110-sys.c    | 26 +++++++--------
+ drivers/clk/starfive/clk-starfive-jh71x0.h    |  4 +--
+ 6 files changed, 36 insertions(+), 36 deletions(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7100-audio.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c
+@@ -79,7 +79,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB),
+       JH71X0_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB),
+       JH71X0__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS),
+-      JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2,
++      JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 0, 2,
+                   JH7100_AUDCLK_VAD_INTMEM,
+                   JH7100_AUDCLK_AUDIO_12288),
+ };
+--- a/drivers/clk/starfive/clk-starfive-jh7100.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100.c
+@@ -24,48 +24,48 @@
+ #define JH7100_CLK_GMAC_GR_MII_RX     (JH7100_CLK_END + 3)
+ static const struct jh71x0_clk_data jh7100_clk_data[] __initconst = {
+-      JH71X0__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4,
++      JH71X0__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 0, 4,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL0_OUT,
+                   JH7100_CLK_PLL1_OUT,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3,
++      JH71X0__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 0, 3,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL1_OUT,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4,
++      JH71X0__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 0, 4,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL0_OUT,
+                   JH7100_CLK_PLL1_OUT,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3,
++      JH71X0__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 0, 3,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL0_OUT,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2,
++      JH71X0__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 0, 2,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL0_OUT),
+-      JH71X0__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2,
++      JH71X0__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 0, 2,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3,
++      JH71X0__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 0, 3,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL1_OUT,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3,
++      JH71X0__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 0, 3,
+                   JH7100_CLK_OSC_AUD,
+                   JH7100_CLK_PLL0_OUT,
+                   JH7100_CLK_PLL2_OUT),
+       JH71X0_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT),
+-      JH71X0__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3,
++      JH71X0__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 0, 3,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL1_OUT,
+                   JH7100_CLK_PLL2_OUT),
+-      JH71X0__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3,
++      JH71X0__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 0, 3,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_PLL0_OUT,
+                   JH7100_CLK_PLL1_OUT),
+-      JH71X0__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3,
++      JH71X0__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 0, 3,
+                   JH7100_CLK_OSC_AUD,
+                   JH7100_CLK_PLL0_OUT,
+                   JH7100_CLK_PLL2_OUT),
+@@ -76,7 +76,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC),
+       JH71X0_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT),
+       JH71X0_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC),
+-      JH71X0__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2,
++      JH71X0__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 0, 2,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_OSC_AUD),
+       JH71X0__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV),
+@@ -142,7 +142,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT),
+       JH71X0_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS),
+       JH71X0__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT),
+-      JH71X0__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2,
++      JH71X0__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 0, 2,
+                   JH7100_CLK_CPU_AXI,
+                   JH7100_CLK_NNEBUS_SRC1),
+       JH71X0_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS),
+@@ -166,7 +166,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV),
+       JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32,
+                   JH7100_CLK_USBPHY_ROOTDIV),
+-      JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2,
++      JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 0, 2,
+                   JH7100_CLK_OSC_SYS,
+                   JH7100_CLK_USBPHY_PLLDIV25M),
+       JH71X0_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT),
+@@ -200,12 +200,12 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV),
+       JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+       JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+-      JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3,
++      JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 0, 3,
+                   JH7100_CLK_GMAC_GTX,
+                   JH7100_CLK_GMAC_TX_INV,
+                   JH7100_CLK_GMAC_RMII_TX),
+       JH71X0__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX),
+-      JH71X0__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2,
++      JH71X0__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 0, 2,
+                   JH7100_CLK_GMAC_GR_MII_RX,
+                   JH7100_CLK_GMAC_RMII_RX),
+       JH71X0__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE),
+--- a/drivers/clk/starfive/clk-starfive-jh7110-aon.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-aon.c
+@@ -26,7 +26,7 @@
+ static const struct jh71x0_clk_data jh7110_aonclk_data[] = {
+       /* source */
+       JH71X0__DIV(JH7110_AONCLK_OSC_DIV4, "osc_div4", 4, JH7110_AONCLK_OSC),
+-      JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func", 2,
++      JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func", 0, 2,
+                   JH7110_AONCLK_OSC_DIV4,
+                   JH7110_AONCLK_OSC),
+       /* gmac0 */
+@@ -39,7 +39,7 @@ static const struct jh71x0_clk_data jh71
+                   JH7110_AONCLK_GMAC0_GTXCLK,
+                   JH7110_AONCLK_GMAC0_RMII_RTX),
+       JH71X0__INV(JH7110_AONCLK_GMAC0_TX_INV, "gmac0_tx_inv", JH7110_AONCLK_GMAC0_TX),
+-      JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx", 2,
++      JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx", 0, 2,
+                   JH7110_AONCLK_GMAC0_RGMII_RXIN,
+                   JH7110_AONCLK_GMAC0_RMII_RTX),
+       JH71X0__INV(JH7110_AONCLK_GMAC0_RX_INV, "gmac0_rx_inv", JH7110_AONCLK_GMAC0_RX),
+@@ -48,7 +48,7 @@ static const struct jh71x0_clk_data jh71
+       /* rtc */
+       JH71X0_GATE(JH7110_AONCLK_RTC_APB, "rtc_apb", 0, JH7110_AONCLK_APB_BUS),
+       JH71X0__DIV(JH7110_AONCLK_RTC_INTERNAL, "rtc_internal", 1022, JH7110_AONCLK_OSC),
+-      JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k", 2,
++      JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k", 0, 2,
+                   JH7110_AONCLK_RTC_OSC,
+                   JH7110_AONCLK_RTC_INTERNAL),
+       JH71X0_GATE(JH7110_AONCLK_RTC_CAL, "rtc_cal", 0, JH7110_AONCLK_OSC),
+--- a/drivers/clk/starfive/clk-starfive-jh7110-isp.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-isp.c
+@@ -53,7 +53,7 @@ static const struct jh71x0_clk_data jh71
+                   JH7110_ISPCLK_MIPI_RX0_PXL),
+       JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF3, "vin_pixel_if3", 0,
+                   JH7110_ISPCLK_MIPI_RX0_PXL),
+-      JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 2,
++      JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 0, 2,
+                   JH7110_ISPCLK_MIPI_RX0_PXL,
+                   JH7110_ISPCLK_DVP_INV),
+       /* ispv2_top_wrapper */
+--- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c
++++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c
+@@ -36,18 +36,18 @@
+ static const struct jh71x0_clk_data jh7110_sysclk_data[] __initconst = {
+       /* root */
+-      JH71X0__MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", 2,
++      JH71X0__MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", 0, 2,
+                   JH7110_SYSCLK_OSC,
+                   JH7110_SYSCLK_PLL0_OUT),
+       JH71X0__DIV(JH7110_SYSCLK_CPU_CORE, "cpu_core", 7, JH7110_SYSCLK_CPU_ROOT),
+       JH71X0__DIV(JH7110_SYSCLK_CPU_BUS, "cpu_bus", 2, JH7110_SYSCLK_CPU_CORE),
+-      JH71X0__MUX(JH7110_SYSCLK_GPU_ROOT, "gpu_root", 2,
++      JH71X0__MUX(JH7110_SYSCLK_GPU_ROOT, "gpu_root", 0, 2,
+                   JH7110_SYSCLK_PLL2_OUT,
+                   JH7110_SYSCLK_PLL1_OUT),
+       JH71X0_MDIV(JH7110_SYSCLK_PERH_ROOT, "perh_root", 2, 2,
+                   JH7110_SYSCLK_PLL0_OUT,
+                   JH7110_SYSCLK_PLL2_OUT),
+-      JH71X0__MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", 2,
++      JH71X0__MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", 0, 2,
+                   JH7110_SYSCLK_OSC,
+                   JH7110_SYSCLK_PLL2_OUT),
+       JH71X0__DIV(JH7110_SYSCLK_NOCSTG_BUS, "nocstg_bus", 3, JH7110_SYSCLK_BUS_ROOT),
+@@ -62,7 +62,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0__DIV(JH7110_SYSCLK_PLL2_DIV2, "pll2_div2", 2, JH7110_SYSCLK_PLL2_OUT),
+       JH71X0__DIV(JH7110_SYSCLK_AUDIO_ROOT, "audio_root", 8, JH7110_SYSCLK_PLL2_OUT),
+       JH71X0__DIV(JH7110_SYSCLK_MCLK_INNER, "mclk_inner", 64, JH7110_SYSCLK_AUDIO_ROOT),
+-      JH71X0__MUX(JH7110_SYSCLK_MCLK, "mclk", 2,
++      JH71X0__MUX(JH7110_SYSCLK_MCLK, "mclk", 0, 2,
+                   JH7110_SYSCLK_MCLK_INNER,
+                   JH7110_SYSCLK_MCLK_EXT),
+       JH71X0_GATE(JH7110_SYSCLK_MCLK_OUT, "mclk_out", 0, JH7110_SYSCLK_MCLK_INNER),
+@@ -96,7 +96,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0__DIV(JH7110_SYSCLK_OSC_DIV2, "osc_div2", 2, JH7110_SYSCLK_OSC),
+       JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV4, "pll1_div4", 2, JH7110_SYSCLK_PLL1_DIV2),
+       JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV8, "pll1_div8", 2, JH7110_SYSCLK_PLL1_DIV4),
+-      JH71X0__MUX(JH7110_SYSCLK_DDR_BUS, "ddr_bus", 4,
++      JH71X0__MUX(JH7110_SYSCLK_DDR_BUS, "ddr_bus", 0, 4,
+                   JH7110_SYSCLK_OSC_DIV2,
+                   JH7110_SYSCLK_PLL1_DIV2,
+                   JH7110_SYSCLK_PLL1_DIV4,
+@@ -186,7 +186,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0__DIV(JH7110_SYSCLK_GMAC1_RMII_RTX, "gmac1_rmii_rtx", 30,
+                   JH7110_SYSCLK_GMAC1_RMII_REFIN),
+       JH71X0_GDIV(JH7110_SYSCLK_GMAC1_PTP, "gmac1_ptp", 0, 31, JH7110_SYSCLK_GMAC_SRC),
+-      JH71X0__MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", 2,
++      JH71X0__MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", 0, 2,
+                   JH7110_SYSCLK_GMAC1_RGMII_RXIN,
+                   JH7110_SYSCLK_GMAC1_RMII_RTX),
+       JH71X0__INV(JH7110_SYSCLK_GMAC1_RX_INV, "gmac1_rx_inv", JH7110_SYSCLK_GMAC1_RX),
+@@ -270,11 +270,11 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_MDIV(JH7110_SYSCLK_I2STX0_LRCK_MST, "i2stx0_lrck_mst", 64, 2,
+                   JH7110_SYSCLK_I2STX0_BCLK_MST_INV,
+                   JH7110_SYSCLK_I2STX0_BCLK_MST),
+-      JH71X0__MUX(JH7110_SYSCLK_I2STX0_BCLK, "i2stx0_bclk",   2,
++      JH71X0__MUX(JH7110_SYSCLK_I2STX0_BCLK, "i2stx0_bclk", 0, 2,
+                   JH7110_SYSCLK_I2STX0_BCLK_MST,
+                   JH7110_SYSCLK_I2STX_BCLK_EXT),
+       JH71X0__INV(JH7110_SYSCLK_I2STX0_BCLK_INV, "i2stx0_bclk_inv", JH7110_SYSCLK_I2STX0_BCLK),
+-      JH71X0__MUX(JH7110_SYSCLK_I2STX0_LRCK, "i2stx0_lrck", 2,
++      JH71X0__MUX(JH7110_SYSCLK_I2STX0_LRCK, "i2stx0_lrck", 0, 2,
+                   JH7110_SYSCLK_I2STX0_LRCK_MST,
+                   JH7110_SYSCLK_I2STX_LRCK_EXT),
+       /* i2stx1 */
+@@ -285,11 +285,11 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_MDIV(JH7110_SYSCLK_I2STX1_LRCK_MST, "i2stx1_lrck_mst", 64, 2,
+                   JH7110_SYSCLK_I2STX1_BCLK_MST_INV,
+                   JH7110_SYSCLK_I2STX1_BCLK_MST),
+-      JH71X0__MUX(JH7110_SYSCLK_I2STX1_BCLK, "i2stx1_bclk", 2,
++      JH71X0__MUX(JH7110_SYSCLK_I2STX1_BCLK, "i2stx1_bclk", 0, 2,
+                   JH7110_SYSCLK_I2STX1_BCLK_MST,
+                   JH7110_SYSCLK_I2STX_BCLK_EXT),
+       JH71X0__INV(JH7110_SYSCLK_I2STX1_BCLK_INV, "i2stx1_bclk_inv", JH7110_SYSCLK_I2STX1_BCLK),
+-      JH71X0__MUX(JH7110_SYSCLK_I2STX1_LRCK, "i2stx1_lrck", 2,
++      JH71X0__MUX(JH7110_SYSCLK_I2STX1_LRCK, "i2stx1_lrck", 0, 2,
+                   JH7110_SYSCLK_I2STX1_LRCK_MST,
+                   JH7110_SYSCLK_I2STX_LRCK_EXT),
+       /* i2srx */
+@@ -300,11 +300,11 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_MDIV(JH7110_SYSCLK_I2SRX_LRCK_MST, "i2srx_lrck_mst", 64, 2,
+                   JH7110_SYSCLK_I2SRX_BCLK_MST_INV,
+                   JH7110_SYSCLK_I2SRX_BCLK_MST),
+-      JH71X0__MUX(JH7110_SYSCLK_I2SRX_BCLK, "i2srx_bclk", 2,
++      JH71X0__MUX(JH7110_SYSCLK_I2SRX_BCLK, "i2srx_bclk", 0, 2,
+                   JH7110_SYSCLK_I2SRX_BCLK_MST,
+                   JH7110_SYSCLK_I2SRX_BCLK_EXT),
+       JH71X0__INV(JH7110_SYSCLK_I2SRX_BCLK_INV, "i2srx_bclk_inv", JH7110_SYSCLK_I2SRX_BCLK),
+-      JH71X0__MUX(JH7110_SYSCLK_I2SRX_LRCK, "i2srx_lrck", 2,
++      JH71X0__MUX(JH7110_SYSCLK_I2SRX_LRCK, "i2srx_lrck", 0, 2,
+                   JH7110_SYSCLK_I2SRX_LRCK_MST,
+                   JH7110_SYSCLK_I2SRX_LRCK_EXT),
+       /* pdm */
+@@ -314,7 +314,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GATE(JH7110_SYSCLK_TDM_AHB, "tdm_ahb", 0, JH7110_SYSCLK_AHB0),
+       JH71X0_GATE(JH7110_SYSCLK_TDM_APB, "tdm_apb", 0, JH7110_SYSCLK_APB0),
+       JH71X0_GDIV(JH7110_SYSCLK_TDM_INTERNAL, "tdm_internal", 0, 64, JH7110_SYSCLK_MCLK),
+-      JH71X0__MUX(JH7110_SYSCLK_TDM_TDM, "tdm_tdm", 2,
++      JH71X0__MUX(JH7110_SYSCLK_TDM_TDM, "tdm_tdm", 0, 2,
+                   JH7110_SYSCLK_TDM_INTERNAL,
+                   JH7110_SYSCLK_TDM_EXT),
+       JH71X0__INV(JH7110_SYSCLK_TDM_TDM_INV, "tdm_tdm_inv", JH7110_SYSCLK_TDM_TDM),
+--- a/drivers/clk/starfive/clk-starfive-jh71x0.h
++++ b/drivers/clk/starfive/clk-starfive-jh71x0.h
+@@ -61,10 +61,10 @@ struct jh71x0_clk_data {
+       .parents = { [0] = _parent },                                           \
+ }
+-#define JH71X0__MUX(_idx, _name, _nparents, ...)                              \
++#define JH71X0__MUX(_idx, _name, _flags, _nparents, ...)                      \
+ [_idx] = {                                                                    \
+       .name = _name,                                                          \
+-      .flags = 0,                                                             \
++      .flags = _flags,                                                        \
+       .max = ((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT,                       \
+       .parents = { __VA_ARGS__ },                                             \
+ }
diff --git a/target/linux/starfive/patches-6.6/1007-clk-starfive-jh7100-Add-CLK_SET_RATE_PARENT-to-gmac_.patch b/target/linux/starfive/patches-6.6/1007-clk-starfive-jh7100-Add-CLK_SET_RATE_PARENT-to-gmac_.patch
new file mode 100644 (file)
index 0000000..74e695c
--- /dev/null
@@ -0,0 +1,25 @@
+From 7dfed4b67bd6ba8c33caf90c01821b67cf3260dd Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Sat, 25 Mar 2023 23:04:31 +0100
+Subject: [PATCH 1007/1024] clk: starfive: jh7100: Add CLK_SET_RATE_PARENT to
+ gmac_tx
+
+This is needed by the dwmac-starfive ethernet driver to set the clock
+for 1000, 100 and 10 Mbps links properly.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+---
+ drivers/clk/starfive/clk-starfive-jh7100.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7100.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100.c
+@@ -200,7 +200,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV),
+       JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+       JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+-      JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 0, 3,
++      JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 3,
+                   JH7100_CLK_GMAC_GTX,
+                   JH7100_CLK_GMAC_TX_INV,
+                   JH7100_CLK_GMAC_RMII_TX),
diff --git a/target/linux/starfive/patches-6.6/1008-clk-starfive-jh7100-Keep-more-clocks-alive.patch b/target/linux/starfive/patches-6.6/1008-clk-starfive-jh7100-Keep-more-clocks-alive.patch
new file mode 100644 (file)
index 0000000..27346b4
--- /dev/null
@@ -0,0 +1,94 @@
+From f8d08ec17674e56c7c689f5ca43c4dd3a4b76d13 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Thu, 14 Oct 2021 20:35:43 +0200
+Subject: [PATCH 1008/1024] clk: starfive: jh7100: Keep more clocks alive
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/clk/starfive/clk-starfive-jh7100.c | 37 +++++++++++-----------
+ 1 file changed, 19 insertions(+), 18 deletions(-)
+
+--- a/drivers/clk/starfive/clk-starfive-jh7100.c
++++ b/drivers/clk/starfive/clk-starfive-jh7100.c
+@@ -94,9 +94,9 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI),
+       JH71X0_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS),
+       JH71X0__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT),
+-      JH71X0_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS),
+-      JH71X0_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS),
+-      JH71X0_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS),
++      JH71X0_GATE(JH7100_CLK_DLA_AXI, "dla_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DLA_BUS),
++      JH71X0_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DLA_BUS),
++      JH71X0_GATE(JH7100_CLK_DLA_APB, "dla_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB1_BUS),
+       JH71X0_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV),
+       JH71X0__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT),
+       JH71X0_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC),
+@@ -160,11 +160,12 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS),
+       JH71X0_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV),
+       JH71X0__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV),
+-      JH71X0_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS),
+-      JH71X0_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS),
++      JH71X0_GATE(JH7100_CLK_USB_AXI, "usb_axi", CLK_IGNORE_UNUSED, JH7100_CLK_USB_BUS),
++      JH71X0_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_USB_BUS),
+       JH71X0__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT),
+-      JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV),
+-      JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32,
++      JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", CLK_IGNORE_UNUSED, 8,
++                  JH7100_CLK_USBPHY_ROOTDIV),
++      JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", CLK_IGNORE_UNUSED, 32,
+                   JH7100_CLK_USBPHY_ROOTDIV),
+       JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 0, 2,
+                   JH7100_CLK_OSC_SYS,
+@@ -183,23 +184,23 @@ static const struct jh71x0_clk_data jh71
+       JH71X0__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC),
+       JH71X0_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS),
+       JH71X0_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS),
+-      JH71X0_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT),
++      JH71X0_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", CLK_IGNORE_UNUSED, 4, JH7100_CLK_VOUT_ROOT),
+       JH71X0__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT),
+       JH71X0__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC),
+-      JH71X0_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS),
+-      JH71X0_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS),
++      JH71X0_GATE(JH7100_CLK_DISP_AXI, "disp_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DISP_BUS),
++      JH71X0_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", CLK_IGNORE_UNUSED, JH7100_CLK_DISP_BUS),
+       JH71X0_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS),
+       JH71X0_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC),
+       JH71X0__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT),
+       JH71X0_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS),
+       JH71X0_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC),
+       JH71X0__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT),
+-      JH71X0_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS),
++      JH71X0_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", CLK_IGNORE_UNUSED, JH7100_CLK_AHB_BUS),
+       JH71X0__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT),
+-      JH71X0_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV),
+-      JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV),
+-      JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
+-      JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF),
++      JH71X0_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", CLK_IGNORE_UNUSED, 31, JH7100_CLK_GMAC_ROOT_DIV),
++      JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", CLK_IGNORE_UNUSED, 255, JH7100_CLK_GMAC_ROOT_DIV),
++      JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", CLK_IGNORE_UNUSED, 8, JH7100_CLK_GMAC_RMII_REF),
++      JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", CLK_IGNORE_UNUSED, 8, JH7100_CLK_GMAC_RMII_REF),
+       JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 3,
+                   JH7100_CLK_GMAC_GTX,
+                   JH7100_CLK_GMAC_TX_INV,
+@@ -209,8 +210,8 @@ static const struct jh71x0_clk_data jh71
+                   JH7100_CLK_GMAC_GR_MII_RX,
+                   JH7100_CLK_GMAC_RMII_RX),
+       JH71X0__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE),
+-      JH71X0_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF),
+-      JH71X0_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV),
++      JH71X0_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", CLK_IGNORE_UNUSED, JH7100_CLK_GMAC_RMII_REF),
++      JH71X0_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", CLK_IGNORE_UNUSED, 127, JH7100_CLK_GMAC_ROOT_DIV),
+       JH71X0_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS),
+       JH71X0_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC),
+       JH71X0_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS),
+@@ -223,7 +224,7 @@ static const struct jh71x0_clk_data jh71
+       JH71X0_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB),
+       JH71X0_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB),
+       JH71X0_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB),
+-      JH71X0_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS),
++      JH71X0_GATE(JH7100_CLK_TRNG_APB, "trng_apb", CLK_IGNORE_UNUSED, JH7100_CLK_APB1_BUS),
+       JH71X0_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS),
+       JH71X0_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS),
+       JH71X0_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC),
diff --git a/target/linux/starfive/patches-6.6/1009-net-stmmac-use-GFP_DMA32.patch b/target/linux/starfive/patches-6.6/1009-net-stmmac-use-GFP_DMA32.patch
new file mode 100644 (file)
index 0000000..ad8b92f
--- /dev/null
@@ -0,0 +1,30 @@
+From 980f1e9ef19d472e72c36e142db7fb4e224f0f3e Mon Sep 17 00:00:00 2001
+From: Matteo Croce <technoboy85@gmail.com>
+Date: Fri, 21 May 2021 03:26:38 +0200
+Subject: [PATCH 1009/1024] net: stmmac: use GFP_DMA32
+
+Signed-off-by: Matteo Croce <mcroce@microsoft.com>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -1434,7 +1434,7 @@ static int stmmac_init_rx_buffers(struct
+ {
+       struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+       struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+-      gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
++      gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+       if (priv->dma_cap.host_dma_width <= 32)
+               gfp |= GFP_DMA32;
+@@ -4673,7 +4673,7 @@ static inline void stmmac_rx_refill(stru
+       struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+       int dirty = stmmac_rx_dirty(priv, queue);
+       unsigned int entry = rx_q->dirty_rx;
+-      gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
++      gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+       if (priv->dma_cap.host_dma_width <= 32)
+               gfp |= GFP_DMA32;
diff --git a/target/linux/starfive/patches-6.6/1010-hwrng-Add-StarFive-JH7100-Random-Number-Generator-dr.patch b/target/linux/starfive/patches-6.6/1010-hwrng-Add-StarFive-JH7100-Random-Number-Generator-dr.patch
new file mode 100644 (file)
index 0000000..b625014
--- /dev/null
@@ -0,0 +1,477 @@
+From 4989e7aa5ed5ef9bc2532b3a47ff381572f389b5 Mon Sep 17 00:00:00 2001
+From: Huan Feng <huan.feng@starfivetech.com>
+Date: Fri, 8 Jan 2021 03:35:42 +0800
+Subject: [PATCH 1010/1024] hwrng: Add StarFive JH7100 Random Number Generator
+ driver
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/char/hw_random/Kconfig            |  13 ++
+ drivers/char/hw_random/Makefile           |   1 +
+ drivers/char/hw_random/starfive-vic-rng.c | 256 ++++++++++++++++++++++
+ drivers/char/hw_random/starfive-vic-rng.h | 167 ++++++++++++++
+ 4 files changed, 437 insertions(+)
+ create mode 100644 drivers/char/hw_random/starfive-vic-rng.c
+ create mode 100644 drivers/char/hw_random/starfive-vic-rng.h
+
+--- a/drivers/char/hw_random/Kconfig
++++ b/drivers/char/hw_random/Kconfig
+@@ -322,6 +322,19 @@ config HW_RANDOM_POWERNV
+         If unsure, say Y.
++config HW_RANDOM_STARFIVE_VIC
++      tristate "Starfive VIC Random Number Generator support"
++      depends on HW_RANDOM && (SOC_STARFIVE || COMPILE_TEST)
++      default SOC_STARFIVE
++      help
++        This driver provides kernel-side support for the Random Number
++        Generator hardware found on Starfive VIC SoC.
++
++        To compile this driver as a module, choose M here: the
++        module will be called starfive-vic-rng.
++
++        If unsure, say Y.
++
+ config HW_RANDOM_HISI
+       tristate "Hisilicon Random Number Generator support"
+       depends on ARCH_HISI || COMPILE_TEST
+--- a/drivers/char/hw_random/Makefile
++++ b/drivers/char/hw_random/Makefile
+@@ -28,6 +28,7 @@ obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon
+ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
+ obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
+ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
++obj-$(CONFIG_HW_RANDOM_STARFIVE_VIC)  += starfive-vic-rng.o
+ obj-$(CONFIG_HW_RANDOM_HISI)  += hisi-rng.o
+ obj-$(CONFIG_HW_RANDOM_HISTB) += histb-rng.o
+ obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+--- /dev/null
++++ b/drivers/char/hw_random/starfive-vic-rng.c
+@@ -0,0 +1,256 @@
++/*
++ ******************************************************************************
++ * @file  starfive-vic-rng.c
++ * @author  StarFive Technology
++ * @version  V1.0
++ * @date  08/13/2020
++ * @brief
++ ******************************************************************************
++ * @copy
++ *
++ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
++ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
++ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
++ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
++ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
++ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++ *
++ * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
++ */
++#include <linux/err.h>
++#include <linux/kernel.h>
++#include <linux/hw_random.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/random.h>
++
++#include "starfive-vic-rng.h"
++
++#define to_vic_rng(p) container_of(p, struct vic_rng, rng)
++
++struct vic_rng {
++      struct device   *dev;
++      void __iomem    *base;
++      struct hwrng    rng;
++};
++
++static inline void vic_wait_till_idle(struct vic_rng *hrng)
++{
++      while(readl(hrng->base + VIC_STAT) & VIC_STAT_BUSY)
++              ;
++}
++
++static inline void vic_rng_irq_mask_clear(struct vic_rng *hrng)
++{
++      // clear register: ISTAT
++      u32 data = readl(hrng->base + VIC_ISTAT);
++      writel(data, hrng->base + VIC_ISTAT);
++      writel(0, hrng->base + VIC_ALARM);
++}
++
++static int vic_trng_cmd(struct vic_rng *hrng, u32 cmd) {
++      int res = 0;
++      // wait till idle
++      vic_wait_till_idle(hrng);
++      switch (cmd) {
++      case VIC_CTRL_CMD_NOP:
++      case VIC_CTRL_CMD_GEN_NOISE:
++      case VIC_CTRL_CMD_GEN_NONCE:
++      case VIC_CTRL_CMD_CREATE_STATE:
++      case VIC_CTRL_CMD_RENEW_STATE:
++      case VIC_CTRL_CMD_REFRESH_ADDIN:
++      case VIC_CTRL_CMD_GEN_RANDOM:
++      case VIC_CTRL_CMD_ADVANCE_STATE:
++      case VIC_CTRL_CMD_KAT:
++      case VIC_CTRL_CMD_ZEROIZE:
++              writel(cmd, hrng->base + VIC_CTRL);
++              break;
++      default:
++              res = -1;
++              break;
++      }
++
++      return res;
++}
++
++static int vic_rng_init(struct hwrng *rng)
++{
++      struct vic_rng *hrng = to_vic_rng(rng);
++
++      // wait till idle
++
++      // clear register: ISTAT
++      vic_rng_irq_mask_clear(hrng);
++
++      // set mission mode
++      writel(VIC_SMODE_SECURE_EN(1), hrng->base + VIC_SMODE);
++
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_NOISE);
++      vic_wait_till_idle(hrng);
++
++      // set interrupt
++      writel(VIC_IE_ALL, hrng->base + VIC_IE);
++
++      // zeroize
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE);
++
++      vic_wait_till_idle(hrng);
++
++      return 0;
++}
++
++static irqreturn_t vic_rng_irq(int irq, void *priv)
++{
++      u32 status, val;
++      struct vic_rng *hrng = (struct vic_rng *)priv;
++
++      /*
++       * clearing the interrupt will also clear the error register
++       * read error and status before clearing
++       */
++      status = readl(hrng->base + VIC_ISTAT);
++
++      if (status & VIC_ISTAT_ALARMS) {
++              writel(VIC_ISTAT_ALARMS, hrng->base + VIC_ISTAT);
++              val = readl(hrng->base + VIC_ALARM);
++              if (val & VIC_ALARM_ILLEGAL_CMD_SEQ) {
++                      writel(VIC_ALARM_ILLEGAL_CMD_SEQ, hrng->base + VIC_ALARM);
++                      //dev_info(hrng->dev, "ILLEGAL CMD SEQ: LAST_CMD=0x%x\r\n",
++                      //VIC_STAT_LAST_CMD(readl(hrng->base + VIC_STAT)));
++              } else {
++                      dev_info(hrng->dev, "Failed test: %x\r\n", val);
++              }
++      }
++
++      if (status & VIC_ISTAT_ZEROIZE) {
++              writel(VIC_ISTAT_ZEROIZE, hrng->base + VIC_ISTAT);
++              //dev_info(hrng->dev, "zeroized\r\n");
++      }
++
++      if (status & VIC_ISTAT_KAT_COMPLETE) {
++              writel(VIC_ISTAT_KAT_COMPLETE, hrng->base + VIC_ISTAT);
++              //dev_info(hrng->dev, "kat_completed\r\n");
++      }
++
++      if (status & VIC_ISTAT_NOISE_RDY) {
++              writel(VIC_ISTAT_NOISE_RDY, hrng->base + VIC_ISTAT);
++              //dev_info(hrng->dev, "noise_rdy\r\n");
++      }
++
++      if (status & VIC_ISTAT_DONE) {
++              writel(VIC_ISTAT_DONE, hrng->base + VIC_ISTAT);
++              //dev_info(hrng->dev, "done\r\n");
++              /*
++              if (VIC_STAT_LAST_CMD(readl(hrng->base + VIC_STAT)) ==
++                  VIC_CTRL_CMD_GEN_RANDOM) {
++                      dev_info(hrng->dev, "Need Update Buffer\r\n");
++              }
++              */
++      }
++      vic_rng_irq_mask_clear(hrng);
++
++      return IRQ_HANDLED;
++}
++
++static void vic_rng_cleanup(struct hwrng *rng)
++{
++      struct vic_rng *hrng = to_vic_rng(rng);
++
++      writel(0, hrng->base + VIC_CTRL);
++}
++
++static int vic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
++{
++      struct vic_rng *hrng = to_vic_rng(rng);
++
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE);
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_NOISE);
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_CREATE_STATE);
++
++      vic_wait_till_idle(hrng);
++      max = min_t(size_t, max, (VIC_RAND_LEN * 4));
++
++      writel(0x0, hrng->base + VIC_MODE);
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_GEN_RANDOM);
++
++      vic_wait_till_idle(hrng);
++      memcpy_fromio(buf, hrng->base + VIC_RAND0, max);
++      vic_trng_cmd(hrng, VIC_CTRL_CMD_ZEROIZE);
++
++      vic_wait_till_idle(hrng);
++      return max;
++}
++
++static int vic_rng_probe(struct platform_device *pdev)
++{
++      int ret;
++      int irq;
++      struct vic_rng *rng;
++      struct resource *res;
++
++      rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
++      if (!rng){
++              return -ENOMEM;
++      }
++
++      platform_set_drvdata(pdev, rng);
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      rng->base = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(rng->base)){
++              return PTR_ERR(rng->base);
++      }
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq <= 0) {
++              dev_err(&pdev->dev, "Couldn't get irq %d\n", irq);
++              return irq;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irq, vic_rng_irq, 0, pdev->name,
++                              (void *)rng);
++      if (ret) {
++              dev_err(&pdev->dev, "Can't get interrupt working.\n");
++              return ret;
++      }
++
++      rng->rng.name = pdev->name;
++      rng->rng.init = vic_rng_init;
++      rng->rng.cleanup = vic_rng_cleanup;
++      rng->rng.read = vic_rng_read;
++
++      rng->dev = &pdev->dev;
++
++      ret = devm_hwrng_register(&pdev->dev, &rng->rng);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to register hwrng\n");
++              return ret;
++      }
++
++      dev_info(&pdev->dev, "Initialized\n");
++
++      return 0;
++}
++
++static const struct of_device_id vic_rng_dt_ids[] = {
++      { .compatible = "starfive,vic-rng" },
++      { }
++};
++MODULE_DEVICE_TABLE(of, vic_rng_dt_ids);
++
++static struct platform_driver vic_rng_driver = {
++      .probe          = vic_rng_probe,
++      .driver         = {
++              .name           = "vic-rng",
++              .of_match_table = vic_rng_dt_ids,
++      },
++};
++
++module_platform_driver(vic_rng_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Huan Feng <huan.feng@starfivetech.com>");
++MODULE_DESCRIPTION("Starfive VIC random number generator driver");
+--- /dev/null
++++ b/drivers/char/hw_random/starfive-vic-rng.h
+@@ -0,0 +1,167 @@
++/*
++ ******************************************************************************
++ * @file  starfive-vic-rng.h
++ * @author  StarFive Technology
++ * @version  V1.0
++ * @date  08/13/2020
++ * @brief
++ ******************************************************************************
++ * @copy
++ *
++ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
++ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
++ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
++ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
++ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
++ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++ *
++ * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
++ */
++
++#define VIC_CTRL              0x00
++#define VIC_MODE              0x04
++#define VIC_SMODE             0x08
++#define VIC_STAT              0x0C
++#define VIC_IE                        0x10
++#define VIC_ISTAT             0x14
++#define VIC_ALARM             0x18
++#define VIC_BUILD_ID          0x1C
++#define VIC_FEATURES          0x20
++#define VIC_RAND0             0x24
++#define VIC_NPA_DATA0         0x34
++#define VIC_SEED0             0x74
++#define VIC_IA_RDATA          0xA4
++#define VIC_IA_WDATA          0xA8
++#define VIC_IA_ADDR           0xAC
++#define VIC_IA_CMD            0xB0
++
++/* CTRL */
++#define VIC_CTRL_CMD_NOP              0
++#define VIC_CTRL_CMD_GEN_NOISE                1
++#define VIC_CTRL_CMD_GEN_NONCE                2
++#define VIC_CTRL_CMD_CREATE_STATE     3
++#define VIC_CTRL_CMD_RENEW_STATE      4
++#define VIC_CTRL_CMD_REFRESH_ADDIN    5
++#define VIC_CTRL_CMD_GEN_RANDOM               6
++#define VIC_CTRL_CMD_ADVANCE_STATE    7
++#define VIC_CTRL_CMD_KAT              8
++#define VIC_CTRL_CMD_ZEROIZE          15
++
++/* MODE */
++#define _VIC_MODE_ADDIN_PRESENT               4
++#define _VIC_MODE_PRED_RESIST         3
++#define _VIC_MODE_KAT_SEL             2
++#define _VIC_MODE_KAT_VEC             1
++#define _VIC_MODE_SEC_ALG             0
++
++#define VIC_MODE_ADDIN_PRESENT        (1UL << _VIC_MODE_ADDIN_PRESENT)
++#define VIC_MODE_PRED_RESIST  (1UL << _VIC_MODE_PRED_RESIST)
++#define VIC_MODE_KAT_SEL      (1UL << _VIC_MODE_KAT_SEL)
++#define VIC_MODE_KAT_VEC      (1UL << _VIC_MODE_KAT_VEC)
++#define VIC_MODE_SEC_ALG      (1UL << _VIC_MODE_SEC_ALG)
++
++/* SMODE */
++#define _VIC_SMODE_MAX_REJECTS        2
++#define _VIC_SMODE_SECURE_EN  1
++#define _VIC_SMODE_NONCE      0
++
++#define VIC_SMODE_MAX_REJECTS(x)      ((x) << _VIC_SMODE_MAX_REJECTS)
++#define VIC_SMODE_SECURE_EN(x)                ((x) << _VIC_SMODE_SECURE_EN)
++#define VIC_SMODE_NONCE                       (1UL << _VIC_SMODE_NONCE)
++
++/* STAT */
++#define _VIC_STAT_BUSY                31
++#define _VIC_STAT_DRBG_STATE  7
++#define _VIC_STAT_SECURE      6
++#define _VIC_STAT_NONCE_MODE  5
++#define _VIC_STAT_SEC_ALG     4
++#define _VIC_STAT_LAST_CMD    0
++
++#define VIC_STAT_BUSY         (1UL << _VIC_STAT_BUSY)
++#define VIC_STAT_DRBG_STATE   (1UL << _VIC_STAT_DRBG_STATE)
++#define VIC_STAT_SECURE               (1UL << _VIC_STAT_SECURE)
++#define VIC_STAT_NONCE_MODE   (1UL << _VIC_STAT_NONCE_MODE)
++#define VIC_STAT_SEC_ALG      (1UL << _VIC_STAT_SEC_ALG)
++#define VIC_STAT_LAST_CMD(x)  (((x) >> _VIC_STAT_LAST_CMD) & 0xF)
++
++/* IE */
++#define _VIC_IE_GLBL          31
++#define _VIC_IE_DONE          4
++#define _VIC_IE_ALARMS                3
++#define _VIC_IE_NOISE_RDY     2
++#define _VIC_IE_KAT_COMPLETE  1
++#define _VIC_IE_ZEROIZE               0
++
++#define VIC_IE_GLBL           (1UL << _VIC_IE_GLBL)
++#define VIC_IE_DONE           (1UL << _VIC_IE_DONE)
++#define VIC_IE_ALARMS         (1UL << _VIC_IE_ALARMS)
++#define VIC_IE_NOISE_RDY      (1UL << _VIC_IE_NOISE_RDY)
++#define VIC_IE_KAT_COMPLETE   (1UL << _VIC_IE_KAT_COMPLETE)
++#define VIC_IE_ZEROIZE                (1UL << _VIC_IE_ZEROIZE)
++#define VIC_IE_ALL            (VIC_IE_GLBL | VIC_IE_DONE | VIC_IE_ALARMS | \
++                               VIC_IE_NOISE_RDY | VIC_IE_KAT_COMPLETE | VIC_IE_ZEROIZE)
++
++/* ISTAT */
++#define _VIC_ISTAT_DONE               4
++#define _VIC_ISTAT_ALARMS     3
++#define _VIC_ISTAT_NOISE_RDY  2
++#define _VIC_ISTAT_KAT_COMPLETE       1
++#define _VIC_ISTAT_ZEROIZE    0
++
++#define VIC_ISTAT_DONE                (1UL << _VIC_ISTAT_DONE)
++#define VIC_ISTAT_ALARMS      (1UL << _VIC_ISTAT_ALARMS)
++#define VIC_ISTAT_NOISE_RDY   (1UL << _VIC_ISTAT_NOISE_RDY)
++#define VIC_ISTAT_KAT_COMPLETE        (1UL << _VIC_ISTAT_KAT_COMPLETE)
++#define VIC_ISTAT_ZEROIZE     (1UL << _VIC_ISTAT_ZEROIZE)
++
++/* ALARMS */
++#define VIC_ALARM_ILLEGAL_CMD_SEQ                     (1UL << 4)
++#define VIC_ALARM_FAILED_TEST_ID_OK                   0
++#define VIC_ALARM_FAILED_TEST_ID_KAT_STAT             1
++#define VIC_ALARM_FAILED_TEST_ID_KAT                  2
++#define VIC_ALARM_FAILED_TEST_ID_MONOBIT              3
++#define VIC_ALARM_FAILED_TEST_ID_RUN                  4
++#define VIC_ALARM_FAILED_TEST_ID_LONGRUN              5
++#define VIC_ALARM_FAILED_TEST_ID_AUTOCORRELATION      6
++#define VIC_ALARM_FAILED_TEST_ID_POKER                        7
++#define VIC_ALARM_FAILED_TEST_ID_REPETITION_COUNT     8
++#define VIC_ALARM_FAILED_TEST_ID_ADAPATIVE_PROPORTION 9
++
++/* BUILD_ID */
++#define VIC_BUILD_ID_STEPPING(x)              (((x) >> 28) & 0xF)
++#define VIC_BUILD_ID_EPN(x)                   ((x) & 0xFFFF)
++
++/* FEATURES */
++#define VIC_FEATURES_AES_256(x)                       (((x) >> 9) & 1)
++#define VIC_FEATURES_EXTRA_PS_PRESENT(x)      (((x) >> 8) & 1)
++#define VIC_FEATURES_DIAG_LEVEL_NS(x)         (((x) >> 7) & 1)
++#define VIC_FEATURES_DIAG_LEVEL_CLP800(x)     (((x) >> 4) & 7)
++#define VIC_FEATURES_DIAG_LEVEL_ST_HLT(x)     (((x) >> 1) & 7)
++#define VIC_FEATURES_SECURE_RST_STATE(x)      ((x) & 1)
++
++/* IA_CMD */
++#define VIC_IA_CMD_GO                 (1UL << 31)
++#define VIC_IA_CMD_WR                 (1)
++
++#define _VIC_SMODE_MAX_REJECTS_MASK   255UL
++#define _VIC_SMODE_SECURE_EN_MASK     1UL
++#define _VIC_SMODE_NONCE_MASK         1UL
++#define _VIC_MODE_SEC_ALG_MASK                1UL
++#define _VIC_MODE_ADDIN_PRESENT_MASK  1UL
++#define _VIC_MODE_PRED_RESIST_MASK    1UL
++
++#define VIC_SMODE_SET_MAX_REJECTS(y, x)       (((y) & ~(_VIC_SMODE_MAX_REJECTS_MASK << _VIC_SMODE_MAX_REJECTS)) | ((x) << _VIC_SMODE_MAX_REJECTS))
++#define VIC_SMODE_SET_SECURE_EN(y, x) (((y) & ~(_VIC_SMODE_SECURE_EN_MASK   << _VIC_SMODE_SECURE_EN))   | ((x) << _VIC_SMODE_SECURE_EN))
++#define VIC_SMODE_SET_NONCE(y, x)     (((y) & ~(_VIC_SMODE_NONCE_MASK       << _VIC_SMODE_NONCE))       | ((x) << _VIC_SMODE_NONCE))
++#define VIC_SMODE_GET_MAX_REJECTS(x)  (((x) >> _VIC_SMODE_MAX_REJECTS) & _VIC_SMODE_MAX_REJECTS_MASK)
++#define VIC_SMODE_GET_SECURE_EN(x)    (((x) >> _VIC_SMODE_SECURE_EN)   & _VIC_SMODE_SECURE_EN_MASK)
++#define VIC_SMODE_GET_NONCE(x)                (((x) >> _VIC_SMODE_NONCE)       & _VIC_SMODE_NONCE_MASK)
++
++#define VIC_MODE_SET_SEC_ALG(y, x)    (((y) & ~(_VIC_MODE_SEC_ALG_MASK       << _VIC_MODE_SEC_ALG))   | ((x) << _VIC_MODE_SEC_ALG))
++#define VIC_MODE_SET_PRED_RESIST(y, x)        (((y) & ~(_VIC_MODE_PRED_RESIST_MASK   << _VIC_MODE_PRED_RESIST))    | ((x) << _VIC_MODE_PRED_RESIST))
++#define VIC_MODE_SET_ADDIN_PRESENT(y, x) (((y) & ~(_VIC_MODE_ADDIN_PRESENT_MASK << _VIC_MODE_ADDIN_PRESENT))  | ((x) << _VIC_MODE_ADDIN_PRESENT))
++#define VIC_MODE_GET_SEC_ALG(x)               (((x) >> _VIC_MODE_SEC_ALG)       & _VIC_MODE_SEC_ALG_MASK)
++#define VIC_MODE_GET_PRED_RESIST(x)   (((x) >> _VIC_MODE_PRED_RESIST)   & _VIC_MODE_PRED_RESIST_MASK)
++#define VIC_MODE_GET_ADDIN_PRESENT(x) (((x) >> _VIC_MODE_ADDIN_PRESENT) & _VIC_MODE_ADDIN_PRESENT_MASK)
++
++#define VIC_RAND_LEN 4
diff --git a/target/linux/starfive/patches-6.6/1011-pwm-sifive-ptc-Add-SiFive-PWM-PTC-driver.patch b/target/linux/starfive/patches-6.6/1011-pwm-sifive-ptc-Add-SiFive-PWM-PTC-driver.patch
new file mode 100644 (file)
index 0000000..7a588c2
--- /dev/null
@@ -0,0 +1,309 @@
+From 34fd7b7f92deef97f03c80d056e2f51bc08b7dc6 Mon Sep 17 00:00:00 2001
+From: Chenjieqin <Jessica.Chen@starfivetech.com>
+Date: Fri, 8 Jan 2021 03:56:54 +0800
+Subject: [PATCH 1011/1024] pwm: sifive-ptc: Add SiFive PWM PTC driver
+
+yiming.li: clear CNTR of PWM after setting period & duty_cycle
+Emil: cleanups, clock, reset and div_u64
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ drivers/pwm/Kconfig          |  11 ++
+ drivers/pwm/Makefile         |   1 +
+ drivers/pwm/pwm-sifive-ptc.c | 260 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 272 insertions(+)
+ create mode 100644 drivers/pwm/pwm-sifive-ptc.c
+
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -549,6 +549,17 @@ config PWM_SIFIVE
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-sifive.
++config PWM_SIFIVE_PTC
++      tristate "SiFive PWM PTC support"
++      depends on SOC_SIFIVE || SOC_STARFIVE || COMPILE_TEST
++      depends on OF
++      depends on COMMON_CLK
++      help
++        Generic PWM framework driver for SiFive SoCs.
++
++        To compile this driver as a module, choose M here: the module
++        will be called pwm-sifive-ptc.
++
+ config PWM_SL28CPLD
+       tristate "Kontron sl28cpld PWM support"
+       depends on MFD_SL28CPLD || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -50,6 +50,7 @@ obj-$(CONFIG_PWM_ROCKCHIP)   += pwm-rockch
+ obj-$(CONFIG_PWM_RZ_MTU3)     += pwm-rz-mtu3.o
+ obj-$(CONFIG_PWM_SAMSUNG)     += pwm-samsung.o
+ obj-$(CONFIG_PWM_SIFIVE)      += pwm-sifive.o
++obj-$(CONFIG_PWM_SIFIVE_PTC)  += pwm-sifive-ptc.o
+ obj-$(CONFIG_PWM_SL28CPLD)    += pwm-sl28cpld.o
+ obj-$(CONFIG_PWM_SPEAR)               += pwm-spear.o
+ obj-$(CONFIG_PWM_SPRD)                += pwm-sprd.o
+--- /dev/null
++++ b/drivers/pwm/pwm-sifive-ptc.c
+@@ -0,0 +1,260 @@
++/*
++ * Copyright (C) 2018 SiFive, Inc
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2, as published by
++ * the Free Software Foundation.
++ */
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/reset.h>
++
++#include <dt-bindings/pwm/pwm.h>
++
++/* max channel of pwm */
++#define MAX_PWM                               8
++
++/* PTC Register offsets */
++#define REG_RPTC_CNTR                 0x0
++#define REG_RPTC_HRC                  0x4
++#define REG_RPTC_LRC                  0x8
++#define REG_RPTC_CTRL                 0xC
++
++/* Bit for PWM clock */
++#define BIT_PWM_CLOCK_EN              31
++
++/* Bit for clock gen soft reset */
++#define BIT_CLK_GEN_SOFT_RESET                13
++
++#define NS_1                          1000000000U
++
++/* Access PTC register (cntr hrc lrc and ctrl), need to replace PWM_BASE_ADDR */
++#define REG_PTC_BASE_ADDR_SUB(base, N)        \
++      ((base) + (((N) > 3) ? (((N) - 4) * 0x10 + (1 << 15)) : ((N) * 0x10)))
++#define REG_PTC_RPTC_CNTR(base, N)    (REG_PTC_BASE_ADDR_SUB(base, N))
++#define REG_PTC_RPTC_HRC(base, N)     (REG_PTC_BASE_ADDR_SUB(base, N) + 0x4)
++#define REG_PTC_RPTC_LRC(base, N)     (REG_PTC_BASE_ADDR_SUB(base, N) + 0x8)
++#define REG_PTC_RPTC_CTRL(base, N)    (REG_PTC_BASE_ADDR_SUB(base, N) + 0xC)
++
++/* pwm ptc device */
++struct sifive_pwm_ptc_device {
++      struct pwm_chip chip;
++      struct clk      *clk;
++      void __iomem    *regs;
++};
++
++static inline struct sifive_pwm_ptc_device *chip_to_sifive_ptc(struct pwm_chip *c)
++{
++      return container_of(c, struct sifive_pwm_ptc_device, chip);
++}
++
++static int sifive_pwm_ptc_get_state(struct pwm_chip *chip, struct pwm_device *dev,
++                                  struct pwm_state *state)
++{
++      struct sifive_pwm_ptc_device *pwm = chip_to_sifive_ptc(chip);
++      u32 data_lrc;
++      u32 data_hrc;
++      u32 pwm_clk_ns = 0;
++
++      /* get lrc and hrc data from registe */
++      data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm));
++      data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm));
++
++      /* how many ns does apb clock elapse */
++      pwm_clk_ns = NS_1 / clk_get_rate(pwm->clk);
++
++      /* pwm period(ns) */
++      state->period     = data_lrc * pwm_clk_ns;
++
++      /* duty cycle(ns) means high level eclapse ns if it is normal polarity */
++      state->duty_cycle = data_hrc * pwm_clk_ns;
++
++      /* polarity, we don't use it now because it is not in dts */
++      state->polarity   = PWM_POLARITY_NORMAL;
++
++      /* enabled or not */
++      state->enabled    = 1;
++
++      dev_dbg(pwm->chip.dev, "%s: no:%d\n", __func__, dev->hwpwm);
++      dev_dbg(pwm->chip.dev, "data_hrc:0x%x 0x%x\n", data_hrc, data_lrc);
++      dev_dbg(pwm->chip.dev, "period:%llu\n", state->period);
++      dev_dbg(pwm->chip.dev, "duty_cycle:%llu\n", state->duty_cycle);
++      dev_dbg(pwm->chip.dev, "polarity:%d\n", state->polarity);
++      dev_dbg(pwm->chip.dev, "enabled:%d\n", state->enabled);
++
++      return 0;
++}
++
++static int sifive_pwm_ptc_apply(struct pwm_chip *chip, struct pwm_device *dev,
++                              const struct pwm_state *state)
++{
++      struct sifive_pwm_ptc_device *pwm = chip_to_sifive_ptc(chip);
++      void __iomem *reg_addr;
++      u32 pwm_clk_ns = 0;
++      u32 data_hrc = 0;
++      u32 data_lrc = 0;
++      u32 period_data = 0;
++      u32 duty_data = 0;
++
++      dev_dbg(pwm->chip.dev, "%s: no:%d\n", __func__, dev->hwpwm);
++      dev_dbg(pwm->chip.dev, "period:%llu\n", state->period);
++      dev_dbg(pwm->chip.dev, "duty_cycle:%llu\n", state->duty_cycle);
++      dev_dbg(pwm->chip.dev, "polarity:%d\n", state->polarity);
++      dev_dbg(pwm->chip.dev, "enabled:%d\n", state->enabled);
++
++      /* duty_cycle should be less or equal than period */
++      if (state->duty_cycle > state->period)
++              return -EINVAL;
++
++      /* calculate pwm real period (ns) */
++      pwm_clk_ns = NS_1 / clk_get_rate(pwm->clk);
++
++      dev_dbg(pwm->chip.dev, "pwm_clk_ns:%u\n", pwm_clk_ns);
++
++      /* calculate period count */
++      period_data = div_u64(state->period, pwm_clk_ns);
++
++      if (!state->enabled)
++              /* if disabled, just set duty_data to 0, which means low level always */
++              duty_data = 0;
++      else
++              /* calculate duty count */
++              duty_data = div_u64(state->duty_cycle, pwm_clk_ns);
++
++      dev_dbg(pwm->chip.dev, "period_data:%u, duty_data:%u\n",
++              period_data, duty_data);
++
++      if (state->polarity == PWM_POLARITY_NORMAL)
++              /* calculate data_hrc */
++              data_hrc = period_data - duty_data;
++      else
++              /* calculate data_hrc */
++              data_hrc = duty_data;
++
++      data_lrc = period_data;
++
++      /* set hrc */
++      reg_addr = REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm);
++      dev_dbg(pwm->chip.dev, "%s: reg_addr:%p, data:%u\n",
++              __func__, reg_addr, data_hrc);
++
++      iowrite32(data_hrc, reg_addr);
++
++      dev_dbg(pwm->chip.dev, "%s: hrc ok\n", __func__);
++
++      /* set lrc */
++      reg_addr = REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm);
++      dev_dbg(pwm->chip.dev, "%s: reg_addr:%p, data:%u\n",
++              __func__, reg_addr, data_lrc);
++
++      iowrite32(data_lrc, reg_addr);
++      dev_dbg(pwm->chip.dev, "%s: lrc ok\n", __func__);
++
++      /* Clear REG_RPTC_CNTR after setting period & duty_cycle */
++      reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm);
++      iowrite32(0, reg_addr);
++      return 0;
++}
++
++static const struct pwm_ops sifive_pwm_ptc_ops = {
++      .get_state      = sifive_pwm_ptc_get_state,
++      .apply          = sifive_pwm_ptc_apply,
++      .owner          = THIS_MODULE,
++};
++
++static void sifive_pwm_ptc_disable_action(void *data)
++{
++      clk_disable_unprepare(data);
++}
++
++static int sifive_pwm_ptc_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct device_node *node = pdev->dev.of_node;
++      struct sifive_pwm_ptc_device *pwm;
++      struct pwm_chip *chip;
++      struct reset_control *rst;
++      int ret;
++
++      pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
++      if (!pwm)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, pwm);
++
++      chip = &pwm->chip;
++      chip->dev = dev;
++      chip->ops = &sifive_pwm_ptc_ops;
++
++      /* how many parameters can be transferred to ptc, need to fix */
++      chip->of_pwm_n_cells = 3;
++      chip->base = -1;
++
++      /* get pwm channels count, max value is 8 */
++      ret = of_property_read_u32(node, "starfive,npwm", &chip->npwm);
++      if (ret < 0 || chip->npwm > MAX_PWM)
++              chip->npwm = MAX_PWM;
++
++      dev_dbg(dev, "%s: npwm:0x%x\n", __func__, chip->npwm);
++
++      /* get IO base address */
++      pwm->regs = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(pwm->regs))
++              return dev_err_probe(dev, PTR_ERR(pwm->regs),
++                                   "Unable to map IO resources\n");
++
++      pwm->clk = devm_clk_get(dev, NULL);
++      if (IS_ERR(pwm->clk))
++              return dev_err_probe(dev, PTR_ERR(pwm->clk),
++                                   "Unable to get controller clock\n");
++
++      ret = clk_prepare_enable(pwm->clk);
++      if (ret)
++              return dev_err_probe(dev, ret, "Unable to enable clock\n");
++
++      ret = devm_add_action_or_reset(dev, sifive_pwm_ptc_disable_action, pwm->clk);
++      if (ret)
++              return ret;
++
++      rst = devm_reset_control_get_exclusive(dev, NULL);
++      if (IS_ERR(rst))
++              return dev_err_probe(dev, PTR_ERR(rst), "Unable to get reset\n");
++
++      ret = reset_control_deassert(rst);
++      if (ret)
++              return dev_err_probe(dev, ret, "Unable to deassert reset\n");
++
++      /*
++       * after pwmchip_add it will show up as /sys/class/pwm/pwmchip0,
++       * 0 is chip->base, pwm0 can be seen after running echo 0 > export
++       */
++      ret = devm_pwmchip_add(dev, chip);
++      if (ret)
++              return dev_err_probe(dev, ret, "cannot register PTC: %d\n", ret);
++
++      dev_dbg(dev, "SiFive PWM PTC chip registered %d PWMs\n", chip->npwm);
++      return 0;
++}
++
++static const struct of_device_id sifive_pwm_ptc_of_match[] = {
++      { .compatible = "starfive,pwm0" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, sifive_pwm_ptc_of_match);
++
++static struct platform_driver sifive_pwm_ptc_driver = {
++      .probe = sifive_pwm_ptc_probe,
++      .driver = {
++              .name = "pwm-sifive-ptc",
++              .of_match_table = sifive_pwm_ptc_of_match,
++      },
++};
++module_platform_driver(sifive_pwm_ptc_driver);
++
++MODULE_DESCRIPTION("SiFive PWM PTC driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/starfive/patches-6.6/1012-dt-bindings-reset-Add-StarFive-JH7100-audio-reset-de.patch b/target/linux/starfive/patches-6.6/1012-dt-bindings-reset-Add-StarFive-JH7100-audio-reset-de.patch
new file mode 100644 (file)
index 0000000..6986d4b
--- /dev/null
@@ -0,0 +1,48 @@
+From 4f3335c302b7f944b61f564095505b3c7a1b62ee Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 19:29:25 +0100
+Subject: [PATCH 1012/1024] dt-bindings: reset: Add StarFive JH7100 audio reset
+ definitions
+
+Add all resets for the StarFive JH7100 audio reset controller.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ .../dt-bindings/reset/starfive-jh7100-audio.h | 31 +++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+ create mode 100644 include/dt-bindings/reset/starfive-jh7100-audio.h
+
+--- /dev/null
++++ b/include/dt-bindings/reset/starfive-jh7100-audio.h
+@@ -0,0 +1,31 @@
++/* SPDX-License-Identifier: GPL-2.0 OR MIT */
++/*
++ * Copyright (C) 2021 Emil Renner Berthing
++ */
++
++#ifndef __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__
++#define __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__
++
++#define JH7100_AUDRSTN_APB_BUS                0
++#define JH7100_AUDRSTN_I2SADC_APB     1
++#define JH7100_AUDRSTN_I2SADC_SRST    2
++#define JH7100_AUDRSTN_PDM_APB                3
++#define JH7100_AUDRSTN_I2SVAD_APB     4
++#define JH7100_AUDRSTN_I2SVAD_SRST    5
++#define JH7100_AUDRSTN_SPDIF_APB      6
++#define JH7100_AUDRSTN_PWMDAC_APB     7
++#define JH7100_AUDRSTN_I2SDAC_APB     8
++#define JH7100_AUDRSTN_I2SDAC_SRST    9
++#define JH7100_AUDRSTN_I2S1_APB               10
++#define JH7100_AUDRSTN_I2S1_SRST      11
++#define JH7100_AUDRSTN_I2SDAC16K_APB  12
++#define JH7100_AUDRSTN_I2SDAC16K_SRST 13
++#define JH7100_AUDRSTN_DMA1P_AHB      14
++#define JH7100_AUDRSTN_USB_APB                15
++#define JH7100_AUDRST_USB_AXI         16
++#define JH7100_AUDRST_USB_PWRUP_RST_N 17
++#define JH7100_AUDRST_USB_PONRST      18
++
++#define JH7100_AUDRSTN_END            19
++
++#endif /* __DT_BINDINGS_RESET_STARFIVE_JH7100_AUDIO_H__ */
diff --git a/target/linux/starfive/patches-6.6/1013-dt-bindings-reset-Add-starfive-jh7100-audrst-binding.patch b/target/linux/starfive/patches-6.6/1013-dt-bindings-reset-Add-starfive-jh7100-audrst-binding.patch
new file mode 100644 (file)
index 0000000..5eac29e
--- /dev/null
@@ -0,0 +1,56 @@
+From 1e428568e486b40f78febddf2958ba27289bd49f Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Tue, 7 Dec 2021 21:48:51 +0100
+Subject: [PATCH 1013/1024] dt-bindings: reset: Add starfive,jh7100-audrst
+ bindings
+
+Add bindings for the audio reset controller on the StarFive JH7100
+RISC-V SoC.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ .../reset/starfive,jh7100-audrst.yaml         | 38 +++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/reset/starfive,jh7100-audrst.yaml
+@@ -0,0 +1,38 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/reset/starfive,jh7100-audrst.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: StarFive JH7100 SoC Audio Reset Controller Device Tree Bindings
++
++maintainers:
++  - Emil Renner Berthing <kernel@esmil.dk>
++
++properties:
++  compatible:
++    enum:
++      - starfive,jh7100-audrst
++
++  reg:
++    maxItems: 1
++
++  "#reset-cells":
++    const: 1
++
++required:
++  - compatible
++  - reg
++  - "#reset-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    reset-controller@10490000 {
++        compatible = "starfive,jh7100-audrst";
++        reg = <0x10490000 0x10000>;
++        #reset-cells = <1>;
++    };
++
++...
diff --git a/target/linux/starfive/patches-6.6/1014-reset-starfive-Add-JH7100-audio-reset-driver.patch b/target/linux/starfive/patches-6.6/1014-reset-starfive-Add-JH7100-audio-reset-driver.patch
new file mode 100644 (file)
index 0000000..bc33188
--- /dev/null
@@ -0,0 +1,144 @@
+From 91559ced47de9e6fb1e6dd65a5544fcc86dea806 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 19:30:49 +0100
+Subject: [PATCH 1014/1024] reset: starfive: Add JH7100 audio reset driver
+
+The audio resets are almost identical to the system resets, there are
+just fewer of them. So factor out and export a generic probe function,
+so most of the reset controller implementation can be shared.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ MAINTAINERS                                   |  2 +-
+ drivers/reset/starfive/Kconfig                |  7 ++
+ drivers/reset/starfive/Makefile               |  2 +
+ .../starfive/reset-starfive-jh7100-audio.c    | 66 +++++++++++++++++++
+ .../reset/starfive/reset-starfive-jh7100.h    | 16 +++++
+ 5 files changed, 92 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/reset/starfive/reset-starfive-jh7100-audio.c
+ create mode 100644 drivers/reset/starfive/reset-starfive-jh7100.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -20554,7 +20554,7 @@ STARFIVE JH71X0 RESET CONTROLLER DRIVERS
+ M:    Emil Renner Berthing <kernel@esmil.dk>
+ M:    Hal Feng <hal.feng@starfivetech.com>
+ S:    Maintained
+-F:    Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml
++F:    Documentation/devicetree/bindings/reset/starfive,jh7100-*.yaml
+ F:    drivers/reset/starfive/reset-starfive-jh71*
+ F:    include/dt-bindings/reset/starfive?jh71*.h
+--- a/drivers/reset/starfive/Kconfig
++++ b/drivers/reset/starfive/Kconfig
+@@ -11,6 +11,13 @@ config RESET_STARFIVE_JH7100
+       help
+         This enables the reset controller driver for the StarFive JH7100 SoC.
++config RESET_STARFIVE_JH7100_AUDIO
++      tristate "StarFive JH7100 Audio Reset Driver"
++      depends on RESET_STARFIVE_JH7100
++      default m if SOC_STARFIVE
++      help
++        This enables the audio reset driver for the StarFive JH7100 SoC.
++
+ config RESET_STARFIVE_JH7110
+       bool "StarFive JH7110 Reset Driver"
+       depends on CLK_STARFIVE_JH7110_SYS
+--- a/drivers/reset/starfive/Makefile
++++ b/drivers/reset/starfive/Makefile
+@@ -2,4 +2,6 @@
+ obj-$(CONFIG_RESET_STARFIVE_JH71X0)           += reset-starfive-jh71x0.o
+ obj-$(CONFIG_RESET_STARFIVE_JH7100)           += reset-starfive-jh7100.o
++obj-$(CONFIG_RESET_STARFIVE_JH7100_AUDIO)     += reset-starfive-jh7100-audio.o
++
+ obj-$(CONFIG_RESET_STARFIVE_JH7110)           += reset-starfive-jh7110.o
+--- /dev/null
++++ b/drivers/reset/starfive/reset-starfive-jh7100-audio.c
+@@ -0,0 +1,66 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Audio reset driver for the StarFive JH7100 SoC
++ *
++ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
++ */
++
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include "reset-starfive-jh71x0.h"
++
++#include <dt-bindings/reset/starfive-jh7100-audio.h>
++
++/* register offsets */
++#define JH7100_AUDRST_ASSERT0 0x00
++#define JH7100_AUDRST_STATUS0 0x04
++
++/*
++ * Writing a 1 to the n'th bit of the ASSERT register asserts
++ * line n, and writing a 0 deasserts the same line.
++ * Most reset lines have their status inverted so a 0 bit in the STATUS
++ * register means the line is asserted and a 1 means it's deasserted. A few
++ * lines don't though, so store the expected value of the status registers when
++ * all lines are asserted.
++ */
++static const u32 jh7100_audrst_asserted[1] = {
++      BIT(JH7100_AUDRST_USB_AXI) |
++      BIT(JH7100_AUDRST_USB_PWRUP_RST_N) |
++      BIT(JH7100_AUDRST_USB_PONRST)
++};
++
++static int jh7100_audrst_probe(struct platform_device *pdev)
++{
++      void __iomem *base = devm_platform_ioremap_resource(pdev, 0);
++
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      return reset_starfive_jh71x0_register(&pdev->dev, pdev->dev.of_node,
++                                            base + JH7100_AUDRST_ASSERT0,
++                                            base + JH7100_AUDRST_STATUS0,
++                                            jh7100_audrst_asserted,
++                                            JH7100_AUDRSTN_END,
++                                            THIS_MODULE);
++}
++
++static const struct of_device_id jh7100_audrst_dt_ids[] = {
++      { .compatible = "starfive,jh7100-audrst" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, jh7100_audrst_dt_ids);
++
++static struct platform_driver jh7100_audrst_driver = {
++      .probe = jh7100_audrst_probe,
++      .driver = {
++              .name = "jh7100-reset-audio",
++              .of_match_table = jh7100_audrst_dt_ids,
++      },
++};
++module_platform_driver(jh7100_audrst_driver);
++
++MODULE_AUTHOR("Emil Renner Berthing");
++MODULE_DESCRIPTION("StarFive JH7100 audio reset driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/reset/starfive/reset-starfive-jh7100.h
+@@ -0,0 +1,16 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
++ */
++
++#ifndef _RESET_STARFIVE_JH7100_H_
++#define _RESET_STARFIVE_JH7100_H_
++
++#include <linux/platform_device.h>
++
++int reset_starfive_jh7100_generic_probe(struct platform_device *pdev,
++                                      const u32 *asserted,
++                                      unsigned int status_offset,
++                                      unsigned int nr_resets);
++
++#endif
diff --git a/target/linux/starfive/patches-6.6/1015-RISC-V-Add-StarFive-JH7100-audio-reset-node.patch b/target/linux/starfive/patches-6.6/1015-RISC-V-Add-StarFive-JH7100-audio-reset-node.patch
new file mode 100644 (file)
index 0000000..521144c
--- /dev/null
@@ -0,0 +1,28 @@
+From 418603fdce51212d4547aacfe2b4801fc5e61978 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sat, 20 Nov 2021 21:33:08 +0100
+Subject: [PATCH 1015/1024] RISC-V: Add StarFive JH7100 audio reset node
+
+Add device tree node for the audio resets on the StarFive JH7100 RISC-V
+SoC.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -168,6 +168,12 @@
+                       #clock-cells = <1>;
+               };
++              audrst: reset-controller@10490000 {
++                      compatible = "starfive,jh7100-audrst";
++                      reg = <0x0 0x10490000 0x0 0x10000>;
++                      #reset-cells = <1>;
++              };
++
+               clkgen: clock-controller@11800000 {
+                       compatible = "starfive,jh7100-clkgen";
+                       reg = <0x0 0x11800000 0x0 0x10000>;
diff --git a/target/linux/starfive/patches-6.6/1016-soc-sifive-ccache-Add-StarFive-JH7100-support.patch b/target/linux/starfive/patches-6.6/1016-soc-sifive-ccache-Add-StarFive-JH7100-support.patch
new file mode 100644 (file)
index 0000000..2906e16
--- /dev/null
@@ -0,0 +1,160 @@
+From 8e090d271683d5869cdab0729f54a8af8c79c476 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Tue, 31 Oct 2023 15:14:44 +0100
+Subject: [PATCH 1016/1024] soc: sifive: ccache: Add StarFive JH7100 support
+
+This adds support for the StarFive JH7100 SoC which also features this
+SiFive cache controller.
+
+The JH7100 has non-coherent DMAs but predate the standard RISC-V Zicbom
+exension, so instead we need to use this cache controller for
+non-standard cache management operations.
+
+Unfortunately the interrupt for uncorrected data is broken on the JH7100
+and fires continuously, so add a quirk to not register a handler for it.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ drivers/soc/sifive/sifive_ccache.c | 62 +++++++++++++++++++++++++++++-
+ 1 file changed, 60 insertions(+), 2 deletions(-)
+
+--- a/drivers/soc/sifive/sifive_ccache.c
++++ b/drivers/soc/sifive/sifive_ccache.c
+@@ -8,13 +8,16 @@
+ #define pr_fmt(fmt) "CCACHE: " fmt
++#include <linux/align.h>
+ #include <linux/debugfs.h>
+ #include <linux/interrupt.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_address.h>
+ #include <linux/device.h>
+ #include <linux/bitfield.h>
++#include <asm/cacheflush.h>
+ #include <asm/cacheinfo.h>
++#include <asm/dma-noncoherent.h>
+ #include <soc/sifive/sifive_ccache.h>
+ #define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100
+@@ -39,10 +42,14 @@
+ #define SIFIVE_CCACHE_CONFIG_SETS_MASK GENMASK_ULL(23, 16)
+ #define SIFIVE_CCACHE_CONFIG_BLKS_MASK GENMASK_ULL(31, 24)
++#define SIFIVE_CCACHE_FLUSH64 0x200
++#define SIFIVE_CCACHE_FLUSH32 0x240
++
+ #define SIFIVE_CCACHE_WAYENABLE 0x08
+ #define SIFIVE_CCACHE_ECCINJECTERR 0x40
+ #define SIFIVE_CCACHE_MAX_ECCINTR 4
++#define SIFIVE_CCACHE_LINE_SIZE 64
+ static void __iomem *ccache_base;
+ static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR];
+@@ -56,6 +63,11 @@ enum {
+       DIR_UNCORR,
+ };
++enum {
++      QUIRK_NONSTANDARD_CACHE_OPS     = BIT(0),
++      QUIRK_BROKEN_DATA_UNCORR        = BIT(1),
++};
++
+ #ifdef CONFIG_DEBUG_FS
+ static struct dentry *sifive_test;
+@@ -106,6 +118,8 @@ static void ccache_config_read(void)
+ static const struct of_device_id sifive_ccache_ids[] = {
+       { .compatible = "sifive,fu540-c000-ccache" },
+       { .compatible = "sifive,fu740-c000-ccache" },
++      { .compatible = "starfive,jh7100-ccache",
++        .data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS | QUIRK_BROKEN_DATA_UNCORR) },
+       { .compatible = "sifive,ccache0" },
+       { /* end of table */ }
+ };
+@@ -124,6 +138,34 @@ int unregister_sifive_ccache_error_notif
+ }
+ EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier);
++#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
++static void ccache_flush_range(phys_addr_t start, size_t len)
++{
++      phys_addr_t end = start + len;
++      phys_addr_t line;
++
++      if (!len)
++              return;
++
++      mb();
++      for (line = ALIGN_DOWN(start, SIFIVE_CCACHE_LINE_SIZE); line < end;
++                      line += SIFIVE_CCACHE_LINE_SIZE) {
++#ifdef CONFIG_32BIT
++              writel(line >> 4, ccache_base + SIFIVE_CCACHE_FLUSH32);
++#else
++              writeq(line, ccache_base + SIFIVE_CCACHE_FLUSH64);
++#endif
++              mb();
++      }
++}
++
++static const struct riscv_nonstd_cache_ops ccache_mgmt_ops __initconst = {
++      .wback = &ccache_flush_range,
++      .inv = &ccache_flush_range,
++      .wback_inv = &ccache_flush_range,
++};
++#endif /* CONFIG_RISCV_NONSTANDARD_CACHE_OPS */
++
+ static int ccache_largest_wayenabled(void)
+ {
+       return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF;
+@@ -210,11 +252,15 @@ static int __init sifive_ccache_init(voi
+       struct device_node *np;
+       struct resource res;
+       int i, rc, intr_num;
++      const struct of_device_id *match;
++      unsigned long quirks;
+-      np = of_find_matching_node(NULL, sifive_ccache_ids);
++      np = of_find_matching_node_and_match(NULL, sifive_ccache_ids, &match);
+       if (!np)
+               return -ENODEV;
++      quirks = (uintptr_t)match->data;
++
+       if (of_address_to_resource(np, 0, &res)) {
+               rc = -ENODEV;
+               goto err_node_put;
+@@ -240,6 +286,10 @@ static int __init sifive_ccache_init(voi
+       for (i = 0; i < intr_num; i++) {
+               g_irq[i] = irq_of_parse_and_map(np, i);
++
++              if (i == DATA_UNCORR && (quirks & QUIRK_BROKEN_DATA_UNCORR))
++                      continue;
++
+               rc = request_irq(g_irq[i], ccache_int_handler, 0, "ccache_ecc",
+                                NULL);
+               if (rc) {
+@@ -249,6 +299,14 @@ static int __init sifive_ccache_init(voi
+       }
+       of_node_put(np);
++#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
++      if (quirks & QUIRK_NONSTANDARD_CACHE_OPS) {
++              riscv_cbom_block_size = SIFIVE_CCACHE_LINE_SIZE;
++              riscv_noncoherent_supported();
++              riscv_noncoherent_register_cache_ops(&ccache_mgmt_ops);
++      }
++#endif
++
+       ccache_config_read();
+       ccache_cache_ops.get_priv_group = ccache_get_priv_group;
+@@ -269,4 +327,4 @@ err_node_put:
+       return rc;
+ }
+-device_initcall(sifive_ccache_init);
++arch_initcall(sifive_ccache_init);
diff --git a/target/linux/starfive/patches-6.6/1017-riscv-errata-Add-StarFive-JH7100-errata.patch b/target/linux/starfive/patches-6.6/1017-riscv-errata-Add-StarFive-JH7100-errata.patch
new file mode 100644 (file)
index 0000000..91b3adf
--- /dev/null
@@ -0,0 +1,44 @@
+From 30fb5963f4cf3b7d114a8212358147615480685c Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:25 +0100
+Subject: [PATCH 1017/1024] riscv: errata: Add StarFive JH7100 errata
+
+This not really an errata, but since the JH7100 was made before
+the standard Zicbom extension it needs the DMA_GLOBAL_POOL and
+RISCV_NONSTANDARD_CACHE_OPS enabled to work correctly.
+
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
+Acked-by: Palmer Dabbelt <palmer@rivosinc.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/Kconfig.errata | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/arch/riscv/Kconfig.errata
++++ b/arch/riscv/Kconfig.errata
+@@ -53,6 +53,23 @@ config ERRATA_SIFIVE_CIP_1200
+         If you don't know what to do here, say "Y".
++config ERRATA_STARFIVE_JH7100
++      bool "StarFive JH7100 support"
++      depends on ARCH_STARFIVE && NONPORTABLE
++      select DMA_GLOBAL_POOL
++      select RISCV_DMA_NONCOHERENT
++      select RISCV_NONSTANDARD_CACHE_OPS
++      select SIFIVE_CCACHE
++      default n
++      help
++        The StarFive JH7100 was a test chip for the JH7110 and has
++        caches that are non-coherent with respect to peripheral DMAs.
++        It was designed before the Zicbom extension so needs non-standard
++        cache operations through the SiFive cache controller.
++
++        Say "Y" if you want to support the BeagleV Starlight and/or
++        StarFive VisionFive V1 boards.
++
+ config ERRATA_THEAD
+       bool "T-HEAD errata"
+       depends on RISCV_ALTERNATIVE
diff --git a/target/linux/starfive/patches-6.6/1018-riscv-dts-starfive-Mark-the-JH7100-as-having-non-coh.patch b/target/linux/starfive/patches-6.6/1018-riscv-dts-starfive-Mark-the-JH7100-as-having-non-coh.patch
new file mode 100644 (file)
index 0000000..0b223c5
--- /dev/null
@@ -0,0 +1,26 @@
+From 29e4bc0fafd9add93acc967f3992948b3afe7176 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Thu, 30 Nov 2023 16:19:27 +0100
+Subject: [PATCH 1018/1024] riscv: dts: starfive: Mark the JH7100 as having
+ non-coherent DMAs
+
+The StarFive JH7100 SoC has non-coherent device DMAs, so mark the
+soc bus as such.
+
+Link: https://github.com/starfive-tech/JH7100_Docs/blob/main/JH7100%20Cache%20Coherence%20V1.0.pdf
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -138,6 +138,7 @@
+               interrupt-parent = <&plic>;
+               #address-cells = <2>;
+               #size-cells = <2>;
++              dma-noncoherent;
+               ranges;
+               clint: clint@2000000 {
diff --git a/target/linux/starfive/patches-6.6/1019-riscv-dts-starfive-Add-JH7100-cache-controller.patch b/target/linux/starfive/patches-6.6/1019-riscv-dts-starfive-Add-JH7100-cache-controller.patch
new file mode 100644 (file)
index 0000000..cdc63fa
--- /dev/null
@@ -0,0 +1,50 @@
+From e1918356dcc285eb7c50f271795e6fcc18d6c092 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:28 +0100
+Subject: [PATCH 1019/1024] riscv: dts: starfive: Add JH7100 cache controller
+
+The StarFive JH7100 SoC also features the SiFive L2 cache controller,
+so add the device tree nodes for it.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -32,6 +32,7 @@
+                       i-tlb-sets = <1>;
+                       i-tlb-size = <32>;
+                       mmu-type = "riscv,sv39";
++                      next-level-cache = <&ccache>;
+                       riscv,isa = "rv64imafdc";
+                       tlb-split;
+@@ -57,6 +58,7 @@
+                       i-tlb-sets = <1>;
+                       i-tlb-size = <32>;
+                       mmu-type = "riscv,sv39";
++                      next-level-cache = <&ccache>;
+                       riscv,isa = "rv64imafdc";
+                       tlb-split;
+@@ -148,6 +150,17 @@
+                                              &cpu1_intc 3 &cpu1_intc 7>;
+               };
++              ccache: cache-controller@2010000 {
++                      compatible = "starfive,jh7100-ccache", "sifive,ccache0", "cache";
++                      reg = <0x0 0x2010000 0x0 0x1000>;
++                      interrupts = <128>, <130>, <131>, <129>;
++                      cache-block-size = <64>;
++                      cache-level = <2>;
++                      cache-sets = <2048>;
++                      cache-size = <2097152>;
++                      cache-unified;
++              };
++
+               plic: interrupt-controller@c000000 {
+                       compatible = "starfive,jh7100-plic", "sifive,plic-1.0.0";
+                       reg = <0x0 0xc000000 0x0 0x4000000>;
diff --git a/target/linux/starfive/patches-6.6/1020-riscv-dts-starfive-Add-pool-for-coherent-DMA-memory-.patch b/target/linux/starfive/patches-6.6/1020-riscv-dts-starfive-Add-pool-for-coherent-DMA-memory-.patch
new file mode 100644 (file)
index 0000000..c9ba388
--- /dev/null
@@ -0,0 +1,61 @@
+From 3cbd661b811bda9a33253f65b5cf0c25b8c5447f Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:29 +0100
+Subject: [PATCH 1020/1024] riscv: dts: starfive: Add pool for coherent DMA
+ memory on JH7100 boards
+
+The StarFive JH7100 SoC has non-coherent device DMAs, but most drivers
+expect to be able to allocate coherent memory for DMA descriptors and
+such. However on the JH7100 DDR memory appears twice in the physical
+memory map, once cached and once uncached:
+
+  0x00_8000_0000 - 0x08_7fff_ffff : Off chip DDR memory, cached
+  0x10_0000_0000 - 0x17_ffff_ffff : Off chip DDR memory, uncached
+
+To use this uncached region we create a global DMA memory pool there and
+reserve the corresponding area in the cached region.
+
+However the uncached region is fully above the 32bit address limit, so add
+a dma-ranges map so the DMA address used for peripherals is still in the
+regular cached region below the limit.
+
+Link: https://github.com/starfive-tech/JH7100_Docs/blob/main/JH7100%20Data%20Sheet%20V01.01.04-EN%20(4-21-2021).pdf
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../boot/dts/starfive/jh7100-common.dtsi      | 24 +++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+@@ -39,6 +39,30 @@
+                       label = "ack";
+               };
+       };
++
++      reserved-memory {
++              #address-cells = <2>;
++              #size-cells = <2>;
++              ranges;
++
++              dma-reserved@fa000000 {
++                      reg = <0x0 0xfa000000 0x0 0x1000000>;
++                      no-map;
++              };
++
++              linux,dma@107a000000 {
++                      compatible = "shared-dma-pool";
++                      reg = <0x10 0x7a000000 0x0 0x1000000>;
++                      no-map;
++                      linux,dma-default;
++              };
++      };
++
++      soc {
++              dma-ranges = <0x00 0x80000000 0x00 0x80000000 0x00 0x7a000000>,
++                           <0x00 0xfa000000 0x10 0x7a000000 0x00 0x01000000>,
++                           <0x00 0xfb000000 0x00 0xfb000000 0x07 0x85000000>;
++      };
+ };
+ &gpio {
diff --git a/target/linux/starfive/patches-6.6/1021-riscv-dts-starfive-Add-JH7100-MMC-nodes.patch b/target/linux/starfive/patches-6.6/1021-riscv-dts-starfive-Add-JH7100-MMC-nodes.patch
new file mode 100644 (file)
index 0000000..fb3ba52
--- /dev/null
@@ -0,0 +1,49 @@
+From 7be159c760aa8a1ece1354892af215b2f8c21152 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:30 +0100
+Subject: [PATCH 1021/1024] riscv: dts: starfive: Add JH7100 MMC nodes
+
+Add device tree nodes for the Synopsis MMC controllers on the
+StarFive JH7100 SoC.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/boot/dts/starfive/jh7100.dtsi | 26 ++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -188,6 +188,32 @@
+                       #reset-cells = <1>;
+               };
++              sdio0: mmc@10000000 {
++                      compatible = "snps,dw-mshc";
++                      reg = <0x0 0x10000000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SDIO0_AHB>,
++                               <&clkgen JH7100_CLK_SDIO0_CCLKINT_INV>;
++                      clock-names = "biu", "ciu";
++                      interrupts = <4>;
++                      data-addr = <0>;
++                      fifo-depth = <32>;
++                      fifo-watermark-aligned;
++                      status = "disabled";
++              };
++
++              sdio1: mmc@10010000 {
++                      compatible = "snps,dw-mshc";
++                      reg = <0x0 0x10010000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SDIO1_AHB>,
++                               <&clkgen JH7100_CLK_SDIO1_CCLKINT_INV>;
++                      clock-names = "biu", "ciu";
++                      interrupts = <5>;
++                      data-addr = <0>;
++                      fifo-depth = <32>;
++                      fifo-watermark-aligned;
++                      status = "disabled";
++              };
++
+               clkgen: clock-controller@11800000 {
+                       compatible = "starfive,jh7100-clkgen";
+                       reg = <0x0 0x11800000 0x0 0x10000>;
diff --git a/target/linux/starfive/patches-6.6/1022-riscv-dts-starfive-Enable-SD-card-on-JH7100-boards.patch b/target/linux/starfive/patches-6.6/1022-riscv-dts-starfive-Enable-SD-card-on-JH7100-boards.patch
new file mode 100644 (file)
index 0000000..bf380eb
--- /dev/null
@@ -0,0 +1,85 @@
+From 015edaccef82200d913d5f1e99fd95641f526bc6 Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Thu, 30 Nov 2023 16:19:31 +0100
+Subject: [PATCH 1022/1024] riscv: dts: starfive: Enable SD-card on JH7100
+ boards
+
+Add pinctrl and MMC device tree nodes for the SD-card on the
+BeagleV Starlight and StarFive VisionFive V1 boards.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ .../boot/dts/starfive/jh7100-common.dtsi      | 47 +++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+@@ -12,6 +12,7 @@
+ / {
+       aliases {
++              mmc0 = &sdio0;
+               serial0 = &uart3;
+       };
+@@ -108,6 +109,43 @@
+               };
+       };
++      sdio0_pins: sdio0-0 {
++              clk-pins {
++                      pinmux = <GPIOMUX(54, GPO_SDIO0_PAD_CCLK_OUT,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++              sdio-pins {
++                      pinmux = <GPIOMUX(55, GPO_LOW, GPO_DISABLE,
++                                GPI_SDIO0_PAD_CARD_DETECT_N)>,
++                               <GPIOMUX(53,
++                                GPO_SDIO0_PAD_CCMD_OUT,
++                                GPO_SDIO0_PAD_CCMD_OEN,
++                                GPI_SDIO0_PAD_CCMD_IN)>,
++                               <GPIOMUX(49,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT0,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT0,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT0)>,
++                               <GPIOMUX(50,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT1,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT1,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT1)>,
++                               <GPIOMUX(51,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT2,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT2,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT2)>,
++                               <GPIOMUX(52,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT3,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT3,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT3)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
+       uart3_pins: uart3-0 {
+               rx-pins {
+                       pinmux = <GPIOMUX(13, GPO_LOW, GPO_DISABLE,
+@@ -178,6 +216,15 @@
+       clock-frequency = <27000000>;
+ };
++&sdio0 {
++      broken-cd;
++      bus-width = <4>;
++      cap-sd-highspeed;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdio0_pins>;
++      status = "okay";
++};
++
+ &uart3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart3_pins>;
diff --git a/target/linux/starfive/patches-6.6/1023-riscv-errata-Make-ERRATA_STARFIVE_JH7100-depend-on-D.patch b/target/linux/starfive/patches-6.6/1023-riscv-errata-Make-ERRATA_STARFIVE_JH7100-depend-on-D.patch
new file mode 100644 (file)
index 0000000..2b782e1
--- /dev/null
@@ -0,0 +1,33 @@
+From 1e70a0772165dd552f82434c9072dabfaaaf4c2a Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Date: Fri, 15 Dec 2023 20:09:09 +0100
+Subject: [PATCH 1023/1024] riscv: errata: Make ERRATA_STARFIVE_JH7100 depend
+ on !DMA_DIRECT_REMAP
+
+Similar to the Renesas RZ/Five[1] the JH7100 SoC needs the non-portable
+CONFIG_DMA_GLOBAL_POOL enabled which is incompatible with DMA_DIRECT_REMAP
+selected by RISCV_ISA_ZICBOM.
+
+[1]: commit 31b2daea0764 ("soc: renesas: Make RZ/Five depend on !DMA_DIRECT_REMAP")
+
+Link: https://lore.kernel.org/all/24942b4d-d16a-463f-b39a-f9dfcb89d742@infradead.org/
+Fixes: 64fc984a8a54 ("riscv: errata: Add StarFive JH7100 errata")
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
+---
+ arch/riscv/Kconfig.errata | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/arch/riscv/Kconfig.errata
++++ b/arch/riscv/Kconfig.errata
+@@ -55,7 +55,9 @@ config ERRATA_SIFIVE_CIP_1200
+ config ERRATA_STARFIVE_JH7100
+       bool "StarFive JH7100 support"
+-      depends on ARCH_STARFIVE && NONPORTABLE
++      depends on ARCH_STARFIVE
++      depends on !DMA_DIRECT_REMAP
++      depends on NONPORTABLE
+       select DMA_GLOBAL_POOL
+       select RISCV_DMA_NONCOHERENT
+       select RISCV_NONSTANDARD_CACHE_OPS
diff --git a/target/linux/starfive/patches-6.6/1024-riscv-dts-Add-full-JH7100-Starlight-and-VisionFive-s.patch b/target/linux/starfive/patches-6.6/1024-riscv-dts-Add-full-JH7100-Starlight-and-VisionFive-s.patch
new file mode 100644 (file)
index 0000000..c697d57
--- /dev/null
@@ -0,0 +1,1130 @@
+From 782f99cc437d975c9ef5a1f351bb8fb83d50039b Mon Sep 17 00:00:00 2001
+From: Emil Renner Berthing <kernel@esmil.dk>
+Date: Sun, 1 Sep 2024 12:43:01 +0000
+Subject: [PATCH 1024/1024] riscv: dts: Add full JH7100, Starlight and
+ VisionFive support
+
+Based on the device tree in https://github.com/starfive-tech/u-boot/
+with contributions from:
+yanhong.wang <yanhong.wang@starfivetech.com>
+Huan.Feng <huan.feng@starfivetech.com>
+ke.zhu <ke.zhu@starfivetech.com>
+yiming.li <yiming.li@starfivetech.com>
+jack.zhu <jack.zhu@starfivetech.com>
+Samin Guo <samin.guo@starfivetech.com>
+Chenjieqin <Jessica.Chen@starfivetech.com>
+bo.li <bo.li@starfivetech.com>
+
+Rearranged, cleanups, fixes, pins and resets added by Emil.
+Cleanups, fixes, clocks added by Geert.
+Cleanups and GPIO fixes from Drew.
+Thermal zone added by Stephen.
+PWM pins added by Jianlong.
+cpu-map added by Jonas.
+
+Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
+Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
+Signed-off-by: Stephen L Arnold <nerdboy@gentoo.org>
+Signed-off-by: Drew Fustini <drew@beagleboard.org>
+Signed-off-by: Jianlong Huang <jianlong.huang@starfivetech.com>
+Signed-off-by: Jonas Hahnfeld <hahnjo@hahnjo.de>
+---
+ .../dts/starfive/jh7100-beaglev-starlight.dts |  16 +
+ .../boot/dts/starfive/jh7100-common.dtsi      | 432 +++++++++++++++
+ .../jh7100-starfive-visionfive-v1.dts         |  19 +
+ arch/riscv/boot/dts/starfive/jh7100.dtsi      | 503 ++++++++++++++++++
+ 4 files changed, 970 insertions(+)
+
+--- a/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts
++++ b/arch/riscv/boot/dts/starfive/jh7100-beaglev-starlight.dts
+@@ -6,8 +6,24 @@
+ /dts-v1/;
+ #include "jh7100-common.dtsi"
++#include <dt-bindings/gpio/gpio.h>
+ / {
+       model = "BeagleV Starlight Beta";
+       compatible = "beagle,beaglev-starlight-jh7100-r0", "starfive,jh7100";
+ };
++
++&gmac {
++      snps,reset-gpios = <&gpio 63 GPIO_ACTIVE_LOW>;
++};
++
++&gpio {
++      /* don't reset gpio mux for serial console on uart3 */
++      starfive,keep-gpiomux = <13 14>;
++};
++
++&mdio {
++      phy: ethernet-phy@7 {
++              reg = <7>;
++      };
++};
+--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+@@ -64,9 +64,174 @@
+                            <0x00 0xfa000000 0x10 0x7a000000 0x00 0x01000000>,
+                            <0x00 0xfb000000 0x00 0xfb000000 0x07 0x85000000>;
+       };
++
++      reserved-memory {
++              #address-cells = <2>;
++              #size-cells = <2>;
++              ranges;
++
++              linux,cma {
++                      compatible = "shared-dma-pool";
++                      reusable;
++                      size = <0x0 0x28000000>;
++                      alignment = <0x0 0x1000>;
++                      alloc-ranges = <0x0 0xa0000000 0x0 0x28000000>;
++                      linux,cma-default;
++              };
++
++              jpu_reserved: framebuffer@c9000000 {
++                      reg = <0x0 0xc9000000 0x0 0x4000000>;
++              };
++
++              nvdla_reserved: framebuffer@d0000000 {
++                      no-map;
++                      reg = <0x0 0xd0000000 0x0 0x28000000>;
++              };
++
++              vin_reserved: framebuffer@f9000000 {
++                      compatible = "shared-dma-pool";
++                      no-map;
++                      reg = <0x0 0xf9000000 0x0 0x1000000>;
++              };
++
++              sffb_reserved: framebuffer@fb000000 {
++                      compatible = "shared-dma-pool";
++                      no-map;
++                      reg = <0x0 0xfb000000 0x0 0x2000000>;
++              };
++      };
++
++      wifi_pwrseq: wifi-pwrseq {
++              compatible = "mmc-pwrseq-simple";
++              reset-gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
++      };
++};
++
++&display {
++      memory-region = <&sffb_reserved>;
++      status = "okay";
++};
++
++&crtc {
++      ddr-format = <4>; //<WIN_FMT_RGB565>;
++      status = "okay";
++
++      port: port@0 {
++              reg = <0>;
++
++              crtc_0_out: endpoint {
++                      remote-endpoint = <&hdmi_input0>;
++              };
++      };
++};
++
++&encoder {
++      encoder-type = <2>; // 2-TMDS, 3-LVDS, 6-DSI, 8-DPI
++      status = "okay";
++
++      ports {
++              port@0 {
++                      hdmi_out: endpoint {
++                              remote-endpoint = <&tda998x_0_input>;
++                      };
++              };
++
++              port@1 {
++                      hdmi_input0: endpoint {
++                              remote-endpoint = <&crtc_0_out>;
++                      };
++              };
++
++      };
++};
++
++&gmac {
++      starfive,gtxclk-dlychain = <4>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&gmac_pins>;
++      phy-mode = "rgmii-txid";
++      phy-handle = <&phy>;
++      status = "okay";
++
++      mdio: mdio {
++              #address-cells = <1>;
++              #size-cells = <0>;
++              compatible = "snps,dwmac-mdio";
++      };
+ };
+ &gpio {
++      gmac_pins: gmac-0 {
++              gtxclk-pins {
++                      pins = <PAD_FUNC_SHARE(115)>;
++                      bias-pull-up;
++                      drive-strength = <35>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++              miitxclk-pins {
++                      pins = <PAD_FUNC_SHARE(116)>;
++                      bias-pull-up;
++                      drive-strength = <14>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++              tx-pins {
++                      pins = <PAD_FUNC_SHARE(117)>,
++                             <PAD_FUNC_SHARE(119)>,
++                             <PAD_FUNC_SHARE(120)>,
++                             <PAD_FUNC_SHARE(121)>,
++                             <PAD_FUNC_SHARE(122)>,
++                             <PAD_FUNC_SHARE(123)>,
++                             <PAD_FUNC_SHARE(124)>,
++                             <PAD_FUNC_SHARE(125)>,
++                             <PAD_FUNC_SHARE(126)>;
++                      bias-pull-up;
++                      drive-strength = <35>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++              rxclk-pins {
++                      pins = <PAD_FUNC_SHARE(127)>;
++                      bias-pull-up;
++                      drive-strength = <14>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <6>;
++              };
++              rxer-pins {
++                      pins = <PAD_FUNC_SHARE(129)>;
++                      bias-pull-up;
++                      drive-strength = <14>;
++                      input-enable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++              rx-pins {
++                      pins = <PAD_FUNC_SHARE(128)>,
++                             <PAD_FUNC_SHARE(130)>,
++                             <PAD_FUNC_SHARE(131)>,
++                             <PAD_FUNC_SHARE(132)>,
++                             <PAD_FUNC_SHARE(133)>,
++                             <PAD_FUNC_SHARE(134)>,
++                             <PAD_FUNC_SHARE(135)>,
++                             <PAD_FUNC_SHARE(136)>,
++                             <PAD_FUNC_SHARE(137)>,
++                             <PAD_FUNC_SHARE(138)>,
++                             <PAD_FUNC_SHARE(139)>,
++                             <PAD_FUNC_SHARE(140)>,
++                             <PAD_FUNC_SHARE(141)>;
++                      bias-pull-up;
++                      drive-strength = <14>;
++                      input-enable;
++                      input-schmitt-enable;
++                      slew-rate = <0>;
++              };
++      };
++
+       i2c0_pins: i2c0-0 {
+               i2c-pins {
+                       pinmux = <GPIOMUX(62, GPO_LOW,
+@@ -146,6 +311,166 @@
+               };
+       };
++      pwmdac_pins: pwmdac-0 {
++              pwmdac-pins {
++                      pinmux = <GPIOMUX(23, GPO_PWMDAC_LEFT_OUT,
++                                GPO_ENABLE, GPI_NONE)>,
++                               <GPIOMUX(24, GPO_PWMDAC_RIGHT_OUT,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <35>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++      };
++
++      pwm_pins: pwm-0 {
++              pwm-pins {
++                      pinmux = <GPIOMUX(7,
++                                GPO_PWM_PAD_OUT_BIT0,
++                                GPO_PWM_PAD_OE_N_BIT0,
++                                GPI_NONE)>,
++                               <GPIOMUX(5,
++                                GPO_PWM_PAD_OUT_BIT1,
++                                GPO_PWM_PAD_OE_N_BIT1,
++                                GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <35>;
++                      input-disable;
++                      input-schmitt-disable;
++                      slew-rate = <0>;
++              };
++      };
++
++      sdio0_pins: sdio0-0 {
++              clk-pins {
++                      pinmux = <GPIOMUX(54, GPO_SDIO0_PAD_CCLK_OUT,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++              sdio-pins {
++                      pinmux = <GPIOMUX(55, GPO_LOW, GPO_DISABLE,
++                                GPI_SDIO0_PAD_CARD_DETECT_N)>,
++                               <GPIOMUX(53,
++                                GPO_SDIO0_PAD_CCMD_OUT,
++                                GPO_SDIO0_PAD_CCMD_OEN,
++                                GPI_SDIO0_PAD_CCMD_IN)>,
++                               <GPIOMUX(49,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT0,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT0,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT0)>,
++                               <GPIOMUX(50,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT1,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT1,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT1)>,
++                               <GPIOMUX(51,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT2,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT2,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT2)>,
++                               <GPIOMUX(52,
++                                GPO_SDIO0_PAD_CDATA_OUT_BIT3,
++                                GPO_SDIO0_PAD_CDATA_OEN_BIT3,
++                                GPI_SDIO0_PAD_CDATA_IN_BIT3)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      sdio1_pins: sdio1-0 {
++              clk-pins {
++                      pinmux = <GPIOMUX(33, GPO_SDIO1_PAD_CCLK_OUT,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++              sdio-pins {
++                      pinmux = <GPIOMUX(29,
++                                GPO_SDIO1_PAD_CCMD_OUT,
++                                GPO_SDIO1_PAD_CCMD_OEN,
++                                GPI_SDIO1_PAD_CCMD_IN)>,
++                               <GPIOMUX(36,
++                                GPO_SDIO1_PAD_CDATA_OUT_BIT0,
++                                GPO_SDIO1_PAD_CDATA_OEN_BIT0,
++                                GPI_SDIO1_PAD_CDATA_IN_BIT0)>,
++                               <GPIOMUX(30,
++                                GPO_SDIO1_PAD_CDATA_OUT_BIT1,
++                                GPO_SDIO1_PAD_CDATA_OEN_BIT1,
++                                GPI_SDIO1_PAD_CDATA_IN_BIT1)>,
++                               <GPIOMUX(34,
++                                GPO_SDIO1_PAD_CDATA_OUT_BIT2,
++                                GPO_SDIO1_PAD_CDATA_OEN_BIT2,
++                                GPI_SDIO1_PAD_CDATA_IN_BIT2)>,
++                               <GPIOMUX(31,
++                                GPO_SDIO1_PAD_CDATA_OUT_BIT3,
++                                GPO_SDIO1_PAD_CDATA_OEN_BIT3,
++                                GPI_SDIO1_PAD_CDATA_IN_BIT3)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++      };
++
++      spi2_pins: spi2-0 {
++              mosi-pins {
++                      pinmux = <GPIOMUX(18, GPO_SPI2_PAD_TXD,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++              miso-pins {
++                      pinmux = <GPIOMUX(16, GPO_LOW, GPO_DISABLE,
++                                GPI_SPI2_PAD_RXD)>;
++                      bias-pull-up;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++              sck-pins {
++                      pinmux = <GPIOMUX(12, GPO_SPI2_PAD_SCK_OUT,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++              ss-pins {
++                      pinmux = <GPIOMUX(15, GPO_SPI2_PAD_SS_0_N,
++                                GPO_ENABLE, GPI_NONE)>,
++                               <GPIOMUX(11, GPO_SPI2_PAD_SS_1_N,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
++      uart0_pins: uart0-0 {
++              rx-pins {
++                      pinmux = <GPIOMUX(40, GPO_LOW, GPO_DISABLE,
++                                GPI_UART0_PAD_SIN)>,
++                               <GPIOMUX(39, GPO_LOW, GPO_DISABLE,
++                                GPI_UART0_PAD_CTSN)>;
++                      bias-pull-up;
++                      drive-strength = <14>;
++                      input-enable;
++                      input-schmitt-enable;
++              };
++              tx-pins {
++                      pinmux = <GPIOMUX(41, GPO_UART0_PAD_SOUT,
++                                GPO_ENABLE, GPI_NONE)>,
++                               <GPIOMUX(42, GPO_UART0_PAD_RTSN,
++                                GPO_ENABLE, GPI_NONE)>;
++                      bias-disable;
++                      drive-strength = <35>;
++                      input-disable;
++                      input-schmitt-disable;
++              };
++      };
++
+       uart3_pins: uart3-0 {
+               rx-pins {
+                       pinmux = <GPIOMUX(13, GPO_LOW, GPO_DISABLE,
+@@ -186,6 +511,17 @@
+               regulators {
+               };
+       };
++
++      tda998x@70 {
++              compatible = "nxp,tda998x";
++              reg = <0x70>;
++
++              port {
++                      tda998x_0_input: endpoint {
++                              remote-endpoint = <&hdmi_out>;
++                      };
++              };
++      };
+ };
+ &i2c1 {
+@@ -225,8 +561,104 @@
+       status = "okay";
+ };
++&ptc {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwm_pins>;
++      status = "okay";
++};
++
++&pwmdac {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwmdac_pins>;
++      status = "okay";
++};
++
++&qspi {
++      nor_flash: nor-flash@0 {
++              compatible = "spi-flash";
++              reg = <0>;
++              spi-max-frequency = <31250000>;
++              page-size = <256>;
++              block-size = <16>;
++              cdns,read-delay = <4>;
++              cdns,tshsl-ns = <1>;
++              cdns,tsd2d-ns = <1>;
++              cdns,tchsh-ns = <1>;
++              cdns,tslch-ns = <1>;
++              spi-tx-bus-width = <1>;
++              spi-rx-bus-width = <1>;
++      };
++
++      nand_flash: nand-flash@1 {
++              compatible = "spi-flash-nand";
++              reg = <1>;
++              spi-max-frequency = <31250000>;
++              page-size = <2048>;
++              block-size = <17>;
++              cdns,read-delay = <4>;
++              cdns,tshsl-ns = <1>;
++              cdns,tsd2d-ns = <1>;
++              cdns,tchsh-ns = <1>;
++              cdns,tslch-ns = <1>;
++              spi-tx-bus-width = <1>;
++              spi-rx-bus-width = <1>;
++      };
++};
++
++&sdio0 {
++      broken-cd;
++      bus-width = <4>;
++      cap-sd-highspeed;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdio0_pins>;
++      max-frequency = <10000000>;
++      status = "okay";
++};
++
++&sdio1 {
++      #address-cells = <1>;
++      #size-cells = <0>;
++      bus-width = <4>;
++      cap-sd-highspeed;
++      cap-sdio-irq;
++      cap-power-off-card;
++      mmc-pwrseq = <&wifi_pwrseq>;
++      non-removable;
++      pinctrl-names = "default";
++      pinctrl-0 = <&sdio1_pins>;
++      status = "okay";
++
++      wifi@1 {
++              compatible = "brcm,bcm4329-fmac";
++              reg = <1>;
++      };
++};
++
++&spi2 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi2_pins>;
++      status = "okay";
++
++      spi_dev0: spi@0 {
++              compatible = "rohm,dh2228fv";
++              spi-max-frequency = <10000000>;
++              reg = <0>;
++      };
++};
++
++&uart0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart0_pins>;
++      status = "okay";
++};
++
+ &uart3 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart3_pins>;
+       status = "okay";
+ };
++
++&usb3 {
++      dr_mode = "host";
++      status = "okay";
++};
+--- a/arch/riscv/boot/dts/starfive/jh7100-starfive-visionfive-v1.dts
++++ b/arch/riscv/boot/dts/starfive/jh7100-starfive-visionfive-v1.dts
+@@ -18,3 +18,22 @@
+               priority = <224>;
+       };
+ };
++
++&gpio {
++      /* don't reset gpio mux for serial console and reset gpio */
++      starfive,keep-gpiomux = <13 14 63>;
++};
++
++&i2c0 {
++      eeprom@50 {
++              compatible = "atmel,24c04";
++              reg = <0x50>;
++              pagesize = <16>;
++      };
++};
++
++&mdio {
++      phy: ethernet-phy@0 {
++              reg = <0>;
++      };
++};
+--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
++++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
+@@ -6,7 +6,9 @@
+ /dts-v1/;
+ #include <dt-bindings/clock/starfive-jh7100.h>
++#include <dt-bindings/clock/starfive-jh7100-audio.h>
+ #include <dt-bindings/reset/starfive-jh7100.h>
++#include <dt-bindings/reset/starfive-jh7100-audio.h>
+ / {
+       compatible = "starfive,jh7100";
+@@ -135,6 +137,13 @@
+               clock-frequency = <0>;
+       };
++      /* gmac device configuration */
++      stmmac_axi_setup: stmmac-axi-config {
++              snps,wr_osr_lmt = <0xf>;
++              snps,rd_osr_lmt = <0xf>;
++              snps,blen = <256 128 64 32 0 0 0>;
++      };
++
+       soc {
+               compatible = "simple-bus";
+               interrupt-parent = <&plic>;
+@@ -143,6 +152,24 @@
+               dma-noncoherent;
+               ranges;
++              dtim: dtim@1000000 {
++                      compatible = "starfive,dtim0";
++                      reg = <0x0 0x1000000 0x0 0x2000>;
++                      reg-names = "mem";
++              };
++
++              itim0: itim@1808000 {
++                      compatible = "starfive,itim0";
++                      reg = <0x0 0x1808000 0x0 0x8000>;
++                      reg-names = "mem";
++              };
++
++              itim1: itim@1820000 {
++                      compatible = "starfive,itim0";
++                      reg = <0x0 0x1820000 0x0 0x8000>;
++                      reg-names = "mem";
++              };
++
+               clint: clint@2000000 {
+                       compatible = "starfive,jh7100-clint", "sifive,clint0";
+                       reg = <0x0 0x2000000 0x0 0x10000>;
+@@ -172,6 +199,151 @@
+                       riscv,ndev = <133>;
+               };
++              gmac: ethernet@10020000 {
++                      compatible = "starfive,jh7100-dwmac", "snps,dwmac";
++                      reg = <0x0 0x10020000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_GMAC_ROOT_DIV>,
++                               <&clkgen JH7100_CLK_GMAC_AHB>,
++                               <&clkgen JH7100_CLK_GMAC_PTP_REF>,
++                               <&clkgen JH7100_CLK_GMAC_TX_INV>,
++                               <&clkgen JH7100_CLK_GMAC_GTX>;
++                      clock-names = "stmmaceth", "pclk", "ptp_ref", "tx", "gtx";
++                      resets = <&rstgen JH7100_RSTN_GMAC_AHB>;
++                      reset-names = "ahb";
++                      interrupts = <6>, <7>;
++                      interrupt-names = "macirq", "eth_wake_irq";
++                      max-frame-size = <9000>;
++                      snps,multicast-filter-bins = <0>;
++                      snps,perfect-filter-entries = <128>;
++                      starfive,syscon = <&sysmain 0x70 0>;
++                      rx-fifo-depth = <32768>;
++                      tx-fifo-depth = <16384>;
++                      snps,axi-config = <&stmmac_axi_setup>;
++                      snps,fixed-burst;
++                      /*snps,force_sf_dma_mode;*/
++                      snps,force_thresh_dma_mode;
++                      snps,no-pbl-x8;
++                      status = "disabled";
++              };
++
++              dma2p: dma-controller@100b0000 {
++                      compatible = "starfive,jh7100-axi-dma";
++                      reg = <0x0 0x100b0000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SGDMA2P_AXI>,
++                               <&clkgen JH7100_CLK_SGDMA2P_AHB>;
++                      clock-names = "core-clk", "cfgr-clk";
++                      resets = <&rstgen JH7100_RSTN_SGDMA2P_AXI>,
++                               <&rstgen JH7100_RSTN_SGDMA2P_AHB>;
++                      reset-names = "axi", "ahb";
++                      interrupts = <2>;
++                      #dma-cells = <1>;
++                      dma-channels = <4>;
++                      snps,dma-masters = <1>;
++                      snps,data-width = <4>;
++                      snps,block-size = <4096 4096 4096 4096>;
++                      snps,priority = <0 1 2 3>;
++                      snps,axi-max-burst-len = <128>;
++                      dma-coherent;
++              };
++
++              crypto: crypto@100d0000 {
++                      compatible = "starfive,vic-sec";
++                      reg = <0x0 0x100d0000 0x0 0x20000>,
++                            <0x0 0x11800234 0x0 0xc>;
++                      reg-names = "secmem", "secclk";
++                      clocks = <&clkgen JH7100_CLK_SEC_AHB>;
++                      interrupts = <31>;
++              };
++
++              i2sadc0: i2sadc0@10400000 {
++                      compatible = "snps,designware-i2sadc0";
++                      reg = <0x0 0x10400000 0x0 0x1000>;
++                      clocks = <&clkgen JH7100_CLK_APB1_BUS>;
++                      clock-names = "i2sclk";
++                      interrupt-parent = <&plic>;
++                      #sound-dai-cells = <0>;
++                      dmas = <&dma2p 28>;
++                      dma-names = "rx";
++              };
++
++              i2svad: i2svad@10420000 {
++                      compatible = "starfive,sf-i2svad";
++                      reg = <0x0 0x10420000 0x0 0x1000> ;
++                      clocks = <&audclk JH7100_AUDCLK_I2SVAD_APB>;
++                      clock-names = "i2svad_apb";
++                      resets = <&audrst JH7100_AUDRSTN_I2SVAD_APB>,
++                               <&audrst JH7100_AUDRSTN_I2SVAD_SRST>;
++                      reset-names = "apb_i2svad", "i2svad_srst";
++                      interrupts = <60>, <61>;
++                      interrupt-names = "spintr", "slintr";
++                      #sound-dai-cells = <0>;
++              };
++
++              pwmdac: pwmdac@10440000 {
++                      compatible = "starfive,pwmdac";
++                      reg = <0x0 0x10440000 0x0 0x1000>;
++                      clocks = <&clkgen JH7100_CLK_AUDIO_ROOT>,
++                               <&clkgen JH7100_CLK_AUDIO_SRC>,
++                               <&clkgen JH7100_CLK_AUDIO_12288>,
++                               <&audclk JH7100_AUDCLK_DMA1P_AHB>,
++                               <&audclk JH7100_AUDCLK_PWMDAC_APB>,
++                               <&audclk JH7100_AUDCLK_DAC_MCLK>;
++                      clock-names = "audio_root",
++                                    "audio_src",
++                                    "audio_12288",
++                                    "dma1p_ahb",
++                                    "pwmdac_apb",
++                                    "dac_mclk";
++                      resets = <&audrst JH7100_AUDRSTN_APB_BUS>,
++                               <&audrst JH7100_AUDRSTN_DMA1P_AHB>,
++                               <&audrst JH7100_AUDRSTN_PWMDAC_APB>;
++                      reset-names = "apb_bus", "dma1p_ahb", "apb_pwmdac";
++                      dmas = <&dma2p 23>;
++                      dma-names = "tx";
++                      #sound-dai-cells = <0>;
++              };
++
++              i2sdac0: i2sdac0@10450000 {
++                      compatible = "snps,designware-i2sdac0";
++                      reg = <0x0 0x10450000 0x0 0x1000>;
++                      clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>,
++                               <&audclk JH7100_AUDCLK_I2SDAC_BCLK>,
++                               <&audclk JH7100_AUDCLK_I2SDAC_LRCLK>,
++                               <&audclk JH7100_AUDCLK_I2SDAC_APB>;
++                      clock-names = "dac_mclk", "i2sdac0_bclk", "i2sdac0_lrclk", "i2sdac_apb";
++                      resets = <&audrst JH7100_AUDRSTN_I2SDAC_APB>,
++                               <&audrst JH7100_AUDRSTN_I2SDAC_SRST>;
++                      reset-names = "apb_i2sdac", "i2sdac_srst";
++                      #sound-dai-cells = <0>;
++                      dmas = <&dma2p 30>;
++                      dma-names = "tx";
++              };
++
++              i2sdac1: i2sdac1@10460000 {
++                      compatible = "snps,designware-i2sdac1";
++                      reg = <0x0 0x10460000 0x0 0x1000>;
++                      clocks = <&audclk JH7100_AUDCLK_DAC_MCLK>,
++                               <&audclk JH7100_AUDCLK_I2S1_BCLK>,
++                               <&audclk JH7100_AUDCLK_I2S1_LRCLK>,
++                               <&audclk JH7100_AUDCLK_I2S1_APB>;
++                      clock-names = "dac_mclk", "i2sdac1_bclk", "i2sdac1_lrclk", "i2s1_apb";
++                      resets = <&audrst JH7100_AUDRSTN_I2S1_APB>,
++                               <&audrst JH7100_AUDRSTN_I2S1_SRST>;
++                      #sound-dai-cells = <0>;
++                      dmas = <&dma2p 31>;
++                      dma-names = "tx";
++              };
++
++              i2sdac16k: i2sdac16k@10470000 {
++                      compatible = "snps,designware-i2sdac16k";
++                      reg = <0x0 0x10470000 0x0 0x1000>;
++                      clocks = <&clkgen JH7100_CLK_APB1_BUS>;
++                      clock-names = "i2sclk";
++                      #sound-dai-cells = <0>;
++                      dmas = <&dma2p 29>;
++                      dma-names = "tx";
++              };
++
+               audclk: clock-controller@10480000 {
+                       compatible = "starfive,jh7100-audclk";
+                       reg = <0x0 0x10480000 0x0 0x10000>;
+@@ -214,6 +386,82 @@
+                       status = "disabled";
+               };
++              spdif_transmitter: spdif-transmitter {
++                      compatible = "linux,spdif-dit";
++                      #sound-dai-cells = <0>;
++              };
++
++              spdif_receiver: spdif-receiver {
++                      compatible = "linux,spdif-dir";
++                      #sound-dai-cells = <0>;
++              };
++
++              pwmdac_codec: pwmdac-transmitter {
++                      compatible = "linux,pwmdac-dit";
++                      #sound-dai-cells = <0>;
++              };
++
++              dmic_codec: dmic {
++                      compatible = "dmic-codec";
++                      #sound-dai-cells = <0>;
++              };
++
++              sound: snd-card {
++                      compatible = "simple-audio-card";
++                      simple-audio-card,name = "Starfive-Multi-Sound-Card";
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++
++                      /* pwmdac */
++                      simple-audio-card,dai-link@0 {
++                              reg = <0>;
++                              status = "okay";
++                              format = "left_j";
++                              bitclock-master = <&sndcpu0>;
++                              frame-master = <&sndcpu0>;
++
++                              sndcpu0: cpu {
++                                      sound-dai = <&pwmdac>;
++                              };
++
++                              codec {
++                                      sound-dai = <&pwmdac_codec>;
++                              };
++                      };
++              };
++
++              usb3: usb@104c0000 {
++                      compatible = "cdns,usb3";
++                      reg = <0x0 0x104c0000 0x0 0x10000>,     // memory area for HOST registers
++                            <0x0 0x104d0000 0x0 0x10000>,     // memory area for DEVICE registers
++                            <0x0 0x104e0000 0x0 0x10000>;     // memory area for OTG/DRD registers
++                      reg-names = "otg", "xhci", "dev";
++                      interrupts = <44>, <52>, <43>;
++                      interrupt-names = "host", "peripheral", "otg";
++                      phy-names = "cdns3,usb3-phy", "cdns3,usb2-phy";
++                      maximum-speed = "super-speed";
++                      status = "disabled";
++              };
++
++              dma1p: dma-controller@10500000 {
++                      compatible = "starfive,jh7100-axi-dma";
++                      reg = <0x0 0x10500000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SGDMA1P_AXI>,
++                               <&clkgen JH7100_CLK_SGDMA1P_BUS>;
++                      clock-names = "core-clk", "cfgr-clk";
++                      resets = <&rstgen JH7100_RSTN_DMA1P_AXI>,
++                               <&rstgen JH7100_RSTN_SGDMA1P_AXI>;
++                      reset-names = "axi", "ahb";
++                      interrupts = <1>;
++                      #dma-cells = <1>;
++                      dma-channels = <16>;
++                      snps,dma-masters = <1>;
++                      snps,data-width = <3>;
++                      snps,block-size = <4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096 4096>;
++                      snps,priority = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15>;
++                      snps,axi-max-burst-len = <64>;
++              };
++
+               clkgen: clock-controller@11800000 {
+                       compatible = "starfive,jh7100-clkgen";
+                       reg = <0x0 0x11800000 0x0 0x10000>;
+@@ -222,12 +470,93 @@
+                       #clock-cells = <1>;
+               };
++              otp: otp@11810000 {
++                      compatible = "starfive,fu740-otp";
++                      reg = <0x0 0x11810000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_OTP_APB>;
++                      fuse-count = <0x200>;
++              };
++
+               rstgen: reset-controller@11840000 {
+                       compatible = "starfive,jh7100-reset";
+                       reg = <0x0 0x11840000 0x0 0x10000>;
+                       #reset-cells = <1>;
+               };
++              sysmain: syscon@11850000 {
++                      compatible = "starfive,jh7100-sysmain", "syscon";
++                      reg = <0x0 0x11850000 0x0 0x10000>;
++              };
++
++              qspi: spi@11860000 {
++                      compatible = "cdns,qspi-nor";
++                      reg = <0x0 0x11860000 0x0 0x10000>,
++                            <0x0 0x20000000 0x0 0x20000000>;
++                      clocks = <&clkgen JH7100_CLK_QSPI_AHB>;
++                      interrupts = <3>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      cdns,fifo-depth = <256>;
++                      cdns,fifo-width = <4>;
++                      cdns,trigger-address = <0x0>;
++                      spi-max-frequency = <250000000>;
++                      status = "disabled";
++              };
++
++              uart0: serial@11870000 {
++                      compatible = "starfive,jh7100-hsuart", "snps,dw-apb-uart";
++                      reg = <0x0 0x11870000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_UART0_CORE>,
++                               <&clkgen JH7100_CLK_UART0_APB>;
++                      clock-names = "baudclk", "apb_pclk";
++                      resets = <&rstgen JH7100_RSTN_UART0_APB>;
++                      interrupts = <92>;
++                      reg-io-width = <4>;
++                      reg-shift = <2>;
++                      status = "disabled";
++              };
++
++              uart1: serial@11880000 {
++                      compatible = "starfive,jh7100-hsuart", "snps,dw-apb-uart";
++                      reg = <0x0 0x11880000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_UART1_CORE>,
++                               <&clkgen JH7100_CLK_UART1_APB>;
++                      clock-names = "baudclk", "apb_pclk";
++                      resets = <&rstgen JH7100_RSTN_UART1_APB>;
++                      interrupts = <93>;
++                      reg-io-width = <4>;
++                      reg-shift = <2>;
++                      status = "disabled";
++              };
++
++              spi0: spi@11890000 {
++                      compatible = "snps,dw-apb-ssi";
++                      reg = <0x0 0x11890000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SPI0_CORE>,
++                               <&clkgen JH7100_CLK_SPI0_APB>;
++                      clock-names = "ssi_clk", "pclk";
++                      resets = <&rstgen JH7100_RSTN_SPI0_APB>;
++                      reset-names = "spi";
++                      interrupts = <94>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "disabled";
++              };
++
++              spi1: spi@118a0000 {
++                      compatible = "snps,dw-apb-ssi";
++                      reg = <0x0 0x118a0000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SPI1_CORE>,
++                               <&clkgen JH7100_CLK_SPI1_APB>;
++                      clock-names = "ssi_clk", "pclk";
++                      resets = <&rstgen JH7100_RSTN_SPI1_APB>;
++                      reset-names = "spi";
++                      interrupts = <95>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "disabled";
++              };
++
+               i2c0: i2c@118b0000 {
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0x118b0000 0x0 0x10000>;
+@@ -254,6 +583,41 @@
+                       status = "disabled";
+               };
++              trng: trng@118d0000 {
++                      compatible = "starfive,vic-rng";
++                      reg = <0x0 0x118d0000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_TRNG_APB>;
++                      interrupts = <98>;
++              };
++
++              vpu_enc: vpu_enc@118e0000 {
++                      compatible = "cm,cm521-vpu";
++                      reg = <0x0 0x118e0000 0x0 0x4000>;
++                      reg-names = "control";
++                      clocks = <&clkgen JH7100_CLK_VP6_CORE>;
++                      clock-names = "vcodec";
++                      interrupts = <26>;
++              };
++
++              vpu_dec: vpu_dec@118f0000 {
++                      compatible = "c&m,cm511-vpu";
++                      reg = <0 0x118f0000 0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_VP6_CORE>;
++                      clock-names = "vcodec";
++                      interrupts = <23>;
++                      //memory-region = <&vpu_reserved>;
++              };
++
++              jpu: coadj12@11900000 {
++                      compatible = "cm,codaj12-jpu-1";
++                      reg = <0x0 0x11900000 0x0 0x300>;
++                      reg-names = "control";
++                      clocks = <&clkgen JH7100_CLK_JPEG_APB>;
++                      clock-names = "jpege";
++                      interrupts = <24>;
++                      memory-region = <&jpu_reserved>;
++              };
++
+               gpio: pinctrl@11910000 {
+                       compatible = "starfive,jh7100-pinctrl";
+                       reg = <0x0 0x11910000 0x0 0x10000>,
+@@ -268,6 +632,86 @@
+                       #interrupt-cells = <2>;
+               };
++              nvdla@11940000 {
++                      compatible = "nvidia,nvdla_os_initial";
++                      interrupts = <22>;
++                      memory-region = <&nvdla_reserved>;
++                      reg = <0x0 0x11940000 0x0 0x40000>;
++                      status = "okay";
++              };
++
++              display: display-subsystem {
++                      compatible = "starfive,display-subsystem";
++                      dma-coherent;
++                      status = "disabled";
++              };
++
++              encoder: display-encoder {
++                      compatible = "starfive,display-encoder";
++                      status = "disabled";
++              };
++
++              crtc: crtc@12000000 {
++                      compatible = "starfive,jh7100-crtc";
++                      reg = <0x0 0x12000000 0x0 0x10000>,
++                            <0x0 0x12040000 0x0 0x10000>,
++                            <0x0 0x12080000 0x0 0x10000>,
++                            <0x0 0x120c0000 0x0 0x10000>,
++                            <0x0 0x12240000 0x0 0x10000>,
++                            <0x0 0x12250000 0x0 0x10000>,
++                            <0x0 0x12260000 0x0 0x10000>;
++                      reg-names = "lcdc", "vpp0", "vpp1", "vpp2", "clk", "rst", "sys";
++                      clocks = <&clkgen JH7100_CLK_DISP_AXI>, <&clkgen JH7100_CLK_VOUT_SRC>;
++                      clock-names = "disp_axi", "vout_src";
++                      resets = <&rstgen JH7100_RSTN_DISP_AXI>, <&rstgen JH7100_RSTN_VOUT_SRC>;
++                      reset-names = "disp_axi", "vout_src";
++                      interrupts = <101>, <103>;
++                      interrupt-names = "lcdc_irq", "vpp1_irq";
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "disabled";
++
++                      pp1 {
++                              pp-id = <1>;
++                              fifo-out;
++                              //sys-bus-out;
++                              src-format = <11>; //<COLOR_RGB565>;
++                              src-width = <1920>;
++                              src-height = <1080>;
++                              dst-format = <7>; //<COLOR_RGB888_ARGB>;
++                              dst-width = <1920>;
++                              dst-height = <1080>;
++                      };
++              };
++
++              spi2: spi@12410000 {
++                      compatible = "snps,dw-apb-ssi";
++                      reg = <0x0 0x12410000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SPI2_CORE>,
++                               <&clkgen JH7100_CLK_SPI2_APB>;
++                      clock-names = "ssi_clk", "pclk";
++                      resets = <&rstgen JH7100_RSTN_SPI2_APB>;
++                      reset-names = "spi";
++                      interrupts = <70>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "disabled";
++              };
++
++              spi3: spi@12420000 {
++                      compatible = "snps,dw-apb-ssi";
++                      reg = <0x0 0x12420000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_SPI3_CORE>,
++                               <&clkgen JH7100_CLK_SPI3_APB>;
++                      clock-names = "ssi_clk", "pclk";
++                      resets = <&rstgen JH7100_RSTN_SPI3_APB>;
++                      reset-names = "spi";
++                      interrupts = <71>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "disabled";
++              };
++
+               uart2: serial@12430000 {
+                       compatible = "starfive,jh7100-uart", "snps,dw-apb-uart";
+                       reg = <0x0 0x12430000 0x0 0x10000>;
+@@ -341,5 +785,64 @@
+                       reset-names = "sense", "bus";
+                       #thermal-sensor-cells = <0>;
+               };
++
++              ptc: pwm@12490000 {
++                      compatible = "starfive,pwm0";
++                      reg = <0x0 0x12490000 0x0 0x10000>;
++                      clocks = <&clkgen JH7100_CLK_PWM_APB>;
++                      resets = <&rstgen JH7100_RSTN_PWM_APB>;
++                      #pwm-cells = <3>;
++                      sifive,npwm = <8>;
++                      status = "disabled";
++              };
++
++              thermal-zones {
++                      cpu-thermal {
++                              polling-delay-passive = <250>;
++                              polling-delay = <15000>;
++
++                              thermal-sensors = <&sfctemp>;
++
++                              cooling-maps {
++                              };
++
++                              trips {
++                                      cpu_alert0: cpu_alert0 {
++                                              /* milliCelsius */
++                                              temperature = <75000>;
++                                              hysteresis = <2000>;
++                                              type = "passive";
++                                      };
++
++                                      cpu_crit: cpu_crit {
++                                              /* milliCelsius */
++                                              temperature = <90000>;
++                                              hysteresis = <2000>;
++                                              type = "critical";
++                                      };
++                              };
++                      };
++              };
++
++              xrp@f0000000 {
++                      compatible = "cdns,xrp";
++                      reg = <0x0  0xf0000000 0x0 0x01ffffff>,
++                            <0x10 0x72000000 0x0 0x00001000>,
++                            <0x10 0x72001000 0x0 0x00fff000>,
++                            <0x0  0x124b0000 0x0 0x00010000>;
++                      clocks = <&clkgen JH7100_CLK_VP6_CORE>;
++                      interrupts = <27>, <28>;
++                      firmware-name = "vp6_elf";
++                      dsp-irq = <19 20>;
++                      dsp-irq-src = <0x20 0x21>;
++                      intc-irq-mode = <1>;
++                      intc-irq = <0 1>;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      ranges = <0x40000000 0x0  0x40000000 0x01000000>,
++                               <0xb0000000 0x10 0x70000000 0x3000000>;
++                      dsp@0 {
++                      };
++              };
+       };
+ };